Phaser with AI: build a 2D game with an agent

By GamesByAI · Updated · 43 sources

Phaser is a free, open-source 2D framework for browser games, released under the MIT license. It runs 25 of the 492 games in our catalog. Its repository ships agent skills for every major subsystem, and its README says the API is “well understood by every major frontier LLM” (Phaser README).

The catch is older code. Phaser 3 tutorials and examples use APIs that Phaser 4 removed or changed, models reproduce them, and two of those changes fail silently.

This guide sets up a project that keeps the agent on Phaser 4, builds a small game one scene at a time and lets the agent test it. Every fact was checked on October 1, 2026. To install the agent, use the Claude Code guide or the Codex guide.

1. Start a Phaser 4 project from the official template

Phaser 4.0.0 came out on April 10, 2026, and the current release is 4.2.1, from July 9, 2026 (4.0.0, 4.2.1). Create the project with Phaser’s own CLI. For a first project its README suggests Web Bundler, then Vite (create-game); pick TypeScript when it asks:

npm create @phaserjs/game@latest my-game
cd my-game
npm install --save-exact phaser@4.2.1

The last line matters because the Vite TypeScript template on GitHub pins Phaser 4.0.0 (template). --save-exact keeps the version fixed, so the agent always works against the release its skills describe.

Tell the agent which dev command to run. The template’s npm run dev and npm run build first run a log.js script, which sends the template name, the build type and the Phaser version to a Phaser Studio server. npm run dev-nolog and npm run build-nolog skip it (template README). The dev server runs on port 8080, and both Vite configs already set base: './', which matters when you upload the build later.

2. Install the Phaser agent skills

The npm package carries the skills. After the install above, node_modules/phaser/skills/ holds 28 skill folders, from scenes, physics-arcade and tilemaps to v3-to-v4-migration (package files, repository). Each is a standard SKILL.md with a name and a description, so Claude Code keeps only those descriptions in context until a task matches one (skills docs).

Copy them from the package, not from GitHub, so they always match the installed version. This script uses Node’s built-in fs.cpSync, so it works on Windows too:

// scripts/sync-phaser-skills.mjs: copy the skills of the installed Phaser
import { cpSync } from 'node:fs';

cpSync('node_modules/phaser/skills', '.claude/skills', { recursive: true });
// For Codex, copy to '.agents/skills' instead.
npm pkg set scripts.postinstall="node scripts/sync-phaser-skills.mjs"
npm install

npm pkg set adds the script without hand-editing package.json, and the bare npm install after it runs the project’s postinstall script (npm scripts). npm runs that hook only for an install without package names (npm source), so after a targeted upgrade such as npm install --save-exact phaser@<version>, run node scripts/sync-phaser-skills.mjs yourself.

Codex reads skills from .agents/skills (Codex docs); our skills guide lists the folders Cursor and Gemini CLI read.

Each skill names the source files it describes, such as src/scene/Scene.js. In your project those live in node_modules/phaser/src/, because the npm package ships the source (package files). Put that path in your rules file, so the agent reads the real code when a skill isn’t enough.

3. Phaser 3 code that models still write

These patterns fill years of Phaser 3 tutorials and examples. Each was removed or changed in Phaser 4, according to the migration guide and the 4.2.1 source.

Phaser 3 code Phaser 4 code If it slips in
setTintFill(color) setTint(color), then setTintMode() with Phaser.TintModes.FILL no tint: the method is now an empty stub that logs a console error
Math.TAU as a quarter turn Math.PI_OVER_2 silent: TAU is now a full turn
Math.PI2 Math.TAU removed
Geom.Point Math.Vector2 the class is gone
preFX, postFX enableFilters(), then filters.internal or filters.external; cameras have filters already preFX is undefined
BitmapMask the Mask filter: filters.internal.addMask() the class is gone
setPipeline('Light2D') setLighting(true) pipelines are gone
roundPixels on by default off by default; pixelArt: true turns it on silent: sprites can sit between pixels
Phaser.Structs.Set the native Set the class is gone
Mesh, Plane, Camera3D removed; use a 3D library the classes are gone

The TAU row is the dangerous one. In Phaser 3.90, Phaser.Math.TAU is π/2 (3.90 source); in Phaser 4 it’s 2π (4.2.1 source). Code that used it still runs and turns everything four times too far. Glow and mask filters need enableFilters() on a game object first (filters skill).

One line of the migration guide doesn’t match the code: it says Phaser.Structs.Map was replaced with the native Map, but 4.2.1 still exports it, contains() included (Structs source). Only Structs.Set is gone.

