Phaser AE

The API

A game engine built for AI agents and made for the web. Its kernel is small enough for an agent to hold in full: the loop, the clock, the objects on stage and the services every game needs. Everything else plugs in as an expansion pack.

  • 53 files
  • 9,491 lines
  • 26 modules
  • 358 packs plug in
Read the docs

Version 3.0.0-2 · releases

Why it exists

A game engine for AI agents, made for the web.

  1. Phaser 4 was built for people. We spent fourteen years crafting the Phaser API for developers who write every line of code by hand, and it grew to be the most popular open-source framework for making web games.
  2. Then agents started writing the code instead. Language models write Phaser 4 code well, but to do it they have to hold a large API in memory, and spend much of their time working out what is what, when all you want is a kart racer on screen.
  3. So we built an engine for them. Phaser AE's kernel is small enough for an agent to hold in full. Everything a game needs beyond it is an expansion pack, and a game takes only the packs it uses.

Phaser 4.1.0's API docs run to 708 pages. The Phaser AE kernel's run to 26, one a module: 206 exports in 53 files, with 358 expansion packs to add as a game needs them.

Phaser 4.1.0708 pages of API docs

Built for people, over fourteen years.

The Phaser AE kernel26 pages of API docs

206 exports in 53 files, and 358 packs to add as a game needs them.

What it can do

Everything a modern web game needs.

  • WebGPUWebGL2

    WebGPU, and WebGL2 where it isn't

    The engine draws with WebGPU, and with WebGL2 on devices without it yet. It picks for you, and never falls back to a backend a game can't use.

  • 2D, 3D and vector

    Three cores in one engine: 2D, 3D and vector. A 3D game with a 2D HUD uses one backend for both.

  • Hundreds of thousands of sprites

    A screen crowded with sprites stays fast, because they draw in batches from shared atlas pages.

  • Windows · Chrome
    Mac · Safari
    same seed, same input, same bits

    Multiplayer: deterministic to the bit

    Fixed steps, a seeded random source, and a Math of its own: sin, cos, atan2, pow and the rest are ported from fdlibm and give the same bits in any browser, on any machine. The same seed and the same input replay the same game anywhere, which is what multiplayer and replays need.

  • Atlases built as you load

    Sprites, glyphs and drawn pictures are packed onto atlas pages as they load. The atlas never runs out of room, because a new page opens whenever one fills.

  • PBR materials

    Physically based 3D materials with texture maps, cascaded sun shadows, and reflections of a sky that bakes its own environment map.

  • A vector renderer

    Shapes, fonts and effects drawn as vectors, in 2D and in 3D, crisp at any scale.

  • Aa×16, still sharp

    Text that stays sharp

    MSDF text, drawn natively on WebGPU and WebGL2, on screen and in 2D and 3D worlds: sharp at any size, shaping any script and mixing fonts, with outlines, shadows and gradients.

  • Play

    UI built in

    Panels, windows, buttons, toggles, sliders, progress bars, text inputs, lists and dropdowns, styled as data, with 125 icons.

The idea

Small on purpose.

Your gamewhat you or your agent writescenesrulesart and soundimport { game } from './engine.js'game.ui · game.physics · game.bars · …Expansion packs358 of them; a game names the ones it usescore-2dworld-sidephysics-2dcollisionuitypographybarsmatch3vfx3d-fireworld-3daudiorender-webgpurender-webgl2…attach(k): steps, passes, hooks, signals, worldsThe kernelthe loop, and the services all games useclockrosterscenesinputassetstimerssignalshooksframe graphrngcameracommandsworkershistoryk.surface: backend packs onlyThe browserwhat the backends draw toWebGPUWebGL2the canvasinput eventsaudio
  • The kernel runs the game. The loop, the clock, the objects on stage, scenes, input, loading, timers, random numbers, and the signals and hooks that connect them.
  • Packs do the rest. Drawing, physics, sound, menus, worlds and whole kinds of game are all packs. A game lists the packs it uses, and only those are in it.
  • The kernel never imports a pack. Packs plug into it and reach each other only through the game, its signals, its hooks and a registry. You can swap any pack, and adding one changes nothing else.
  • Your agent can read all of it. The kernel and the packs a game uses arrive in its folder as source code, each with a page of docs, never as a compiled binary.

What's inside

26 modules, 206 exports.

View the full API reference →

One frame

The same loop on any machine.

1Workersfinished jobs settle2Clockwall time → whole steps3Inputpolled; frame.start4Fixed stepsthe simulation, 0 to n5Camerainterpolated; frame.render6Drawworlds, then passesEach fixed stepeach one a fixed dt, in seconds1input edges2last step's signals3previous transforms4frame.simulate5each step, in name order6worlds' afterStep7animations8due timers9spawn and kill flushedagain

Time comes from the clock. The simulation runs in fixed steps, in seconds. Pausing is one flag and slow motion is one number. Nothing in a game reads the wall clock.

Random numbers come from the seed. They all come from the seeded rng, and even Math gives the same bits in any browser. The same seed and the same input replay the same game.

Order comes from names. Steps, passes and hooks say what they run before or after, by name. No pack depends on the order it was imported in.

How a pack plugs in

A pack is an id and an attach function.

The Pack and Kernel APIs →
A pack{ id: 'fall', attach(k) { k.simulate.add('fall', { before: 'collision' }, dt => …) return { fall: api } },}k, the kernelk.simulate.addk.frame.addPassk.hooks.onk.signalsk.columnsk.registerWorldk.registry.addk.commands.claimk.cheats.addk.workers.runreturnsgame.fallits API, on the game, typedgame.fall.speed = 300;game.spawn({ texture: 'rock', tags: ['falls'],});one key a pack, never shared

