eigenwijskids/elm-playground-eigenwijs/15.0.0

NOTE: the latest version is 16.0.0

Eigenwijs.Playground

Playgrounds

picture : List (Shape PictureMsg) -> Program () Picture PictureMsg

Make a picture! Here is a picture of a triangle with an eyeball:

import Playground exposing (..)


main =
    picture
        [ triangle green 150
        , circle white 40
        , circle black 10
        ]
animation : (Time -> List (Shape Msg)) -> Program () Animation Msg

Create an animation!

Once you get comfortable using picture to layout shapes, you can try out an animation. Here is square that zigzags back and forth:

import Playground exposing (..)


main =
    animation view


view time =
    [ square blue 40
        |> moveX (zigzag -100 100 2 time)
    ]

We need to define a view to make our animation work.

Within view we can use functions like spin, wave, and zigzag to move and rotate our shapes.

(Computer -> memory -> List (Shape Msg))
-> (Computer -> memory -> memory)
-> memory
-> Program Value (Game memory) Msg

Create a game!

Once you get comfortable with animation, you can try making a game with the keyboard and mouse. Here is an example of a green square that just moves to the right:

import Playground exposing (..)


main =
    game view update 0


view computer offset =
    [ square green 40
        |> moveRight offset
    ]


update computer offset =
    offset + 0.03

This shows the three important parts of a game:

  1. memory - makes it possible to store information. So with our green square, we save the offset in memory. It starts out at 0.
  2. view - lets us say which shapes to put on screen. So here we move our square right by the offset saved in memory.
  3. update - lets us update the memory. We are incrementing the offset by a tiny amount on each frame.

The update function is called about 60 times per second, so our little changes to offset start to add up pretty quickly!

This game is not very fun though! Making a game also gives you access to the Computer, so you can use information about the Mouse and Keyboard to make it interactive! So here is a red square that moves based on the arrow keys:

import Playground exposing (..)


main =
    game view update ( 0, 0 )


view computer ( x, y ) =
    [ square red 40
        |> move x y
    ]


update computer ( x, y ) =
    ( x + toX computer.keyboard
    , y + toY computer.keyboard
    )

Notice that in the update we use information from the keyboard to update the x and y values. These building blocks let you make pretty fancy games!

Connection
-> (Computer -> WithMessages memory -> List (Shape Msg))
-> (Computer -> WithMessages memory -> WithMessages memory)
-> WithMessages memory
-> Program Value (Game (WithMessages memory)) Msg

Create a network game; a game that has a messages inbox field in Computer and a messages outbox as a field in its memory. The game takes a Connection parameter. Messages sent are tagged with your sender name, configured as name in the Connection.

Shapes

type Shape msg

Shapes help you make a picture, animation, or game.

Read on to see examples of circle, rectangle, words, image, and many more!

circle : Color -> Number -> Shape msg

Make circles:

dot =
    circle red 10


sun =
    circle yellow 300

You give a color and then the radius. So the higher the number, the larger the circle.

oval : Color -> Number -> Number -> Shape msg

Make ovals:

football =
    oval brown 200 100

You give the color, and then the width and height. So our football example is 200 pixels wide and 100 pixels tall.

square : Color -> Number -> Shape msg

Make squares. Here are two squares combined to look like an empty box:

import Playground exposing (..)


main =
    picture
        [ square purple 80
        , square white 60
        ]

The number you give is the dimension of each side. So that purple square would be 80 pixels by 80 pixels.

rectangle : Color -> Number -> Number -> Shape msg

Make rectangles. This example makes a red cross:

import Playground exposing (..)


main =
    picture
        [ rectangle red 20 60
        , rectangle red 60 20
        ]

You give the color, width, and then height. So the first shape is vertical part of the cross, the thinner and taller part.

triangle : Color -> Number -> Shape msg

Make triangles. So if you wanted to draw the Egyptian pyramids, you could do a simple version like this:

import Playground exposing (..)


main =
    picture
        [ triangle darkYellow 200
        ]

The number is the "radius", so the distance from the center to each point of the pyramid is 200. Pretty big!