TypeScript catches most of the table, because Phaser ships its own type definitions (README). TAU and roundPixels pass any type check, so they belong in your rules file by name.

4. Copy the project rules for Phaser

Save these rules as AGENTS.md in the project root. Codex reads it as project guidance (Codex docs), and Claude Code reads it when the project has no CLAUDE.md. With a CLAUDE.md, put @AGENTS.md at its top (Claude Code docs). They are conventions for this project, with sources for the Phaser facts behind them below:

# Game rules: Phaser 4.2.1

- Phaser 4.2.1 from npm, pinned exactly. Vite, TypeScript.
  Run npm run dev-nolog and npm run build-nolog.
- Phaser 4 APIs only. Check .claude/skills first; the source of truth is
  node_modules/phaser/src. Porting Phaser 3 code? Read v3-to-v4-migration.
- Never use: setTintFill, Geom.Point, BitmapMask, preFX, postFX,
  setPipeline, Math.PI2, Structs.Set.
  Math.TAU is a full turn (2π). A quarter turn is Math.PI_OVER_2.
- Config: type Phaser.AUTO. Scale.FIT with CENTER_BOTH, base size 960x540.
  pixelArt: true for pixel art. input.activePointers: 2.
- Scenes: Boot, Preloader, Game, UI. Launch UI in parallel with Game.
  Reset per-run state in init(), never in the constructor.
- Create animations once, in Preloader. They are global.
- Listeners on this.events, this.registry.events, this.game.events,
  this.scale, this.sound, window or document need a matching off()
  in a SHUTDOWN handler.
- Movement through Arcade Physics velocity, never x += n per frame.
  Pickups use overlap; walls use collider. Pool bullets and pickups
  in a Group with maxSize.
- Game rules (scoring, lives, waves) live in src/rules/ with no Phaser
  import, tested with Vitest.
- Fullscreen requests run on pointerup. No hover-only actions.
- After every change run npm run typecheck, npm test and npm run e2e.

The facts behind them come from Phaser’s own skills and source:

  • State and animations. A scene’s constructor runs once, but init() runs on every start and restart (scenes skill). Animations created with this.anims.create() are global; creating them again logs a warning (animations skill).
  • Physics. overlap detects without pushing and suits pickups, collide separates bodies, and Arcade uses a fixed timestep by default (Arcade skill). group.get() reuses an inactive member before it creates a new one (groups skill).
  • Renderer. Phaser.WEBGL has no Canvas fallback, while AUTO does (config skill), and the migration guide calls the Canvas renderer deprecated (migration guide).

5. Clean up the listeners that outlive a scene restart

Phaser’s own events skill calls listeners that are never removed “the most common source of bugs” (events skill). In a game they show up on the second run: a listener added in create() is added again on every restart, so the score counts twice or a sound plays twice.

Phaser cleans up part of this for you. When a scene shuts down, its input plugin removes all of its own listeners (InputPlugin.js). The scene’s own this.events emitter only drops its transition listeners (Systems.js), and the registry, the game events, the scale manager and the sound manager are shared by the whole game. Listeners on those emitters pile up with every restart unless you remove them:

create() {
  this.events.on('star-caught', this.addScore, this);
  this.scale.on('resize', this.layout, this);
  this.events.once(Phaser.Scenes.Events.SHUTDOWN, () => {
    this.events.off('star-caught', this.addScore, this);
    this.scale.off('resize', this.layout, this);
  });
}

off() removes a listener only when you pass the same function reference that on() received; the context is an optional filter that must match if you give it (EventEmitter.js). Class methods and arrow functions stored in a field both work. A new inline arrow function in the off() call matches nothing.

Phaser’s events skill shows this pattern with an input listener; in 4.2.1 the input plugin clears those itself, but the same habit protects the emitters that don’t.

6. Build the Phaser game one scene per prompt

The example game: catch falling stars in a basket, dodge rocks, three lives, score on screen, restart. Each prompt ends with a check the agent can run.

First prompt: scenes and movement.

Read AGENTS.md. Set up Boot, Preloader, Game and UI scenes. The basket moves with the arrow keys or A and D on desktop and follows a dragging finger on a phone, inside the screen edges. Use rectangles and circles instead of art for now. Done when npm run typecheck passes and the basket moves in phone emulation too.

Second prompt: one complete run.

Add stars and rocks that fall from random x positions, pooled in two Groups. Catching a star adds 1 to the score; a rock costs a life; the run ends at zero lives with a game over panel and a Restart button. Keep scoring and lives in src/rules/run.ts with Vitest tests. Done when catching one star after five restarts still adds exactly 1.