A whole pack

import type { Pack } from '@phaser-ae/kernel';

/** Everything tagged 'falls' falls, at a speed the game can change. */
export function fall(speed = 120): Pack<{ fall: { speed: number } }> {
    return {
        id: 'fall',
        attach(k) {
            const api = { speed };
            // a fixed step, ordered by name against other packs' steps
            k.simulate.add('fall', { before: 'collision' }, dt => {
                for (const h of k.roster.tagged('falls')) {
                    h.y += api.speed * dt;
                }
            });
            return { fall: api };   // becomes game.fall
        },
    };
}

A game using it

import { game } from './engine.js';

game.scenes.register({
    name: 'play',
    enter() {
        const ship = game.spawn({
            id: 'ship', texture: 'ship', x: 360, y: 1100,
        });
        game.every(0.6, () => game.spawn({
            id: 'rock', texture: 'rock', tags: ['falls'],
            x: game.rng.between(40, 680), y: -40,
        }));
        game.step('steer', dt => {
            if (game.input.keyDown('ArrowLeft')) ship.x -= 300 * dt;
            if (game.input.keyDown('ArrowRight')) ship.x += 300 * dt;
        });
    },
});
game.scenes.go('play');

The bake

From a manifest to a game.

manifest.json"packs": [ core-2d, ui, bars, save ]the bakecomposesthe packs,in orderengine.tscreateGame(…) .use(core2d()) .use(ui()) …your codeimport { game } from './engine.js'game.jsstrict types,the rules,one bundle

manifest.json

{
  "name": "rock-dodge",
  "screen": { "width": 720, "height": 1280, "fit": "letterbox" },
  "seed": 1,
  "packs": [
    { "id": "core-2d" },
    { "id": "bars" },
    { "id": "fall", "options": { "speed": 160 } }
  ]
}
  • The manifest names the packs, in order, each with its options.
  • The bake composes them into the game's engine.ts, writes each pack's page into the game's docs/, and types game with each pack's API.
  • The build checks the game with strict TypeScript and the kernel's rules, then bundles it into one file. Add a pack and bake again, and its API is there.

The rules

The kernel's 7 rules.

  1. 1Everything on stage is a roster object. The roster is the single ground truth of what exists; a thing not in it does not draw.
  2. 2No unattributed pixels. Game code cannot reach the canvas or the GPU.
  3. 3All time flows from the clock. Simulation runs in fixed steps, in seconds.
  4. 4All chance flows from the rng. Every random number comes from the seeded rng, so a seed and the same input replay the same game.
  5. 5One conventions page. Units are set once, in docs/reference/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. 6Garbage fails loud. In dev mode a non-finite number, an unknown name or a write to a killed object throws; in production it is ignored.
  7. 7The kernel never imports a pack. Packs reach each other only through the composed game's keys, signals, hooks and the definitions registry.

Events and cleanup

Hooks, signals, and scenes that clean up.

Hooks: a call, nowscene.enterapp.resizescene.exitSignals: a record, next stepstep nemit collision.hitstep n + 1reads [hit, hit, …]

A hook is a call made the moment something happens. A signal is recorded instead, and what one step emits, the next step reads as a list. Because the simulation uses signals, a step's result never depends on which listener ran first.

Titlegone: everything it made went with itPlaysuspended: draws, but holds stillPausepushed on top; pop and Play resumesPlay ownsobjectstimersstepsframe callbacksundo (k.own)

Objects, timers and steps belong to the scene that made them, and go when it exits. A pause scene pushed on top freezes the scenes beneath it.

Built on it

Just some of the expansion packs.

View all packs →
  • 3D Fire Effects
  • 3D Magic Effects
  • 3D Party Effects
  • 3D Water
  • Aurora
  • GPU particles
  • Jet combat
  • Lens Flare
  • Match-3 boards

Try it

Ask your agent.

Start a game from nothing

Make a Phaser AE game called Rock Dodge: a little ship dodges falling rocks, faster every level, with a score and a game-over screen. Start it with `npx -y @phaser-ae/cli blank rock-dodge --idea "A ship dodges falling rocks, faster every level."`, then build it, play it and publish it.

See one frame

In this Phaser AE game, read docs/00-kernel.md, then walk me through one frame of the game: which of my steps run, in what order, and where the packs' steps and render passes come in.

Add a pack

Add the bars pack to this Phaser AE game and show the player's health as a bar in the top-left corner that turns red as it empties. Search the packs first with `npm run -s packs -- search "health bar"`.

Replay a run

This Phaser AE game is deterministic, thanks to the kernel's seeded rng. Add an instant replay of the player's last life, using the kernel's createHistory.

Pause everything

Add a pause button to this Phaser AE game with the ui pack's pauseButton, and check that timers, animations and steps all stop while it's paused.

Paste one into Claude Code, Codex, Cursor or another coding agent with the Phaser AE CLI. Set up the CLI →

Phaser Pro

163 packs are free. 195 more come with Pro.

Everyone gets the same kernel. Pro unlocks all of the expansion packs, including whole kinds of game, 3D worlds and the showpiece effects, plus new packs as they're released.

Upgrade to Pro

The source

The source comes with your game.

Each engine release puts the kernel's full source and docs in your game's folder (phaser/engine/kernel), where your agent reads them as it builds. Version 3.0.0-2, from commit c164d62f.

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