pentagon : Color -> Number -> Shape msg

Make pentagons:

import Playground exposing (..)


main =
    picture
        [ pentagon darkGrey 100
        ]

You give the color and then the radius. So the distance from the center to each of the five points is 100 pixels.

hexagon : Color -> Number -> Shape msg

Make hexagons:

import Playground exposing (..)


main =
    picture
        [ hexagon lightYellow 50
        ]

The number is the radius, the distance from the center to each point.

If you made more hexagons, you could move them around to make a honeycomb pattern!

octagon : Color -> Number -> Shape msg

Make octogons:

import Playground exposing (..)


main =
    picture
        [ octagon red 100
        ]

You give the color and radius, so each point of this stop sign is 100 pixels from the center.

polygon : Color -> List ( Number, Number ) -> Shape msg

Make any shape you want! Here is a very thin triangle:

import Playground exposing (..)


main =
    picture
        [ polygon black [ ( -10, -20 ), ( 0, 100 ), ( 10, -20 ) ]
        ]

Note: If you rotate a polygon, it will always rotate around (0,0). So it is best to build your shapes around that point, and then use move or group so that rotation makes more sense.

( Number, Number )
-> ( Number, Number )
-> Color
-> Number
-> Shape msg

Draw a line between two points with specified thickness

line : Color -> Number -> List ( Number, Number ) -> Shape msg

Draw a line with specified thickness from a list of coordinates

svgPath : Color -> String -> Shape msg

Create a path from svg path string

svgShape : Svg msg -> Shape msg

Use an svg image as a shape

Sounds

sound : String -> Shape msg

Add sound to your picture, animation or game!

main =
    picture [ circle yellow 50, sound "https://example.com/sound.mp3" ]

Style

withOutline : Color -> Float -> Shape msg -> Shape msg

Add an outline to shapes with specified color and thickness.

withStroke : Color -> Float -> Shape msg -> Shape msg

Add an outline to shapes with specified color and thickness.

withFill : Color -> Shape msg -> Shape msg

Change the fill color of a shape. This also enables you to add a fill color to a line or svgPath shape.

Words

words : Color -> String -> Shape msg

Show some words!

import Playground exposing (..)


main =
    picture
        [ words black "Hello! How are you?"
        ]

You can use scale to make the words bigger or smaller.

withFont : String -> Shape msg -> Shape msg

Change the font used for the words within shapes:

import Playground exposing (..)


main =
    picture
        [ words black "Hello world"
            |> withFont "sans-serif"
        ]

Images

image : Number -> Number -> String -> Shape msg

Add some image from the internet:

import Playground exposing (..)


main =
    picture
        [ image 96 96 "https://elm-lang.org/images/turtle.gif"
        ]

You provide the width, height, and then the URL of the image you want to show.

Move Shapes

move : Number -> Number -> Shape msg -> Shape msg

Move a shape by some number of pixels:

import Playground exposing (..)


main =
    picture
        [ square red 100
            |> move -60 60
        , square yellow 100
            |> move 60 60
        , square green 100
            |> move 60 -60
        , square blue 100
            |> move -60 -60
        ]
moveUp : Number -> Shape msg -> Shape msg

Move a shape up by some number of pixels. So if you wanted to make a tree you could move the leaves up above the trunk:

import Playground exposing (..)


main =
    picture
        [ rectangle brown 40 200
        , circle green 100
            |> moveUp 180
        ]
moveDown : Number -> Shape msg -> Shape msg

Move a shape down by some number of pixels. So if you wanted to put the sky above the ground, you could move the sky up and the ground down:

import Playground exposing (..)


main =
    picture
        [ rectangle lightBlue 200 100
            |> moveUp 50
        , rectangle lightGreen 200 100
            |> moveDown 50
        ]
moveLeft : Number -> Shape msg -> Shape msg

Move shapes to the left.

import Playground exposing (..)


main =
    picture
        [ circle yellow 10
            |> moveLeft 80
            |> moveUp 30
        ]
moveRight : Number -> Shape msg -> Shape msg

Move shapes to the right.

import Playground exposing (..)