Third prompt: the phone pass.

Make the game fit any screen with Scale.FIT, add a fullscreen button that requests fullscreen on pointerup, and play a catch sound once audio is unlocked. Done when two fingers work at once in phone emulation and the console stays empty.

For character art, AI sprite animation for games covers Phaser sprite sheets and atlases.

7. Test the rules without a browser, and the game with one

Rules that don’t import Phaser test in milliseconds with Vitest (npm install --save-dev vitest):

// src/rules/run.test.ts
import { test, expect } from 'vitest';
import { newRun, catchStar, hitRock } from './run';

test('a run ends exactly when the last life is lost', () => {
  let run = newRun(); // 3 lives, score 0
  run = catchStar(run);
  run = hitRock(hitRock(run));
  expect(run.over).toBe(false);
  run = hitRock(run);
  expect(run).toMatchObject({ score: 1, lives: 0, over: true });
  expect(catchStar(run).score).toBe(1); // no points after the end
});

For the whole game, give the agent a clock. Phaser’s loop is public: game.loop.sleep() stops its requestAnimationFrame loop (TimeStep.js), and game.step(time, delta) runs one full update and render of every active scene (Game.js). This hook, loaded only with ?test=1, lets a test move time in whole frames. Count frames as integers: a loop that adds 1000/60 to a float until it reaches 1000 runs 61 times, not 60.

// src/test-hooks.ts
import Phaser from 'phaser';

type GameScene = Phaser.Scene & { snapshot(): object };

export function installTestHooks(game: Phaser.Game) {
  const dt = 1000 / 60;
  let now = performance.now();
  game.events.once(Phaser.Core.Events.POST_STEP, () => game.loop.sleep());
  const advanceFrames = (n: number) => {
    for (let i = 0; i < n; i++) game.step((now += dt), dt);
  };
  const scene = () => game.scene.getScene('Game') as GameScene;
  Object.assign(window, {
    advanceFrames,
    advance: (ms: number) => advanceFrames(Math.round(ms / dt)), // 1000 ms = 60 frames
    restart: () => scene().scene.restart(),
    catchStar: () => scene().events.emit('star-caught'),
    gameState: () => (game.scene.isActive('Game') ? scene().snapshot() : { scene: 'loading' }),
  });
}

Call it from src/main.ts when the URL has ?test=1. Have the Game scene’s snapshot() return scene: 'playing', the score, the lives and listeners: this.events.listenerCount('star-caught'). While the loop sleeps, scenes only move when the test steps frames, including the switch from Preloader to Game.

Point Playwright’s web server option at the template’s dev server, with command: 'npm run dev-nolog' and url: 'http://localhost:8080'. Then this test restarts the run five times and checks for the doubled-listener bug:

// e2e/restart.spec.js
import { test, expect } from '@playwright/test';

test('five restarts leave one listener and one point per star', async ({ page }) => {
  const errors = [];
  page.on('pageerror', (e) => errors.push(e.message));
  page.on('console', (m) => m.type() === 'error' && errors.push(m.text()));

  await page.goto('http://localhost:8080/?test=1&seed=1');
  // Step a few frames per poll, so assets can load between polls.
  await page.waitForFunction(() => {
    window.advanceFrames?.(3);
    return window.gameState?.().scene === 'playing';
  });

  for (let i = 0; i < 5; i++) {
    await page.evaluate(() => { window.restart(); window.advanceFrames(30); });
  }
  const before = await page.evaluate(() => window.gameState());
  expect(before.listeners).toBe(1);

  await page.evaluate(() => { window.catchStar(); window.advanceFrames(1); });
  expect((await page.evaluate(() => window.gameState())).score).toBe(before.score + 1);
  expect(errors).toEqual([]);
});

Playtesting games with AI adds a general smoke test that plays one seeded round; adapt its port and hooks to this template.

8. Make the Phaser game work on phones

  • Screen fit. Scale.FIT keeps the aspect ratio inside the parent element. The parent needs a real size, the ScaleManager ignores its padding, and CSS on the canvas itself conflicts with it (scale skill).
  • Two thumbs. Phaser creates one touch pointer by default: input.activePointers defaults to 1 (Config.js), and touches go to pointers 1 and up, because pointer 0 is the mouse (InputManager.js). Move-and-jump controls need activePointers: 2 or more.
  • Audio. Phaser unlocks Web Audio on the first touch, click or key press by itself. To start music, wait for the sound manager’s unlocked event when this.sound.locked is true (audio skill).
  • Fullscreen. Request it on pointerup, since touch browsers can block it on pointerdown, and give your iframe allowfullscreen (scale skill).

