Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Appendix A: The Game Cheat Sheet

The whole game-facing surface, in the exact shapes used throughout this book and the example games. Keep this page open while coding.

Wiring

module Main exposing (main)

import Canvas exposing (canvas, rect, circle, triangle, text, group, rgb, rgba, onTap, onDrag, onRelease, onResize)
import Keyboard
import Time

init : (Model, Cmd Msg)
update : Msg -> Model -> (Model, Cmd Msg)
view : Model -> View Msg
subscriptions : Model -> Sub Msg

page : Page
page =
    Page.create
        { path = "/", title = "My Game"
        , init = init, update = update, view = view, subscriptions = subscriptions
        }

main : Cmd ()
main =
    App.frontend [ page ]

Run: mar dev my-game · Deploy web: mar deploy my-game · iOS project: mar build --target ios

Canvas

canvas Pixelated [ attrs ] shapes      -- Pixelated (hard pixels) | Crisp (smooth); bare names

Shapes (all coordinates Int, list order = depth, first = back):

rect     x y w h color
circle   cx cy r color
triangle x1 y1 x2 y2 x3 y3 color
text     x y size align color str      -- align: Canvas.Center | Canvas.Left | Canvas.Right
group    [ transforms ] shapes

Colors: rgb r g b · rgba r g b a (alpha 0..100)

Transforms (qualified constructors):

Canvas.Translate dx dy      Canvas.Rotate degrees      Canvas.Scale percent
Canvas.Alpha percent        Canvas.Blend mode
-- modes: Canvas.Add | Canvas.Multiply | Canvas.Screen | Canvas.Erase | Canvas.Normal

Canvas events (attributes; coordinates are canvas-local Int):

onResize Resized     -- Resized : Int -> Int -> Msg   (fires with the size, and on change)
onTap Tapped         -- Tapped : Int -> Int -> Msg
onDrag Dragged       -- pointer moved while down
onRelease Released   -- pointer lifted
onAltTap AltTapped   -- right-click / two-finger tap
onWheel Wheeled      -- scroll deltas dx dy
onHover Hovered      -- desktop garnish only

The tick

Time.every (Time.millis 16) (\_ -> Tick)     -- ≤ 20 ms rides the display clock
Time.every (Time.seconds 1) (\_ -> Second)   -- slower = ordinary timer

One subscription tick = one unit of game time. The runtime catch-up (max 4 ticks per rendered frame) keeps speed constant on slow displays; hidden tabs pause.

Keyboard and Gamepad

Keyboard.onKeyDown KeyDown       -- KeyDown : Keyboard.Key -> Msg
Keyboard.onKeyUp KeyUp
-- keys: Keyboard.ArrowLeft/Right/Up/Down, Keyboard.KeyA..Z, Keyboard.Space,
--       Keyboard.Enter, Keyboard.Escape, Keyboard.ShiftLeft, Keyboard.Digit1..

Gamepad.onButtonDown PadDown     -- PadDown : Gamepad.Button -> Msg
Gamepad.onButtonUp PadUp
Gamepad.onStick PadStick         -- PadStick : Int -> Int -> Msg, each −100..100, deadzoned
Gamepad.onRightStick PadRStick
-- buttons: Gamepad.A/B/X/Y, Gamepad.Up/Down/Left/Right, Gamepad.L1/R1, Gamepad.Start

Device

Device.watch DeviceChanged       -- fires current state immediately, then on change
-- record: pointer (Coarse | Fine, bare), anyFine, supportsHover,
--         width, height, prefersDark, prefersReducedMotion
Device.touchOnly d               -- show virtual pads?
Device.canHover d                -- hover garnish allowed?

Sound

Sound.tone wave hz ms            -- wave: Sound.Square | Sound.Triangle | Sound.Sawtooth | Sound.Noise
Sound.volume v s                 -- 0..100 (default 60)
Sound.duty pct s                 -- Square width: 12 | 25 | 50 classic
Sound.sweep endHz s              -- glide pitch to endHz over the duration
Sound.hold ms s                  -- flat for ms, then glide (needs sweep)
Sound.vibrato depth rate s       -- cents, Hz
Sound.arp [ hz ] s               -- fast cycle: chord on one voice
Sound.sequence [ s, Sound.rest ms, s2 ]
Sound.chord [ s1, s2 ]
Sound.a 4                        -- 440. Notes: c cs d ds e f fs g gs a b, per octave

Playing:

Sound.play s                     -- Cmd: one-shot (fire from update)
Sound.setMuted True              -- Cmd
Sound.loop s                     -- SUBSCRIPTION: same value keeps looping, new value swaps
Sound.once s                     -- subscription: plays once while subscribed
Sound.ambient s                  -- subscription: held bed; new volume swells, new pitch retunes

Loop invariant: in a looped chord, every voice’s durations must sum identically.

Random

Random.uniform first [ rest ] : Random.Generator a
Random.map2 f genA genB
Random.generate Got gen          -- Cmd; result arrives as a Msg

The math kit (write once per game)

imax a b = if a > b then a else b
imin a b = if a < b then a else b
iabs n = if n < 0 then 0 - n else n
clamp lo hi n = imax lo (imin hi n)

modI a m =                                   -- positive remainder
    let r = a - (a // m) * m in
    if r < 0 then r + m else r

rnd seed n =                                 -- Park-Miller LCG → (nextSeed, 0..n-1)
    let s2 = modI (seed * 16807) 2147483647 in
    ( s2, modI (s2 // 64) (imax 1 n) )

Conventions from the book: fixed-point positions ×1000 (x // 1000 to draw), squared-distance checks, direction tables instead of trig, // everywhere (/ is Decimal-only).