main =
    picture
        [ square purple 20
            |> moveRight 80
            |> moveDown 100
        ]
moveX : Number -> Shape msg -> Shape msg

Move the x coordinate of a shape by some amount. Here is a square that moves back and forth:

import Playground exposing (..)


main =
    animation view


view time =
    [ square purple 20
        |> moveX (wave 4 -200 200 time)
    ]

Using moveX feels a bit nicer here because the movement may be positive or negative.

moveY : Number -> Shape msg -> Shape msg

Move the y coordinate of a shape by some amount. Maybe you want to make grass along the bottom of the screen:

import Playground exposing (..)


main =
    game view update 0


update computer memory =
    memory


view computer count =
    [ rectangle green computer.screen.width 100
        |> moveY computer.screen.bottom
    ]

Using moveY feels a bit nicer when setting things relative to the bottom or top of the screen, since the values are negative sometimes.

moveAlong : List ( Number, Number ) -> Number -> Shape msg -> Shape msg

Move a shape along a path, specified by a list of coordinate-pairs, and a number between 0 and 1, where 0 corresponds with the start of the path and 1 indicates arriving at the end, the last coordinate in the list. So in the example below, the circle is moved halfway on the path:

circle red 50
    |> moveAlong [ ( 100, 0 ), ( 200, 200 ), ( 400, 200 ) ] 0.5
moveAlongLoop : List ( Number, Number ) -> Number -> Shape msg -> Shape msg

Move a shape along a path, just like with moveAlong, but by adding the first point also as the last one, we make sure that when amount reaches 1, we end up back at the beginning of the path. In this way you can keep looping from 0 to 1 and continue from 0 again.

Customize Shapes

scale : Number -> Shape msg -> Shape msg

Make a shape bigger or smaller. So if you wanted some words to be larger, you could say:

import Playground exposing (..)


main =
    picture
        [ words black "Hello, nice to see you!"
            |> scale 3
        ]
scaleX : Number -> Shape msg -> Shape msg

Make a shape's width bigger or smaller. So if you wanted a triangle to be wider, you could say:

import Playground exposing (..)


main =
    picture
        [ triangle black 20
            |> scaleX 3
        ]
scaleY : Number -> Shape msg -> Shape msg

Make a shape's height bigger or smaller. So if you wanted a triangle to be taller, you could say:

import Playground exposing (..)


main =
    picture
        [ triangle black 20
            |> scaleY 3
        ]
rotate : Number -> Shape msg -> Shape msg

Rotate shapes in degrees.

import Playground exposing (..)


main =
    picture
        [ words black "These words are tilted!"
            |> rotate 10
        ]

The degrees go counter-clockwise to match the direction of the unit circle.

fade : Number -> Shape msg -> Shape msg

Fade a shape. This lets you make shapes see-through or even completely invisible. Here is a shape that fades in and out:

import Playground exposing (..)


main =
    animation view


view time =
    [ square orange 30
    , square blue 200
        |> fade (zigzag 0 1 3 time)
    ]

The number has to be between 0 and 1, where 0 is totally transparent and 1 is completely solid.

Named Shapes

withName : String -> Shape Msg -> Shape Msg

Name a shape. This lets you set a name for a shape, so you can detect when it is clicked.

import Playground exposing (..)


main =
    game view update {}


view computer memory =
    [ square
        (if computer.mouse |> clickedName "little square" then
            red


         else
            green
        )
        30
        |> withName "little square"
    ]


update computer memory =
    memory
nameOf : Shape Msg -> Maybe String

Return the name of a shape if it was given one.

let
    namedSquare =
        square red 50 |> withName "The Square"
in
case nameOf namedSquare of
    Nothing ->
        "unnamed shape"


    Just name ->
        "a shape named " ++ name
clickedName : String -> Mouse -> Bool

Detect when a named shape is clicked. Use with withName.

Groups

group : List (Shape msg) -> Shape msg

Put shapes together so you can move and rotate them as a group. Maybe you want to put a bunch of stars in the sky:

import Playground exposing (..)


