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).