Appearance
Game lifecycle
A game document can host many runs. Load expensive resources once, reset run state on each start, and release document resources on dispose. The host controls when a run starts and pauses.
States
boot ──► loading ──► armed ──► playing ◄──► paused
▲ │
│ ▼
└──────── over any state ──► disposed- loading: fetch, decode, build the scene, compile shaders, upload textures. None of this may depend on
requestAnimationFrame: a document that is off screen gets almost no frames, and must still finish loading. - armed: the first playable frame is on screen and frozen (or idling in place). No simulation time passes, no sound. No title screen, no menu, no "tap to start" text of the game's own, no tutorial card: the shell's card covers that.
- playing: starts on the
startcommand. The run must respond to input from its first frame. - over: the run ended (fail or win). The game freezes or idles its end frame, sends the result and waits. It draws no game-over screen, no retry button, no score table: the shell does. A
startbegins a new run in place, no reload. The game's own score HUD fades out within 180 ms ofover(the shell's sheet shows the score; never two scores on screen). Music may go on inoverat half level or less; effects stop. The game is silent atmute(its sequencer keeps time, so thatunmutecomes back in step); music stops atpauseanddispose. The messageovergoes out before thestatechange to over and carries the final score. - paused: simulation time, timers and audio stop; the frame stays. All held input is released. Nothing of the game's keeps running while paused: no sequencer interval, no timer that fires sound or score later. In the feed the sound of a paused game stops twice over: by the game's own gate (the kit's, a 5 ms ramp) and by the shell, which shuts the game's channel (shared audio). The session's AudioContext itself keeps running, so
audio.ctx.currentTimemoves during a pause; a game keeps musical time with the kit's clock, not with that value. - disposed: the loop is stopped; the AudioContext closed; geometries, materials, textures and render targets disposed; the renderer disposed; references to large buffers dropped; the canvas set to 1 x 1; then
ack. The WebGL context is not lost on purpose (forceContextLoss/WEBGL_lose_context.loseContextare forbidden at dispose). After theackthe game runs no frame, plays no sound and posts nothing. The shell then points the frame atabout:blankand removes it. The kit closes its port after theack; the shell closes its end with the frame. "The AudioContext closed" holds for a context of the game's own (a game run alone). In the feed the context is the session's (shared audio) and the game closes nothing: every source is stopped, the game's output disconnected, its listeners on the context removed and its channel shut.
A game also pauses itself on visibilitychange to hidden, on pagehide and on window blur while playing, without being told, and reports it with state.
boot is an internal initial state before init; it is not a wire-level state message value. The SDK queues a start received before readiness. Game code does not need to implement that queue.
Put work in the right callback
Use load(ctx) for asynchronous preparation. Call await ctx.breathe(label) between expensive steps, report progress, and pass ctx.signal to fetches. A hidden iframe may receive almost no animation frames, so loading must not depend on requestAnimationFrame.
Use arm(ctx) for the initial waiting scene. It also runs before a new run; do not use random values here when they must depend on the run's seed. Initialize seeded values in start(ctx) with ctx.rng().
Use update(dt, ctx) for simulation. dt is in seconds and defaults to a fixed 1/60-second step. Input has already been sampled. Use render(alpha, ctx) to interpolate between simulation states. Render can run while armed or over, so it must not advance game rules.
Pauses, timers and input
The host's pause command, page visibility and window blur are separate pause reasons. Clearing one reason does not override another. The first delta on resume is clamped, preventing a hidden tab from producing a large simulation jump.
Use ctx.after(seconds, callback) for gameplay timers. They use simulation time, stop during a pause and are discarded on restart. Avoid setInterval or wall-clock timers for scoring, gameplay or sound.
ctx.time is simulation time in the current run. ctx.look is a separate visual clock that also runs in armed and over states and stops while paused. Use it for idle animation, not collision or score calculations.
Scores and ending a run
Call ctx.score(value) to update the SDK HUD and host score. Call ctx.over({ score, summary }) from play to finish; score defaults to the last score. Do not call over inside start; initialize the run there and evaluate its end condition in update.
Loading failures and late commands
A host may change quality while load is pending. Guard objects that do not exist yet in quality, and read ctx.gfx.tier when creating scene detail so the latest setting is applied. resize begins after load resolves. Other lifecycle callbacks are synchronous; see callback requirements.
A rejected load or a thrown lifecycle callback reports a fatal error and stops ordinary operation. The host must dispose the failed game. Frame errors are reported; three consecutive failing frames stop the loop. A WebGL context loss during loading is immediately fatal; after loading, a context that stays lost for one second is fatal. Test these failure paths when integrating your engine and follow the host's startup failure handling.
After external asynchronous work, check ctx.signal.aborted before creating more resources or using scene references. Pass the signal to fetches. ctx.breathe() prevents a pending loading step from continuing after disposal; it does not cancel unrelated requests you started without a signal.
Resource ownership
The SDK disposes scenes registered through ctx.gfx.warmUp, the SDK renderer and objects registered with ctx.gfx.own. Your dispose callback clears your own references and listeners. Use the shared audio interface for host-owned audio resources.