main =
    picture
        [ star
            |> move 100 100
            |> rotate 5
        , star
            |> move -120 40
            |> rotate 20
        , star
            |> move 80 -150
            |> rotate 32
        , star
            |> move -90 -30
            |> rotate -16
        ]


star =
    group
        [ triangle yellow 20
        , triangle yellow 20
            |> rotate 180
        ]

Time

type Time

The current time.

Helpful when making an animation with functions like spin, wave, and zigzag.

spin : Number -> Time -> Number

Create an angle that cycles from 0 to 360 degrees over time.

Here is an animation with a spinning triangle:

import Playground exposing (..)


main =
    animation view


view time =
    [ triangle orange 50
        |> rotate (spin 8 time)
    ]

It will do a full rotation once every eight seconds. Try changing the 8 to a 2 to make it do a full rotation every two seconds. It moves a lot faster!

wave : Number -> Number -> Number -> Time -> Number

Smoothly wave between two numbers.

Here is an animation with a circle that resizes:

import Playground exposing (..)


main =
    animation view


view time =
    [ circle lightBlue (wave 50 90 7 time)
    ]

The radius of the circle will cycles between 50 and 90 every seven seconds. It kind of looks like it is breathing.

zigzag : Number -> Number -> Number -> Time -> Number

Zig zag between two numbers.

Here is an animation with a rectangle that tips back and forth:

import Playground exposing (..)


main =
    animation view


view time =
    [ rectangle lightGreen 20 100
        |> rotate (zigzag -20 20 4 time)
    ]

It gets rotated by an angle. The angle cycles from -20 degrees to 20 degrees every four seconds.

BeginOfTime

Computer

type alias Computer =
{ mouse : Mouse
, keyboard : Keyboard
, screen : Screen
, time : Time
, audio : Audio
, inbox : List Message
, secondsBeforeSend : Number
}

When writing a game, you can look up all sorts of information about your computer:

  • Mouse - Where is the mouse right now?
  • Keyboard - Are the arrow keys down?
  • Screen - How wide is the screen?
  • Time - What time is it right now?

So you can use expressions like computer.mouse.x and computer.keyboard.enter in games where you want some mouse or keyboard interaction.

When you use gameWithAudio, you have access to the audio context from the passed computer parameter, for instance to be able to use the current audio time. This audio field is also available in the computer parameter passed to your update function. It is a Maybe AudioContext, to be able to still run your program even when the context is unavailable. (That can happen if initialisation of the context via parsed program flags failed.)

type alias Mouse =
{ x : Number
, y : Number
, down : Bool
, click : Bool
, clickedNames : List String
}

Figure out what is going on with the mouse.

You could draw a circle around the mouse with a program like this:

import Playground exposing (..)


main =
    game view update 0


view computer memory =
    [ circle yellow 40
        |> moveX computer.mouse.x
        |> moveY computer.mouse.y
    ]


update computer memory =
    memory

You could also use computer.mouse.down to change the color of the circle while the mouse button is down, or ask for computer.mouse.clickedNames to see which named shapes are clicked.

type alias Screen =
{ width : Number
, height : Number
, top : Number
, left : Number
, right : Number
, bottom : Number
}

Get the dimensions of the screen. If the screen is 800 by 600, you will see a value like this:

{ width = 800
, height = 600
, top = 300
, left = -400
, right = 400
, bottom = -300
}

This can be nice when used with moveY if you want to put something on the bottom of the screen, no matter the dimensions.

type alias Keyboard =
{ up : Bool
, down : Bool
, left : Bool
, right : Bool
, space : Bool
, enter : Bool
, shift : Bool
, backspace : Bool
, keys : Set String
}

Figure out what is going on with the keyboard.

If someone is pressing the UP and RIGHT arrows, you will see a value like this:

{ up = True
, down = False
, left = False
, right = True
, space = False
, enter = False
, shift = False
, backspace = False
, keys = Set.fromList [ "ArrowUp", "ArrowRight" ]
}

So if you want to move a character based on arrows, you could write an update like this:

update computer y =
    if computer.keyboard.up then
        y + 1


    else
        y

Check out toX and toY which make this even easier!

