← The API

Phaser AE

API docs

v3.0.0-1187 exports in 24 modulesreleased October 14, 2026

ContentsPacks

Need to get started? Install the Phaser AE CLI →

The game

Packs

Pack (what a pack is), Kernel (what its attach receives), GameBase (the kernel's half of game), GameOptions

engine/kernel/src/pack.ts

Types

Composed

interface Composed<G extends GameBase, T extends object, D extends object = {}, P extends object = {}>

From @phaser-ae/kernel

What createGame returns: a builder that composes packs one .use() at a time. Each call returns the builder with the pack's API, topics and per-object data added to its types, so the composed game is fully typed by inference alone.

  • use<Api extends object, BT extends object, BD extends object, BP extends object>( pack: Pack<Api, BT, BD, BP>, ): Composed<Omit<G, 'spawn'> & Api & { spawn<S extends Record<string, unknown> = Record<string, never>>(spec: SpawnSpecOf<P & BP> & { data?: S }): Omit<Handle, 'data'> & D & BD & { data: S } }, T & BT, D & BD, P & BP> — Compose one pack. Throws if its id is already composed, a requires is missing, or its key is taken. Returns the builder with the pack's API, topics and data merged in.
  • readonly game: G — The composed game, fully typed with every .use()d key.
  • readonly signals: Signals<T> — The signals bus, typed with every composed pack's topics.

GameBase

interface GameBase

From @phaser-ae/kernel

The kernel's half of the composed game: what every game has, before any pack adds its key.

  • anim(h: Handle): { name: string; done: boolean } | null — What h is playing, or null if nothing is. done is true once a one-shot (hold) has reached its last frame and stopped there. There is an anim.complete signal too, and it is the right tool for reacting the moment a thing finishes. This is for ASKING — "has the death animation finished yet?" — which a signal cannot answer: miss the step it was delivered on and the answer is gone for good.
  • readonly clock: Clock — The clock: pause, time scale, frame advance.
  • readonly roster: Roster — Every live object; iterate, query, kill.
  • readonly assets: Assets — The loader and the atlas: load files, get what loaded, frames(key) for a picture's first frame.
  • readonly scenes: Scenes — Define scenes and move between them.
  • readonly camera: Camera — The camera record: position, zoom, bounds, shake.
  • readonly rng: Rng — The seeded random source. Use it, never Math.random, or the replay diverges.
  • readonly commands: Commands — Standing commands: screenshot, record, pause, mute, fullscreen.
  • readonly cheats: Cheats — Debug controls: jump to a level, give a power-up, infinite lives. add one, run one, list them; bake play's cheat step and the backquote panel run them.
  • readonly after: Scheduler['after'] — Run fn once after seconds of simulation time; cancelled when the current scene exits.
  • readonly every: Scheduler['every'] — Run fn every seconds of simulation time; cancelled when the current scene exits.
  • step(name: string, fn: (dt: number) => void, order?: { before?: string; after?: string }): Cancel — Game code's per-fixed-step logic; dt is the step in seconds. Named so it can be ordered against packs' steps (before: 'collision'). Scene-scoped: it holds while its scene is suspended and is removed when the scene exits. Cancelling removes this step alone, even when another step shares its name. Still give each step its own name: a before or after naming a shared name binds only the step of that name added last.
  • frame(name: string, fn: (alpha: number) => void): Cancel — Game code's per-FRAME work: once per rendered frame, after the fixed steps, with the interpolation alpha. Scene-scoped and cancelled with the scene, exactly like step, and skipped while its scene is suspended. IMMEDIATE ART BELONGS HERE, not in a step. A fixed step runs zero, one or many times between two frames, and an immediate batch is filled between one draw and the next — so a line pushed from a step is drawn once PER STEP, and a frame catching up on six steps draws six copies of it. Retained objects do not care: a step moves them and the frame draws where they ended up.
  • start(): Promise<void> — Boot and start ticking. The first call emits boot.domReady, creates the canvas, then emits boot.surfaceCreated and boot.deviceReady, awaiting every subscriber; then input and the page's listeners attach and the tick source starts. It does NOT enter a scene: call scenes.go(first) after it. A call while one is in flight returns the same promise; after stop() it resumes without booting again; after destroy() it does nothing.
  • advance(seconds: number, opts?: import('./inspect.js').AdvanceOptions): Promise<import('./inspect.js').Advanced> — Run seconds of game time now, as fast as the machine can, and resolve when it has. The steps are the same fixed steps real time runs, so the game plays exactly as it would; fps only says how often a frame is drawn along the way (default 10 a second). Files still loading hold game time still until they land (30 s of waiting at most), unless loads: 'run'. The clock is held while it runs and stays held after if it was held before (clock.hold()). Throws if the game is not running or another advance has not finished. For tools that drive a game (a test, an agent playing it, a recorder), not for game code.
  • readonly inspect: import('./inspect.js').InspectFn — The game at this moment, as plain data: the clock, the scenes, the input, the loads, every object with its picture, centre, size and state, and what each pack knows (the text a label shows, a button's state, the music playing). With a target, only what it names: an object's id or id prefix ('frog'), a tag, a layer, an asset key ('frog-idle': its frames and who shows it), a pack ('sound'), or a point in world units ({ x, y }: everything whose box holds it, front first). A copy: changing it changes nothing. It answers at any time, ready or not. inspect.state(() => run) registers the game's own state (the level, lives, score, power-ups) and every inspection reports it as state.
  • readonly input: import('./input.js').Input — The player's input, polled in a step: keyDown(code) while held, keyPressed(code) for one step, pointer, named actions (bind, action), gestures and gamepads. It reads nothing while the game is paused or a menu has it.
  • paint(key: string, w: number, h: number, draw: (ctx: import('./assets.js').PaintContext, frame: number) => void, opts?: { frames?: number; res?: number }): number — A picture by name, painted with the 2D canvas: game.paint('cake', 86, 46, ctx => { … }). Returns its first frame index. See Assets.paint.
  • pixels(key: string, rows: ReadonlyArray<string>, palette: Readonly<Record<string, string>>, scale?: number): number — Pixel art by name from rows of characters and a palette. Returns its first frame index. See Assets.pixels.
  • readonly ready: Promise<void> — Resolves once, the moment the game can be PLAYED: it has started, a frame has been drawn with its first scene current (a game that registers no scenes needs none), nothing holds it back (the loading screen holds it while it is up: Kernel.holdReady), and every file asked for so far has landed, an asset pack loading quietly in the background included. A file asked for later does not take it back. Before it, debug controls (cheats.run) queue and run at this moment, and a pause or resume from the host is remembered and applied then. window.PhaserGameReady() is called at the same moment.
  • readonly isReady: boolean — True from the moment ready resolves.
  • readonly running: boolean — Is the tick source running? True from start() until stop(). It exists so a pack that needs FRAMES to finish its work can say so instead of hanging. Anything that resolves on a frame hook — a loading bar, a transition, a countdown — deadlocks if it is awaited before the game starts: the promise waits for a frame, the frame waits for the module to finish, and the page stays black with nothing logged.
  • stop(): void — Stop the tick source, detach input, the debug panel and the page's listeners, and emit app.shutdown. The canvas, the packs and every object stay, and a later start() carries on from here; the time spent stopped is never simulated. Idempotent.
  • destroy(): void — Stop, then RELEASE: app.dispose for every pack to give back its GPU objects, the canvas out of the DOM, the roster emptied, window.game cleared. A stopped game still holds a device and a canvas, which is fine for a page that ends with it and a leak for a host that opens one game after another — an editor tab, a gallery. A destroyed game cannot be restarted. Idempotent.

GameOptions

interface GameOptions extends ScreenPolicy

From @phaser-ae/kernel

What createGame takes: the screen policy plus the clock, rng, input and loader settings. A bake writes it from the manifest.

  • backgroundPolicy?: 'continue' | 'pause' — What happens while the page is hidden. Default 'continue'; 'pause' holds the simulation, without touching a pause the game or the host set.
  • workers?: WorkerPoolOptions — Worker job entries (from the bakery) and the pool's limits.
  • step?: number — Fixed simulation step in seconds. Default 1/60.
  • seed?: number — Seed for the rng. Default 1. The bakery sets it; a test or replay harness may override it.
  • tick?: TickSource — What fires frames. Default: requestAnimationFrame when it exists, else a manual ticker the harness drives.
  • dev?: boolean — Dev mode: misuse throws. Default true, including standalone builds.
  • inputGrace?: number — Seconds of input the game ignores on every scene entry. Default 0.25. A player frantically tapping fire as they die must not instantly dismiss the game-over screen they have not read yet. 0 turns it off.
  • atlas?: number — The texture atlas is one square canvas, and this is its side in pixels. Default 2048, which holds a generous sprite game. Raise it — 4096 — when the art genuinely is large and there is a lot of it: a deck of 52 high-resolution card faces does not fit in 2048, and the failure is a throw at load rather than a silent missing texture. Every pixel of it is uploaded, so it is a real cost: raise it because the art needs it, not by default.
  • mount?: HTMLElement — The element the canvas is appended to. Default: the element with id game, else document.body. A page that embeds a game inside its own layout — a model viewer in a tool, a preview in a gallery — passes the box it owns, so two games on one page never fight over #game. Sizing under 'fill' and 'height' measures this element. Ignored headless.

Kernel

interface Kernel<T extends object = KernelTopics>

From @phaser-ae/kernel

What a pack sees when it attaches: every kernel service. The kernel never imports a pack, and a pack reaches another pack only through the composed game's typed keys, signals, hooks and the definitions registry.

  • readonly rendererRequirements: ReadonlyArray<{ id: string; backends: ReadonlyArray<'webgpu' | 'webgl2'> }> — Every composed pack that restricts the graphics API, with the APIs it accepts. Automatic backend selection reads it at boot.
  • readonly clock: Clock — The single time authority: fixed step, pause, time scale, alpha.
  • readonly hooks: Hooks — The lifecycle hooks: boot, frame, assets, scene, input, app.
  • readonly frame: FrameGraph — The render pass graph; a pack adds passes ordered by name.
  • readonly simulate: Simulate — The fixed-step simulation list; a pack adds steps ordered by name.
  • readonly roster: Roster — Every live object as columns; spawn, kill, iterate.
  • readonly signals: Signals<T> — Typed signals, delivered at the start of each fixed step. Typed with this pack's declared topics merged in.
  • readonly workers: WorkerPool — The bounded worker pool. The bakery supplies its entries; results arrive at frame boundaries.
  • readonly after: Scheduler['after'] — One-shot timer in simulation seconds, scoped to the current scene.
  • readonly every: Scheduler['every'] — Repeating timer in simulation seconds, scoped to the current scene.
  • readonly rng: Rng — The seeded random source; the same seed and the same steps replay the same game.
  • readonly input: Input — Raw input: keys, pointers, gamepads, gestures, layered consumers. No scene-entry grace on keyDown/keyPressed; game.input is the player's view.
  • readonly commands: Commands — Standing commands (screenshot, pause, mute…): a pack fulfils one with claim.
  • readonly cheats: Cheats — Debug controls: a pack that plays a kind of game adds its own (a level jump, a power-up), as a game does.
  • readonly scenes: Scenes — Scene definitions and transitions.
  • readonly mount: HTMLElement | null — The element the canvas goes in (GameOptions.mount, else #game, else document.body), available before start; null headless. A loading screen can draw into it before any GPU surface exists.
  • readonly assets: Assets — The atlas and the loader.
  • readonly camera: Camera — The camera record a world transforms through and a rig drives.
  • readonly surface: Surface — The canvas and its sizes, for backend packs only. Throws until a surface exists (boot creates it). Game code cannot reach it.
  • registerWorld(world: World): number — A world pack registers itself here at attach; the kernel walks worlds in composed order. Returns the world's first global layer index.
  • readonly registry: Registry — The definitions registry: a data pack registers a definition under a KIND ('post', 'fx', 'backdrop', 'vfx', 'deform', 'drawer', 'inspect' …) and the runtime pack for that kind reads them. Names are unique per kind: a duplicate throws at compose, and an unknown name at use is the runtime pack's error to raise.
  • has(pack: PackId): boolean — Is the named pack composed? For optional cooperation (feel asks for audio), never for hard dependencies.
  • readonly running: boolean — Is the tick source running? True from game.start() until stop(). A pack that needs FRAMES to finish its work should check this and say so instead of hanging. Anything that resolves on a frame hook — a loading bar, a transition, a countdown — deadlocks when it is awaited before the game starts: the promise waits for a frame, the frame waits for the module to finish, and the page stays black with nothing logged.
  • readonly screen: Readonly<ScreenPolicy> — The screen policy the game was created with. Logical size for worlds; game code sees it only through a world.
  • own(dispose: () => void): Cancel — Scope something to the CURRENT scene: the disposer runs when that scene exits. Every pack that creates state on request — a rule, an effector, a joint, a setting change — registers its undo here, so leaving a scene clears everything it made without the game remembering to. Outside any scene the disposer is global and runs on destroy. A scene's disposers run newest first, as an undo stack does, so const prev = x; x = v; k.own(() => { x = prev; }) restores the value from before the scene however many times the scene changed it. Returns a cancel (already disposed → no-op).
  • spawn(spec: SpawnSpec): Handle — The FULL spawn — columns, every pack's spawn data (body, brain), anims — the same call as game.spawn. A pack spawning on the game's behalf (a tilemap's bodies, a particle) uses this; roster.spawn alone skips the spawn data of every other pack.
  • onSpawnData<T>(key: string, fn: (h: Handle, data: T, spec: SpawnSpec) => void): void — Handle a spawn key of this pack's own (a brain, a path, a mesh): fn runs inside spawn whenever the spec carries the key, with the whole spec third (a mesh's size beside its mesh). One handler per key; a second throws.
  • onPlay(fn: (h: Handle, name: string) => boolean): void — Play by name for objects a pack animates itself (a model's clips): when h.play(name) finds no anim of that name declared at spawn, each handler is asked in turn, and the first to return true has played it.
  • readonly dev: boolean — Dev mode (default true): misuse throws instead of being ignored — a non-finite position, an unknown layer, a write to a killed handle, an anim that leaves its strip, a failing scene disposer.
  • holdReady(why: string): () => void — Keep the game from being READY (game.ready) while something a player would see as loading is still up: the loading screen holds it from the moment it shows until it has faded. Returns the release; releasing twice is harmless. A hold taken after the game is ready changes nothing.
  • readonly isReady: boolean — True from the moment the game is ready (game.ready), for good.

Pack

interface Pack<Api extends object, T extends object = {}, D extends object = {}, P extends object = SpawnKeysOf<D>>

From @phaser-ae/kernel

A pack: one id, one attach, one API under ONE named key. Api is what .use() adds to the game's type; T is the signal topics the pack declares, merged into the kernel's signal typing; D is the per-object data it puts on every handle; P is the spawn keys it reads.

  • readonly spawnKeys?: P — Type only, never set: the keys this pack reads from a spawn spec. By default each data key, partially (fsm: { state }). Declare it when the spawn shape differs from the data — body: BodySpec where the columns hold numbers — or when the pack reads a key it keeps no data under (fx, brain).
  • readonly backends?: ReadonlyArray<'webgpu' | 'webgl2'> — Supported graphics APIs when this pack requires backend-specific rendering. Omitted means either.
  • readonly id: PackId — Unique among composed packs; composing the same id twice throws.
  • readonly requires?: ReadonlyArray<string> — Other packs that must be composed first, by id or by world kind (world:2d).
  • readonly topics?: Readonly<Record<string, TopicDecl>> — Signal topics this pack declares, each namespaced by the pack (collision.hit). A topic declared twice throws at compose.
  • readonly columns?: { key: string; columns: Columns; defaults?: Record<string, number>; methods?: Record<string, (h: Handle, ...args: never[]) => unknown> } — Per-object columns, stored beside the kernel's own and read on a handle under key (h.physics.vx). defaults fills a column a spawn does not set (else 0); methods become functions on that sub-handle, called with the handle first. roster.columns(key) gives a step the raw arrays.
  • attach(k: Kernel<KernelTopics & T>): Api — Called once at compose, in composed order. Returns the API the game exposes: an object whose keys (usually one) are added to game. A key already taken throws.
  • detach?(): void — Called once at game.destroy(), in reverse composed order, to release what attach took. stop() does not call it: a stopped game keeps its packs.
  • readonly __topics?: T — Type carrier only, never set at runtime: the topics type T so .use() can merge it into the signal typing.
  • readonly __data?: D — The per-object data this pack puts on every handle under its key ({ fsm: FsmData }). Typed here so spawn() returns it.

Registry

interface Registry

From @phaser-ae/kernel

Named definitions by kind: data packs add, runtime packs read.

  • add<T extends { name: string }>(kind: string, def: T): void — Register def under kind by its name. Throws if that kind already has the name.
  • get<T extends { name: string } = { name: string }>(kind: string, name: string): T | undefined — Look a definition up by kind and name; undefined when unknown — the caller decides how loud to be.
  • list<T extends { name: string } = { name: string }>(kind: string): ReadonlyArray<T> — Every definition of a kind, in registration order; empty for an unknown kind.

SpawnKeysOf

type SpawnKeysOf<D extends object> = { [K in keyof D]?: Partial<D[K]> }

From @phaser-ae/kernel

The spawn keys a pack reads by default: each of its data keys, partially.

SpawnSpecOf

type SpawnSpecOf<P extends object> = Omit<import('./roster.js').SpawnSpec, 'data'> & P & { anims?: Record<string, import('./game.impl.js').AnimDef>; play?: string }

From @phaser-ae/kernel

The spawn spec, with every composed pack's spawn keys typed (and optional). Keys no pack declares are still accepted, untyped.