Lua API
Everything a game can call. The sandbox contains this API, standard Lua
tables/strings/math, require (game-local), and nothing else — no io,
no os, no filesystem.
System
frame() -> n frames since boot
log(...) print to the sim/verify log (bots read this)
byo.complete{goal="…"} declare a goal from game.toml completed
_save() / _load(t) optional: expose state for snapshots (rarely
needed — globals are auto-snapshotted)
Graphics
cls(c)
px(x, y, c) pget(x, y) -> c
line(x0,y0,x1,y1,c)
rect(x,y,w,h,c,fill?) circ(x,y,r,c,fill?) oval(x,y,w,h,c,fill?)
tri(x0,y0,x1,y1,x2,y2,c,fill?)
print(text, x, y, c) built-in 6×8 font
spr(name, x, y, opts?) opts: flip_x, flip_y, frame (1-based),
scale (integer), rot (radians, nearest-neighbor)
camera(x, y) camera() draw offset / reset
clip(x,y,w,h) clip() scissor / reset
Colors are P.<name> — the palette table (see game format).
Index 255 is transparent in sprite data; there is no alpha. Sprites are
referenced by name and come from gen/sprites.lua programs or baked
assets/sprites/*.spr files.
Map layers
Tile scene rendering: push cells when they change, draw the scene in one call. Cells are sprite names; 16 px per cell.
map.define(layer, w, h)
map.fill(layer, x, y, csv) rows ';', cells ',', '' = empty
map.set(layer, x, y, name_or_nil)
map.mask(layer, x, y, w, digits) per-cell: '0' hidden '1' dim '2' full
map.draw(layers, px, py, opts?) layers bottom-up;
opts { mask = layer, dim = sprite }
The mask + dim-sprite pair is the fog-of-war primitive.
Input
Three devices. A game reads whichever it wants; replays record all of them.
btn(name, player?) -> held this frame (player 1 or 2)
btnp(name, player?) -> pressed this frame
mouse() -> x, y, buttons buttons: 1 left, 2 right, 4 middle
mousep(b?) -> pressed this frame b defaults to 1 (left)
text() -> string characters typed this frame
("\b" backspace, "\n" enter)
Button names: up down left right a b x y l r start select.
Browser keys — P1: WASD + Z/X/C/V (space/shift alias a/b); P2: arrows +
comma/period/slash. Mouse is canvas-relative in machine pixels. The text
stream carries all printable typing — build a typing game if you want.
Random & math
rng(stream) -> Rng Rng:int(a,b) :float() :pick(t)
:shuffle(t) :chance(p)
math.random(...) aliased to the "math.random" stream
math.sin/cos/atan deterministic console implementations
Modules
require "gen.foo" loads gen/foo.lua, game-local, cached
Not (yet) in the machine
Audio (sfx/music) and grid pathfinding intrinsics appear in
older design notes but are not implemented. If you need pathfinding,
write it in Lua in your game — several games do.
The machine's laws
- Compute budget:
_updateand_draweach get 4,000k VM instructions per frame (_initgets 25x). Over budget is a game error — an infinite loop dies at the frame it happens, everywhere, deterministically. - Snapshot-clean: every verified game must survive the storm test —
save, simulate garbage, restore, and the future must be bit-identical.
By default the console snapshots every global your game defines (auto
mode); keep state in globals and you get this free. State hidden in
file-local variables fails the storm — move it or implement
_save() -> table/_load(table)yourself. This law is what makes every game rollback-ready for netplay, scrubbing, and save states.