Note: The keys set will be filled with the name of all keys which are down right now. So you will see things like "a", "b", "c", "1", "2", "Space", and "Control" in there. Check out this list to see the names used for all the different special keys! From there, you can use Set.member to check for whichever key you want. E.g. Set.member "Control" computer.keyboard.keys.

toX : Keyboard -> Number

Turn the LEFT and RIGHT arrows into a number.

toX { left = False, right = False, ... } == 0
toX { left = True , right = False, ... } == -1
toX { left = False, right = True , ... } == 1
toX { left = True , right = True , ... } == 0

So to make a square move left and right based on the arrow keys, we could say:

import Playground exposing (..)


main =
    game view update 0


view computer x =
    [ square green 40
        |> moveX x
    ]


update computer x =
    x + toX computer.keyboard
toY : Keyboard -> Number

Turn the UP and DOWN arrows into a number.

toY { up = False, down = False, ... } == 0
toY { up = True , down = False, ... } == 1
toY { up = False, down = True , ... } == -1
toY { up = True , down = True , ... } == 0

This can be used to move characters around in games just like toX:

import Playground exposing (..)


main =
    game view update ( 0, 0 )


view computer ( x, y ) =
    [ square blue 40
        |> move x y
    ]


update computer ( x, y ) =
    ( x + toX computer.keyboard
    , y + toY computer.keyboard
    )
toXY : Keyboard -> ( Number, Number )

If you just use toX and toY, you will move diagonal too fast. You will go right at 1 pixel per update, but you will go up/right at 1.41421 pixels per update.

So toXY turns the arrow keys into an (x,y) pair such that the distance is normalized:

toXY { up = True , down = False, left = False, right = False, ... } == (1, 0)
toXY { up = True , down = False, left = False, right = True , ... } == (0.707, 0.707)
toXY { up = False, down = False, left = False, right = True , ... } == (0, 1)

Now when you go up/right, you are still going 1 pixel per update.

import Playground exposing (..)


main =
    game view update ( 0, 0 )


view computer ( x, y ) =
    [ square green 40
        |> move x y
    ]


update computer ( x, y ) =
    let
        ( dx, dy ) =
            toXY computer.keyboard
    in
    ( x + dx, y + dy )
type alias Audio =
{ time : Float
, available : Bool
}

Find out things about the audio system, such as the time that the audio has been running in seconds, and whether the audio system is ready to use (available or not).

Colors

type Color

Represents a color.

The colors below, like red and green, come from the Tango palette. It provides a bunch of aesthetically reasonable colors. Each color comes with a light and dark version, so you always get a set like lightYellow, yellow, and darkYellow.

rgb : Number -> Number -> Number -> Color

RGB stands for Red-Green-Blue. With these three parts, you can create any color you want. For example:

brightBlue =
    rgb 18 147 216


brightGreen =
    rgb 119 244 8


brightPurple =
    rgb 94 28 221

Each number needs to be between 0 and 255.

It can be hard to figure out what numbers to pick, so try using a color picker like paletton to find colors that look nice together. Once you find nice colors, click on the color previews to get their RGB values.

Light Colors

Dark Colors

Shades of Grey

Alternate Spellings of Gray

Numbers

type alias Number =
Float

A number like 1 or 3.14 or -120.

Audio

AudioPort Msg
-> (Computer -> memory -> List Node)
-> (Computer -> memory -> List (Shape Msg))
-> (Computer -> memory -> memory)
-> memory
-> Program Value (Game memory) Msg

Create a game with web audio elements such as oscillators.

This is still a work in progress, more documentation and tuning of the api is to come.

type alias AudioPort msg =
Value -> Cmd msg

The AudioPort connects our Elm game to a piece of javascript in the html file the game is embedded in, so it can access the WebAudio API. Use in your game program as follows:

port module Main exposing (main)


import Eigenwijs.Playground


port toWebAudio : Eigenwijs.Playground.AudioPort msg

Playground Picture embeds

pictureInit : () -> ( Picture, Cmd PictureMsg )

Picture init function

pictureView : Picture -> List (Shape PictureMsg) -> Html PictureMsg

Picture view function