How to make a mobile game with AI covers the phone test session.

9. Build and ship the Phaser game

Run npm run build-nolog. The template writes the game to dist/, and the relative base: './' paths fit itch.io, which requires relative asset paths. Zip the contents of dist/ with index.html at the root of the archive and upload it as an HTML game (itch.io docs). Where to publish a browser game compares the other hosts.

Phaser Editor MCP and the Phaser Game Agent

Phaser Studio also makes two AI products next to the open-source framework. Neither is needed for the setup above.

  • Phaser Editor v5 includes an MCP server that lets an AI client work inside a running editor: list and open scenes, edit objects, take screenshots, and manage assets, spritesheets, animations and tilemaps. The docs show setup for Claude Desktop, Cursor and VS Code and say many features are still to come (docs); the release post counts more than 40 tools (release post).
  • The Phaser Game Agent builds a game from a description in a cloud sandbox, with Phaser AE, a framework Phaser Studio rebuilt for AI agents, not Phaser 4 (announcement). Its MCP server works with Claude Code, Codex, Cursor and others. Sandbox time is billed per minute, and a fully featured game typically costs around 500 credits (MCP page). Since August 2026 you can export the TypeScript source (export post).

Phaser games made with AI in the catalog

We list 25 Phaser games as of October 1, 2026: 10 credit Claude Code, 7 Codex and 3 Cursor. The entries record the engine and the tools, not which skills or tests a creator used.

AI tools credited on the Phaser games in our catalog
  1. Claude Code1040%
  2. Codex728%
  3. Cursor312%
  4. GitHub Copilot312%
  5. Bolt14%
  6. ChatGPT14%

Out of 25 games; a game can count in several bars.

  • Reach the Relay placed 65th of 945 at Vibe Jam 2026: a turn-based RPG with active-time battles, made with Claude Code.
  • Hired Swords placed 68th: an autobattler that pits your team against other players’ saved ghost teams, also made with Claude Code.
  • Vibe Party is a board game with minigames for up to four players, online or local, made with Codex.
  • Block Yard is a mobile-first factory puzzle with a level editor for sharing puzzles, made with Claude Code.
  • Dungeon Cast draws a first-person dungeon with raycasting, in a 2D engine, made with Claude Code.
  • Wagon Bones placed seventh at the third AI Browser Game Jam: Balatro-style scoring with d12 dice on the Oregon Trail.

Browse the Phaser hub, Claude Code games, Codex games and the engine comparison, which sets Phaser against Three.js, PixiJS and Godot.

Finish the Phaser build

Call the build done when the release build loads from dist/, plays a full run, restarts five times without doubled events, and works with two thumbs on a real phone, with no console errors. Record the Phaser version and the skills you copied with the build, so the next session starts from the same facts.

Then submit the game with its playable URL and an honest account of what the AI made. Every game is reviewed before it’s listed, under our editorial policy.

Games to look at

Questions

Can AI make a Phaser game?

Yes. A Phaser game is JavaScript or TypeScript in text files, so coding agents such as Claude Code, Codex and Cursor can write, run and test it. Our catalog lists 25 Phaser games, including Reach the Relay and Hired Swords, both in the Vibe Jam 2026 top 70 and both made with Claude Code.

Where are Phaser's AI agent skills?

In the skills folder of the Phaser repository, and inside the npm package. After npm install phaser@4.2.1 they sit in node_modules/phaser/skills: 28 folders, from scenes and tilemaps to a v3-to-v4 migration skill. Copy them into .claude/skills for Claude Code or .agents/skills for Codex.

Should a new game made with AI use Phaser 3 or Phaser 4?

Phaser 4. It came out on April 10, 2026, its skills cover Phaser 4 APIs, and the official templates install it. Add a rules file that lists the Phaser 3 APIs it removed or changed, because older tutorials and examples still use them.

Does the Phaser Game Agent use Phaser 4?

No. Its page says games are built with the Phaser Agentic Engine, Phaser AE, in a cloud sandbox billed in credits. Since August 2026 you can export the TypeScript source with the exact Phaser AE version. Phaser 4 itself is free and open source.

Why does my Phaser game ignore a second finger on a phone?

Phaser creates one touch pointer by default (input.activePointers is 1). For controls that need two thumbs at once, such as move and jump, set input.activePointers to 2 or more in the game config, or call this.input.addPointer().

Do I need Phaser Editor to make a game with AI?

No. The agent writes scenes as code. Phaser Editor v5 adds an MCP server that lets an AI client work on editor scenes, assets and tilemaps, which helps if you lay out levels visually in the editor.