← The API

Phaser AE

API docs

v3.0.0-2206 exports in 26 modulesreleased October 19, 2026

ContentsHow it works

Need to get started? Install the Phaser AE CLI →

How it works

The kernel's rules, its modules, one frame step by step, what scenes own, and how a pack plugs in: for pack authors and the curious.

The kernel is the small core every Phaser AE game runs on. It owns the frame loop and the services every game needs: the clock, the roster of objects, scenes, input, the loader and texture atlas, timers, the seeded random source, signals and hooks, standing commands, debug controls, inspection and a worker pool. It also defines the contracts that packs plug into: worlds, backends, render passes, simulation steps and the definitions registry. Everything else (drawing, physics, audio, UI, genres) is a pack, and the kernel never imports one.

This page is for reading or changing the kernel's source. To make a game, read the game's own docs/ folder: its 00-kernel.md is docs/kernel.md, the kernel as game code sees it.

The kernel is source-available under the PolyForm Shield License 1.0.0, with an additional permission to make and sell games: see LICENSE.md.

The rules

The kernel keeps seven rules, and its comments cite them as "Law 1" to "Law 7".

  1. Everything on stage is a roster object. The roster is the single ground truth of what exists; a thing not in it does not draw. A particle system or a tilemap is one roster object (an aggregate).
  2. No unattributed pixels. Game code cannot reach the canvas or the GPU. Packs draw, through the frame graph.
  3. All time flows from the clock. Simulation runs in fixed steps, in seconds. Nothing in a simulation reads performance.now(), Date.now() or setTimeout, so pause is one flag and slow motion is one number.
  4. All chance flows from the rng. Every random number comes from the seeded rng (or, without a game, a createRng(seed) stream), so a seed and the same input replay the same game. On any machine: a build's Math is deterministicMath (math.ts), whose transcendental functions (sin, atan2, pow, hypot …) are fdlibm's algorithms in exactly specified arithmetic, where the engines' own differ in the last bit.
  5. One conventions page. Units are set once, in docs/reference/CONVENTIONS.md (every game's docs/01-conventions.md): world units, y down in 2D, y up and right-handed in 3D, radians, seconds, positions at the centre, CSS colour strings at the boundary.
  6. Garbage fails loud. In dev mode (the default) a non-finite number, an unknown name or a write to a killed object throws; in production it is ignored.
  7. The kernel never imports a pack. Packs reach each other only through the composed game's keys, signals, hooks and the definitions registry.

Entry points

import for holds
@phaser-ae/kernel packs, the bakery createGame, every public type, the 3D maths and drag conventions, colour helpers, tick sources, WebGL2 helpers
@phaser-ae/kernel/verbs game code move, moveTo, teleport, rotate, scale, tint, show, hide, kill, tag, untag, play
@phaser-ae/kernel/internal world packs only the raw roster tables and the shared 2D and 3D gathers

Game code does not call createGame: a bake writes it into the game's engine.ts, composing the manifest's packs in order, and game code imports the composed game from there. From the kernel itself, the bakery's game gate lets game code import only the verbs, types and the documented maths helpers.

The modules

Each service is an interface in src/<name>.ts, which is the documented contract, and an implementation in src/<name>.impl.ts. The implementations' own members are documented where they are not part of the interface.

module provides
game.impl.ts createGame, the Game that owns every service, the frame loop, boot, stop and destroy, the host contract
pack.ts Pack (what a pack is), Kernel (what its attach receives), GameBase (the kernel's half of game), GameOptions
clock.ts the fixed-step clock, pause, time scale, hold and advance; the TickSource that fires frames
roster.ts Handle, SpawnSpec, the roster's queries, blend modes and vertex deformers
scene.ts the scene stack: go, push, pop, and suspension beneath a push
scheduler.ts after and every: timers on the game clock, owned by a scene
signals.ts step-batched signals, declared per topic
hooks.ts the lifecycle hooks: boot, frame, assets, scene, input, app, debug
frame.ts, order.ts the render pass graph, the simulation list, and the before/after ordering both use
input.ts keys, pointers, gamepads, actions, gestures, the consumer layers and the scene-entry grace
assets.ts the loader, asset packs (assets.json), the atlas, pictures in textures of their own, paint, pixels
camera.ts the camera record a world transforms through and a rig drives
screen.ts, safe-area.impl.ts the screen policy (fit), the surface backends draw to, safe-area insets
render.ts the world/backend contract: instance tables, aggregates, deformers, drawers
world2d.ts, world3d.ts the shared gathers that build instance tables, and the 2D and 3D camera transforms
commands.ts standing commands a host wires once: pause, mute, fullscreen, screenshot, recording
cheats.ts debug controls and their panel
inspect.ts game.inspect(), game.advance() and what they report
workers.ts the worker pool and its job protocol
registry.impl.ts the definitions registry (Kernel.registry): who may replace a name, and sameDefinition, the equality that makes an identical definition a no-op (cheats use it too)
rng.ts, color.ts, wrap.ts, types.ts the seeded rng (and createRng for code without a game), the one colour parser, toroidal wrapping, shared units
history.ts createHistory: undo, redo, seek and replay for deterministic rules, with refusals, checkpoints and validated saves
math.ts, math-shim.ts deterministicMath, a Math whose 22 implementation-approximated functions are ported from fdlibm and give the same bits on every engine; the module the bakery injects as every bundle's Math; installDeterministicMath() for a Node host
maths3d.ts, controls3d.ts pure 3D maths (matrices, rays, quaternions, directions) and the three drag conventions
curves.ts Catmull–Rom: a span in any dimension (uniform, centripetal or chordal), the scalar form for value curves, and an arc-length spline through points
webgl.ts what the WebGL2 backend shares with packs, and the program compiler every pack uses
verbs.ts the verbs

One frame

Every tick runs Game.runFrame, the same on every backend:

  1. Finished worker jobs settle, in submission order.
  2. The clock turns the wall time since the last tick into a whole number of fixed steps (none while paused or held).
  3. Input is polled; the world layer is suspended while the clock is paused. frame.start is emitted.
  4. For each fixed step: input edges are consumed; signals emitted in the previous step are delivered; every object's transform and the camera are copied to their previous values; frame.simulate is emitted; the simulation steps run in their resolved order; each world's afterStep runs; animations advance; due timers fire; the step is counted; and the roster flush emits the spawn and kill signals and the scene.spawn and scene.kill hooks for what the step made and removed.
  5. The camera is interpolated at the clock's alpha, and frame.render is emitted (game.frame callbacks run here).
  6. Each world writes depth and builds its instance tables, already interpolated; the render passes run in their resolved order; frame.end is emitted.

Order among steps, passes and hook subscribers comes from each one's before/after names, never from kernel source. A cycle throws the next time the order is resolved.

Hooks or signals. A hook is a call, made the moment something happens (scene.enter, app.resize). A signal is a record: what one step emits, the next step reads as an array. Simulation talks in signals, so a step's outcome never depends on who subscribed first; the lifecycle talks in hooks.

Ownership

Scenes own things. Every object, timer, game.step, game.frame and Kernel.own disposer belongs to the scene that was current when it was made. When that scene exits, all of it goes: objects are killed, timers cancelled, steps removed and disposers run newest first, and camera bounds go back to what they were. While a scene is suspended beneath a push, its objects still draw but leave every query, and its timers, steps, frame callbacks and animations hold. Anything made before the first scene lasts for the game.

A pack that changes state on request (a collision rule, gravity, ambient light) registers the undo with k.own, so leaving a scene restores it without the game remembering to.

How a pack plugs in

A pack is a Pack object: an id, attach(k), and optionally requires, backends, topics, columns and detach. createGame(...).use(pack) attaches packs in manifest order; each attach receives the Kernel and returns the API added to game under its key. In attach a pack may:

  • add fixed steps (k.simulate.add) and render passes (k.frame.addPass), ordered by name;
  • subscribe to hooks (k.hooks.on) and emit or read its declared signal topics;
  • declare per-object columns, read on a handle under the pack's key (h.physics.vx), and handle its own spawn keys (k.onSpawnData);
  • register a world (k.registerWorld) or, as a backend, draw to k.surface;
  • register definitions under a registry kind ('deform', 'drawer', 'inspect' …) for the pack that reads them. Two packs registering one name throws at compose; after compose the last definition wins and an identical one changes nothing (the stored object stays), so a pack's define can run in a scene's enter every time it does. A pack that caches work per definition keys the cache by the definition, not its name, so a replacement is picked up;
  • claim a standing command (k.commands.claim), add debug controls (k.cheats.add), hold the ready moment while a loading screen is up (k.holdReady), and run jobs on worker threads (k.workers.run).

The page around the game

  • window.game is the composed game from createGame on, so a host page can call game.commands.pause() or read game.isReady while the game loads.
  • window.PhaserGameReady(), if defined, is called once at the ready moment (game.ready), and window.PhaserGamePlaying() once when a scene named play first enters.
  • The backquote key opens the debug-controls panel; ?cheats in the address adds a button for it on a phone.
  • The canvas goes in GameOptions.mount, else the element with id game, else document.body, and is sized by the screen policy's fit.

Headless and dev mode

With no document the kernel runs headless: no canvas in a page and no DOM listeners, a manual tick source where there is no requestAnimationFrame, and every load resolving blank, so tests and replays run in Node exactly as the game would. A test harness can serve real files to the loader through globalThis.__aeHeadlessFetch. GameOptions.dev (default true, standalone builds included) makes misuse throw rather than pass silently.