pictureUpdate : PictureMsg -> Picture -> ( Picture, Cmd PictureMsg )

Picture update function

pictureSubscriptions : Picture -> Sub PictureMsg

Picture subscriptions

type alias Picture =
Screen

Picture model

type alias PictureMsg =
( Int, Int )

Picture message type

Playground Animation embeds

animationInit : () -> ( Animation, Cmd Msg )

Animation init

animationView : Animation -> (Time -> List (Shape Msg)) -> Html Msg

Animation view

Animation update function

Animation subscriptions

The type for animations.

type alias AnimationMsg =
Msg

Animation message alias

Playground Game embeds

gameInit : memory -> Value -> ( Game memory, Cmd Msg )

Game init function

gameView : (Computer -> memory -> List (Shape Msg)) -> Game memory -> Html Msg

Game view function

gameUpdate : (Computer -> memory -> memory) -> Msg -> Game memory -> Game memory

Game update function

gameSubscriptions : Game memory -> Sub Msg

Game subscriptions

type Game memory

Game model containing the visibility status, custom data (memory) and the Computer state record.

type alias GameMsg =
Msg

Game message alias

Calculations

center : Shape msg -> ( Number, Number )

Extracts the center from any shape.

extent : Shape msg -> Number

Calculates (the currently very crude approximation of) the extent (size) of any (group of) shape(s).

distanceTo : Shape msg -> Shape msg -> Number

Calculates the distance between two shapes, taking their sizes into account:

shape1 =
    circle red 50


shape2 =
    circle red 20
        |> move 100 40


distanceToCollision =
    shape2
        |> distanceTo shape1
collidesWith : Shape msg -> Shape msg -> Bool

Indicates whether two (groups of) shapes collide.

containsPoint : ( Number, Number ) -> Shape msg -> Bool

Returns true if the point (x, y) lies within the shape.

toPolygon : Color -> Shape msg -> Shape msg

Convert any shape to a polygon representation of it (a circle would get translated into an approximation of a circle using line segments, currently an octagon). This can be used to create a polygon from a group of arranged basic shapes, as an alternative way definition; it is sometimes easier than enumerating all the points one by one as coordinate pairs.

List ( Number, Number )
-> List ( Number, Number )
-> List ( Number, Number )

Calculate the Minkovski "difference" as used for computing intersection of convex shapes: see https://en.wikipedia.org/wiki/Minkowski_addition It is the Minkovski sum, where for each point in list a a list of new points is generated consisting where the a point's coordinates are subtracted from each point in b. The resulting list of points form a shape that will only contain the origin if the two convex shapes intersect.

verticesOf : Shape msg -> List ( Number, Number )

Returns a list of vertices (points) corresponding to the circumference of the shape. If the shape is circular, this returns an approximating polygon.

pointsOf : Shape msg -> List ( Number, Number )

This function is the same as verticesOf ("vertices" is a more formal way of saying "points" or "coordinates").

positionOf : Shape msg -> ( Number, Number )

Returns the position of a shape, as a pair (x, y).

scaleOf : Shape msg -> ( Number, Number )

Returns the scaling set on a shape, as a pair (horizontalScaling, verticalScaling).

rotationOf : Shape msg -> Number

Returns the angle of rotation of a shape.

Generating lists of coordinates (paths)

pathFromCommands : List CursorTransform -> List ( Number, Number )

Make a list of coordinates starting from point (0, 0) adding new points from a recipe; a list of commands, such as:

main =
    picture [ polygon green trousers ]


trousers =
    pathFromCommands
        [ forward 55
        , turn -92
        , forward 100
        , turnTo 180
        , forward 20
        , turn -88
        , forward 80
        , turn 176
        , forward 80
        , turnTo 180
        , forward 20
        , turn -88
        ]
forward : Number -> Cursor -> Cursor

For use with pathFromCommands: move n steps forward.

turn : Number -> Cursor -> Cursor

For use with pathFromCommands: turn n degrees counter clockwise.

turnTo : Number -> Cursor -> Cursor

For use with pathFromCommands: change direction to the angle n in degrees, turning counter clockwise.