Citation and evidence

Developing Browser Games with AI

39 min full readUpdated 77 references

This article's verification

Report a problem with this article

Edit article
More

Use this article

Raw MarkdownExplore connections

Improve this page

Revision history

Topics

AI AgentsAI Code GenerationDeveloper ToolsSoftware Development

Cite this article

Developing browser games with AI combines ordinary web game development with an assistant that helps plan, edit and check the project. The game can run entirely in the browser even when an AI service helped write it. Using an assistant during development does not by itself make that model a runtime dependency.

This guide covers a small JavaScript or TypeScript game, from a reproducible template to a hosted test build. It is part of Developing Games with AI, which explains the shared workflow; this page goes deeper on the browser-specific parts: framework setup, giving a coding agent a browser it can drive, input, audio, loops, hosting and the failures that are specific to the web. The sample brief, prompts and acceptance checks are recommended working methods, not a claim that an assistant will finish a game autonomously or on a particular schedule. Versions and commands were checked against each project's own documentation on 30 October 2026.

Choose the smallest suitable stack

Choose the framework for the game you intend to maintain. Avoid adding a renderer, physics library, networking service and UI framework merely because an assistant suggested all four.

Logo of the 2D browser game framework discussed in this guide
An official logo for the 2D framework discussed below.
Starting pointDocumented roleUseful first task
PhaserA 2D game framework for desktop and mobile browsers, using JavaScript or TypeScript[1]A single scene with movement, a collectible, a hazard and restart
Three.jsA general-purpose JavaScript 3D library; its introductory example assembles a scene, camera, mesh and renderer[2]A small scene in which the player can move and interact with one object
An existing working web projectThe versions, dependencies and build scripts already in the repositoryOne isolated change that preserves the existing game

Expanded article table

Phaser supplies game-oriented systems such as scenes and physics. Its own documentation says it is not the right choice for a fully 3D game, for publishing on a modern console, or for someone who wants a no-code editor instead of learning JavaScript.[1] Three.js supplies rendering building blocks; its game tutorial states that three.js is not a game engine and provides no collisions, physics, input systems or pathfinding, so a game must add them.[22] A 3D screenshot is therefore a useful rendering check, but it does not demonstrate that a game loop, collision system or save format works.

Current releases, as recorded in the npm registry on 30 October 2026:

PackageLatest versionPublishedNote
phaser4.2.19 July 2026Phaser 4.0.0 was published on 10 April 2026; MIT license[26]
three0.186.124 September 2026Its import-map instructions ask for one consistent version of the library and addons[24][27]
vite8.3.521 October 2026Requires Node.js 20.19+ or 22.12+[73]
@playwright/test1.64.114 October 2026Supports Node.js 22.x, 24.x or 26.x[35]

Expanded article table

For the first project, a 2D collect-and-avoid game provides a bounded exercise. Phaser's official introductory tutorial uses a player, platforms, collectible stars and hazards; note that the tutorial itself is written for Phaser 3.[3] The workflow below uses a different illustrative brief: collect five tokens in one room, avoid one hazard, and restart after either outcome.

Phaser 4 is not Phaser AE

Phaser Studio also sells a separate AI product. RuntimeWire reported on 1 July 2026 that Phaser Studio had launched Phaser Game Agent, which the company says turns a one-sentence idea into a playable browser game.[29] In a 23 July 2026 post, Phaser Studio explained that the Game Agent runs on Phaser AE, "a completely separate engine" built for LLMs that does 2D and 3D, is WebGPU-first and "is not open source", and that the Game Agent "does not generate Phaser code".[28] Phaser's agent index (llms.txt) and the Markdown versions of its pages that phaser.io serves to agents (for example the create-game tutorial at the same URL plus .md) carry text addressed to AI agents that recommends starting users with Phaser AE.[25][30] An agent that reads those pages may suggest the commercial product. If you want the open-source framework, say so in the project instructions. The rest of this guide covers Phaser 4, the MIT-licensed npm package.

Establish a versioned baseline

Pick one of the two routes below, run it unmodified, and commit the result before the first agent change.

Route A: an official Phaser template

Phaser documents an interactive starter command:[4]

npm create @phaserjs/game@latest

It selects and configures a template. Phaser's tutorial for the tool says it accepts a folder name (npm create @phaserjs/game@latest my-game, or . for the current folder) and recommends that first-time users choose Web Bundler and then Vite. It also offers other bundlers, framework integrations such as React and Vue, and three demo games.[25] Read the generated template's README before running its scripts. The command requests the current generator, so it is not an instruction to reproduce a permanently fixed engine version.

Version labels require care. At the time this guide was checked, the Phaser Vite repository's package.json description still read "A Phaser 3 template using Vite", while its README described a Phaser 4 template updated for Phaser 4.0.0 and Vite 6.3.1. Its package.json pinned phaser to exactly 4.0.0 and allowed vite in the range ^6.3.1 (any later Vite 6 release, but not Vite 7 or 8). Both are behind the npm releases in the table above.[6][26] Its documented scripts were:[6]

CommandWhat the template README says it does
npm installInstall project dependencies
npm run devLaunch a development web server, on http://localhost:8080 by default
npm run buildCreate a production build in the dist folder
npm run dev-nologDevelopment server without sending anonymous data
npm run build-nologProduction build without sending anonymous data

Expanded article table

The README explains that the plain dev and build scripts run a log.js file that makes a call to a Phaser Studio domain with the template name, build type and Phaser version, and that the -nolog variants or deleting the file stop it.[6] Tell the agent which variant the project uses so it does not "fix" the scripts on its own. The template's Vite configuration sets base: './', which keeps built asset URLs relative.[62] Inspect the actual files rather than treating a search snippet or repository description as an API guarantee.

Avoid copying Phaser from the installation page's CDN snippet without checking the version. As checked, its jsDelivr and cdnjs examples still referenced Phaser 3.86.0.[4]

Route B: Vite plus three.js

Three.js recommends installing from npm with a build tool for most users, and its installation page uses Vite:[24]

npm install --save three
npm install --save-dev vite
npx vite

npx vite serves the project (the manual shows a URL like http://localhost:5173), and npx vite build writes a production build to dist/.[24] To start from a scaffold instead, Vite's guide documents npm create vite@latest, with template names including vanilla and vanilla-ts; an illustrative TypeScript start is npm create vite@latest my-game -- --template vanilla-ts, followed by npm install --save three.[73]

The CDN route needs an import map, and three.js warns to import all dependencies from the same version and the same CDN, because mixing sources can include duplicate code or break the application.[24] It also needs a local server: the manual says important features do not work when an HTML file is opened directly by double-clicking it, and lists npx serve ., npx http-server and python -m http.server among the options.[24]

Record and lock

Record the selected framework, its exact installed version, the template revision, Node.js version and package manager in the project notes. Keep the lockfile in version control. With an npm project that has a compatible, synchronized lockfile, npm ci installs from it and exits with an error rather than silently updating a mismatch between the manifest and lockfile.[5]

Run the unmodified project first. Save the working baseline, a screenshot, the launch command and the build output. Ask the assistant to explain where startup, scenes, assets and styles live before asking it to change them. Do not accept an engine upgrade as an incidental part of adding a score counter.

Give the agent the framework's own documentation

Models are trained on older material, and both libraries have changed. Point the agent at documentation that matches the installed version instead of relying on what it remembers.

SourceWhat it providesHow to use it
Phaser skills in the npm packageThe phaser package ships a skills/ directory with 28 SKILL.md files (checked in 4.0.0, 4.1.0 and 4.2.1), covering topics such as scenes, input, scale, loading assets, Arcade physics, and a v3-to-v4-migration skill[31]Copy the relevant folders into the project's .claude/skills/<skill-name>/SKILL.md location, which Claude Code reads for project skills[33]
Phaser docs MCP serverPhaser's llms.txt lists https://phaser.io/oss/docs/mcp (Streamable HTTP) with the tools search_phaser_docs, read_phaser_doc and list_phaser_docs[30]Add it as an HTTP MCP server (example below)
three.js llms.txtInstructions for LLMs at https://threejs.org/docs/llms.txt, including an example that labels a three.min.js r128 CDN script as an outdated pattern and recommends import maps, plus guidance on choosing WebGLRenderer or WebGPURenderer[32]Link it in the project instructions file

Expanded article table

Illustrative commands, using Claude Code's documented claude mcp add --transport http <name> <url> form and the standard shell:[34]

claude mcp add --transport http phaser-docs https://phaser.io/oss/docs/mcp
mkdir -p .claude/skills
cp -R node_modules/phaser/skills/v3-to-v4-migration .claude/skills/

The migration skill matters because Phaser 4 replaced Phaser 3's renderer pipelines with render nodes, unified FX and masks into a filter system and deprecated the Canvas renderer.[31] Code that an assistant recalls from Phaser 3 examples may therefore not match the installed engine. The migration skill says a game that uses only the standard API, such as sprites, text and tilemaps, should work transparently with the new renderer, so problems tend to appear in rendering effects and internals.[31]

Turn the idea into a testable brief

Write the game's rules before its presentation. The following is a project specification, not framework code:

Build one room with placeholder shapes.

Player: moves within the room using keyboard or touch controls.
Objective: collect five tokens; each token counts once.
Failure: touching the hazard ends the round.
Win: collecting the fifth token ends the round.
Restart: returns to the initial room, score and object positions.

Out of scope: accounts, multiplayer, inventory, procedural levels,
runtime AI, external art services and a shop.

Implement one stage at a time. Preserve the installed dependencies.
After each change, run the available checks, open the game, exercise
that stage and report the observed result and remaining limitations.

Specify what happens when two events occur together. For example, choose whether a hazard hit or the last pickup wins when both happen in the same update. That is a design decision the assistant should implement explicitly. Otherwise a bug fix may change the game rules without anyone noticing.

Use a small task table:

TaskObservable acceptance check
MovementPlayer responds to each supported control and stays inside the room
PickupOne token disappears and the score increases exactly once
FailureHazard contact changes the round state and stops further scoring
WinThe fifth valid pickup produces the intended end state
RestartRepeating a round does not retain score, extra listeners or old hazards
PresentationLabels and controls remain readable at the chosen phone and desktop sizes

Expanded article table

Commit an accepted stage before starting the next. When a change fails, preserve the evidence and compare it with the last working state rather than repeatedly asking for a broad rewrite.

A project instructions file for a browser game

The hub explains instruction files such as CLAUDE.md and AGENTS.md; see Write a project instructions file. Anthropic's guidance is that the most useful line in such a file is the command that runs the project's check, and that the agent should report the command it ran and its output, or the screenshot, as evidence.[48] For a Phaser Vite template the browser-specific part might look like this (illustrative; every command is one documented above or below):

## Commands
- Dev server: npm run dev-nolog   (http://localhost:8080)
- Build: npm run build-nolog       (output: dist/)
- Tests: npx playwright test

## Facts
- Engine: phaser 4.0.0 as pinned in package.json. Use Phaser 4 APIs;
  read .claude/skills/v3-to-v4-migration before using filters or masks.
- Static assets live in public/assets and are loaded with relative
  paths such as 'assets/player.png' (no leading slash).
- Never open index.html from the filesystem; always use the dev server.

## Done means
- The build succeeds, the Playwright tests pass, and the report includes
  the command output and a screenshot of the state that changed.

Give the assistant the correct project context

A useful request identifies the relevant files, the installed framework version, the current behavior, the desired behavior and the checks that must pass. Include an actual error message or screenshot when diagnosing a failure.

Phaser's documentation describes scenes as the way to divide a game into logical sections. A scene's callbacks are init(), preload(), create() and update(), and a stopped scene has the status SHUTDOWN and can be started again.[7] Tell the assistant where the current project creates its scene state and how it cleans up round-specific objects. A restart bug can come from a value or listener surviving longer than intended, even when the initial round works. Phaser's sound manager is also global to the game rather than to a scene, so a looping sound started in one scene keeps playing after a scene change unless it is stopped.[56]

For a Three.js project, also identify the renderer, camera, animation loop and ownership of loaded resources. Request changes to the existing structure instead of a second unrelated rendering loop. Have the assistant list any new dependency and explain which requirement it serves before installing it.

Keep design decisions in a short project file: coordinate convention, target browsers, supported input, asset directories, pause behavior and release command. Put factual observations in the task report. A screenshot showing a player next to a token is different evidence from an observed pickup that changes the score.

Keep the loop independent of frame rate

A browser game's loop is normally driven by requestAnimationFrame(). MDN notes that its callback rate generally matches the display refresh rate (60 Hz is most common, but 75, 120 and 144 Hz are widely used), that the callback is one-shot and must be requested again for each frame, and that animation must use the supplied timestamp, or it will run faster on high-refresh-rate screens.[49] Phaser runs this loop for you and passes time and delta to each scene's update(time, delta);[7] three.js examples use renderer.setAnimationLoop().[2]

A common assistant mistake is to move objects by a fixed amount per frame. Ask for movement scaled by elapsed time, and test the same input on a 60 Hz and a high-refresh display if you have one. For rules that must be reproducible, such as replays, deterministic AI or physics that should not change with frame rate, a fixed simulation step is the standard technique. MDN's game-loop article describes updating the simulation at a constant frequency and drawing as often as the device allows, with an example that runs the simulation at 20 Hz and warns that a very large number of pending ticks means the game was asleep or the machine cannot keep up.[50] Glenn Fiedler's widely cited "Fix Your Timestep!" (2004) describes the accumulator pattern and the "spiral of death" when a simulation cannot keep up with the steps it is asked to take; it suggests leaving headroom or clamping the number of steps per frame, and its final example caps a frame's time at 0.25 seconds.[51]

Determinism across browsers is a separate problem. In the Battle Drones project, Simon Willison reported that Claude had to replace Math.sin(), Math.cos() and related calls with custom versions because Chrome and Safari returned very slightly different numbers, which desynchronized replays.[70] If replays or lockstep multiplayer matter, record a replay in one browser and check it in another.

Handle input and resizing together

Do not treat mobile support as a smaller screenshot of the keyboard game. Specify a touch interaction that can perform every required action, then test it. Check finger reach, overlapping controls, releasing a finger outside a button and returning after the browser loses focus.

Phaser's input system unifies mouse and touch pointer events; interactive game objects must be enabled for input with setInteractive().[8] That API does not choose a playable control scheme. The assistant still needs a brief describing whether movement uses a virtual stick, directional buttons, dragging or tapping a destination.

If the project handles DOM pointer events itself, two browser rules matter. MDN describes Pointer Events as a single, hardware-agnostic event model for mouse, pen and touch.[52] By default the browser handles panning and pinch gestures itself and sends pointercancel when it takes over a touch; the CSS touch-action: none disables browser panning and zooming on an element, and MDN names a game surface as a typical use.[53] A drag that stops halfway on a phone but works with a mouse often has this cause.

Choose a logical game size and a scaling policy. Phaser's Scale Manager distinguishes FIT, which fits the game within an area while preserving aspect ratio, from RESIZE, which resizes the canvas to the available space regardless of aspect ratio; it also offers ENVELOP and EXPAND.[9] Decide how empty margins, the HUD and touch controls should behave. Check that a tap at the apparent center of a control still activates it after the canvas moves or resizes. Phaser's documentation says scale.updateBounds() is needed when the canvas position or visibility is changed by some other method.[9]

For Three.js, keep the canvas's CSS display size, drawing-buffer dimensions and camera aspect ratio consistent. Its responsive-design guide treats these separately; changing CSS dimensions alone can stretch the rendered scene.[23]

Fullscreen has its own rules. Element.requestFullscreen() requires transient user activation, so it must be called from a user input handler such as a tap.[54] Phaser's Scale Manager documents startFullscreen() under a pointerup event, notes that a game inside an iframe is blocked from fullscreen unless the iframe has the allowfullscreen attribute, and says ESC, navigation or opening another tab cancels it.[9] MDN's browser-compatibility data records that on iOS Safari the Fullscreen API is available only on iPad, not on iPhone, and that its overlay button and swipe-to-exit make it unsuitable for some games.[55] Design the iPhone layout to work without fullscreen.

Define background-tab behavior too. The Page Visibility API exposes whether a document is visible, and browsers commonly throttle background work; most stop sending requestAnimationFrame() callbacks to background tabs.[10] Phaser emits hidden and visible events per the Page Visibility API, and pause and resume events that usually occur at the same time.[58] For a small single-player game, pausing and clearing held input when the page becomes hidden is a reasonable policy; MDN's game-loop article lists treating the gap as a pause as one option, and notes that it is problematic for most multiplayer games.[50] Test resuming without a large movement jump or a stuck key. Do not infer elapsed gameplay time solely by counting rendered frames.

Import assets through the build pipeline

Keep placeholders until interaction works. Then replace a limited asset set, checking collision bounds and readability again. A larger sprite can change the apparent relationship between artwork and the collision shape even when the underlying rule is unchanged.

Vite distinguishes imported assets, whose URLs are processed by the build, from files in its public directory, which are copied to the root of dist as-is.[11] The Phaser Vite template supports both: an image can be imported at the top of a scene file, or a static file can be placed in public/assets and loaded with a path such as 'assets/bg.png', which npm run build copies to dist/assets.[6] Phaser's loader also has this.load.setPath(), which prefixes the filenames of files added afterwards.[74]

Watch the leading slash. Vite's own documentation says public files should be referenced with a root-absolute path such as /icon.png, and that base rewrites imported asset URLs, CSS url() references and HTML references at build time, but not URLs concatenated at runtime; for those it provides import.meta.env.BASE_URL.[11][20] A runtime string such as '/assets/bg.png' in a Phaser loader call is not rewritten. It works on the development server and then fails on a host that serves the game from a subpath, such as a GitHub Pages project site or itch.io.[19][63] Follow the selected template's convention and test the exported build, not only the development server.

For each asset, record its intended key, file path, dimensions, format, origin and permission for use. Test filenames on the deployed host, including letter case. Check sprite-sheet frame sizes and whether the final decoded audio and image files load. Large textures are a separate risk: MDN's WebGL guidance says effectively all systems support at least a 4096 maximum texture size and warns that a desktop's 16k texture support does not carry over to most other systems.[67] The broader production workflow is covered in Creating Game Assets with AI.

Audio

Add a visible start control and a mute option. Browser autoplay policies can block audible playback before user interaction, so sound beginning correctly after a deliberate click or tap is a separate acceptance check.[12] MDN's Web Audio guidance summarizes the policy as "Create or resume context from inside a user gesture": an AudioContext created outside a gesture starts in the suspended state and needs resume() after user interaction.[57] Phaser's documentation says most browsers will not play sound until the user clicks or taps, that Chrome logs a warning about the AudioContext when a Web Audio game boots, that this is normal, and that Phaser tries to resume the context after the first gesture; the sound manager's locked property shows whether audio is still blocked.[56] An assistant's claim that it called an audio function is insufficient evidence that the browser played anything.

Let the agent see and play the game

An agent that only edits files cannot see whether the canvas renders, whether a sprite is behind the HUD, or whether a click lands on the right button. The hub's Let the agent see and play the game section explains the general loop. For browser games the tools are mature, and the main choice is between an interactive browser the agent drives during a session and a test suite it runs.

ToolWhat the agent getsSetup for Claude Code (from the project's documentation)Canvas caveat
Playwright MCPBrowser automation through structured accessibility snapshots, plus screenshots, console messages, network requests, key presses and JavaScript evaluation[41]claude mcp add playwright npx @playwright/mcp@latest[41]Snapshot-based actions target accessible elements; coordinate tools such as browser_mouse_click_xy are opt-in via --caps=vision[41]
Playwright CLIThe same automation as shell commands with installable skills; Microsoft says CLI workflows suit coding agents because they are more token-efficient than MCP[42]npm install -g @playwright/cli@latest, then playwright-cli install --skills[42]Headless by default; pass --headed to open to watch[42]
Chrome DevTools MCPControl of a live Chrome with console messages (source-mapped), network inspection, screenshots, script evaluation, device emulation and performance traces[43][45]claude mcp add chrome-devtools --scope user npx chrome-devtools-mcp@latest[44]click_at(x, y) requires --experimentalVision[45][46]
Claude in ChromeClaude Code drives your own Chrome or Edge through an extension, reads console errors and DOM state, and records GIFs[47]claude --chrome, or enable it by default through /chrome; requires a direct Anthropic plan and extension 1.0.36 or later[47]Uses your real browser profile and logins
Playwright TestA repeatable test suite with screenshots, assertions and reports[35]npm init playwright@latest, then npx playwright test[35]Tests drive canvas games by coordinates or keys and read state you expose

Expanded article table

To pass server flags through Claude Code, put them after --, which Claude Code documents as the separator between its own options and the server's command and arguments.[34] An illustrative combination of the two documented forms is claude mcp add playwright -- npx @playwright/mcp@latest --caps=vision. Two privacy notes from the READMEs: Chrome DevTools MCP collects usage statistics by default (opt out with --no-usage-statistics), and its performance tools may send trace URLs to Google's CrUX API unless run with --no-performance-crux.[43] Playwright MCP blocks navigation to file:// URLs by default.[41]

Expose game state for checks

A canvas is one HTML element. Accessibility snapshots and role-based locators see a real Start or Restart button, but not a coin drawn inside the canvas.[16][41] The practical fix is a small, deliberate test interface: the game publishes a read-only summary of its state in development or test builds, and the agent or test reads it with JavaScript evaluation. Playwright's page.evaluate() runs a function in the page and returns the result, and page.waitForFunction() waits until a condition in the page becomes true.[37][75] Playwright MCP's browser_evaluate and Chrome DevTools MCP's evaluate_script do the same in an interactive session.[41][45]

An illustrative pattern (the global's name and fields are your own choice, not an API):

// In the game scene, after each update (development and test builds only)
window.__GAME__ = { scene: 'play', score: this.score, state: this.roundState };

Ask the agent to read window.__GAME__ before and after an action and to report both values with a screenshot. That turns "the token looks gone" into "score changed from 0 to 1 and the screenshot shows four tokens".

A first Playwright test

Playwright's webServer option starts the development server before the tests, and use.baseURL lets tests navigate with relative paths.[36] The following configuration and test are illustrative; the option names and methods are taken from Playwright's documentation, and the port matches the Phaser Vite template's default:[6][17][36][75][77]

// playwright.config.js (illustrative)
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  webServer: {
    command: 'npm run dev-nolog',
    url: 'http://localhost:8080',
    reuseExistingServer: !process.env.CI,
  },
  use: { baseURL: 'http://localhost:8080' },
  projects: [
    { name: 'desktop', use: { ...devices['Desktop Chrome'] } },
    { name: 'phone', use: { ...devices['iPhone 13'] } },
  ],
});
// tests/round.spec.js (illustrative)
import { test, expect } from '@playwright/test';

test('one pickup scores exactly once', async ({ page }) => {
  await page.goto('/');
  await page.getByRole('button', { name: 'Start' }).click();
  await page.waitForFunction(() => window.__GAME__?.state === 'playing');
  await page.keyboard.down('ArrowRight');
  await page.waitForFunction(() => window.__GAME__.score === 1);
  await page.keyboard.up('ArrowRight');
  const score = await page.evaluate(() => window.__GAME__.score);
  expect(score).toBe(1);
});

Playwright supports keyboard and pointer actions.[15] The Start button in this example is an HTML element; if the start control is drawn on the canvas, click by coordinates with page.mouse.click(x, y) instead.[39] For touch paths, touchscreen.tap(x, y) dispatches touchstart and touchend and throws unless the browser context has hasTouch enabled, which device profiles such as devices['iPhone 13'] set.[17][40] Playwright's device emulation sets viewport, user agent and touch support.[17] It remains an emulation, so it does not reproduce every characteristic of a physical phone.

Visual comparisons use await expect(page).toHaveScreenshot(). The first run writes a reference image; later runs compare against it, npx playwright test --update-snapshots updates the references, and maxDiffPixels and threshold tune the comparison. Playwright warns that rendering varies with host OS, version, hardware, power source and headless mode, so baselines should be generated in the environment that checks them.[76] Animated canvases rarely produce stable pixels. Playwright's clock API can control requestAnimationFrame, timers and performance along with Date, which can help freeze time-dependent behavior, but install must be called before any other clock-related calls.[38] Use screenshots for layout and HUD checks, and state assertions for rules.

Keep checks separate:

CheckWhat to verify
LoadExpected canvas or start screen appears; required resources succeed
PlayInput changes the intended gameplay state
End stateWin and failure follow the written rules
RestartA second and third round begin cleanly
ResizeHUD, world and input remain aligned
RegressionExisting accepted behavior survives the new change

Expanded article table

Use actual state assertions or a fixed scenario instead of taking an arbitrary screenshot after a long timeout. Test on the intended devices before release. Broader release testing, accessibility checks and playtest records are covered in Testing and Releasing AI-Assisted Games.

Academic evidence also supports checking integration directly. A 2026 exploratory study examined six tasks in one Python/Pygame game: three localized refactorings worked functionally, while only one of three gameplay additions integrated correctly. This is a single case involving GPT-4o, not a browser-framework benchmark or a ranking of current assistants.[18] It illustrates why a changed file or passing narrow test cannot establish that an entire game feature works.

Debug from browser evidence

Capture the first useful error, the steps that reproduce it and the expected result. Give the assistant that information before asking for a patch.

SymptomEvidence to collectBounded request
Blank game areaConsole exception, canvas size and whether startup ranFix the first startup failure without replacing the game
Missing image or soundRequest URL, HTTP status, response type and loader keyCorrect the failed resource path and retest the build
Pickup looks correct but does not countPlayer and token positions, round state and collision setupInspect the rule and confirm one increment per pickup
Touch activates the wrong placeViewport, canvas bounds and input coordinatesCorrect coordinate mapping without changing world rules
Restart duplicates behaviorFirst-round and later-round observationsFind persistent state or duplicate registration
Hosted build fails while development worksExported files and failed deployed requestsFix the deployment-specific difference

Expanded article table

The browser Network panel can show resource requests, response status, headers and sizes.[13] Use those observations to distinguish a missing file from a rendering failure. Avoid adding repeated retries to conceal a consistently wrong asset path. An agent connected through Chrome DevTools MCP or Playwright MCP can read the same console and network data itself; ask it to quote the failing request and status rather than summarize.[41][45]

Check performance and rendering support

For performance, first reproduce the slowdown on the intended device and scene. MDN's Canvas guidance discusses measures such as pre-rendering repeated drawing to an offscreen canvas, using integer coordinates and caching scaled images instead of scaling in drawImage() every frame.[14] These are possible interventions, not an instruction to apply every optimization. Compare the same scene before and after a change; preserve image quality and gameplay behavior while measuring.

Chrome DevTools MCP can record a performance trace (performance_start_trace) and emulate a slower device with a CPU throttling rate and network presets through emulate, so an agent can measure rather than guess.[45] A throttled desktop is still not a phone.

Check rendering support explicitly:

  • WebGL. MDN says a well-formed WebGL page should generate no errors other than OUT_OF_MEMORY and CONTEXT_LOST, and that extension availability and limits vary by client system.[67]
  • WebGPU. MDN lists the WebGPU API as limited availability (not Baseline) and available only in secure contexts (HTTPS).[68] Feature-detect with navigator.gpu and handle a missing adapter.
  • Three.js renderers. The three.js manual says WebGPURenderer uses WebGPU and falls back automatically to a WebGL 2 backend, that forceWebGL: true forces that backend for testing, that it initializes asynchronously (use setAnimationLoop() or await renderer.init()), and that it is still experimental; WebGLRenderer remains the recommended choice for pure WebGL 2 applications.[69] Custom ShaderMaterial code and EffectComposer passes do not carry over to WebGPURenderer, so do not let an agent switch renderers as a side effect of another task.[69]

Build and publish the exact artifact tested

For a Vite project with the documented scripts, npm run build produces the distribution files and npm run preview serves the result for local checking. Vite explicitly describes its preview server as a local preview facility, not a production server.[19] A particular template may use different script names, so check its manifest. The Phaser Vite template, as checked, defines no preview script;[6] Vite's CLI accepts -c, --config <file>, so npx vite preview --config vite/config.prod.mjs previews its build.[61]

Open the built result, complete a round and restart. Then deploy the distribution directory, not the development source tree. When hosting beneath a path such as /my-game/, configure the build's public base path and verify assets at that exact deployed URL. Vite documents how base affects asset URLs, and its base option accepts an absolute path, a full URL, or an empty string or ./ for embedded deployment.[20][60]

HostDocumented routeLimits and pitfalls (as checked 30 October 2026)
GitHub PagesVite's guide: set base to '/<REPO>/' for a project site, then in Settings, Pages, choose GitHub Actions as the source and use the provided workflow[19]Published sites may be no larger than 1 GB; deployments time out after 10 minutes; soft bandwidth limit of 100 GB per month; not to be used primarily for commercial transactions or SaaS[64]
Cloudflare PagesGit integration through the dashboard, or Direct Upload with npx wrangler pages deploy <BUILD_OUTPUT_DIRECTORY>[19][66]Free plan: up to 20,000 files per site; 25 MiB per file; dashboard drag and drop accepts up to 1,000 files[65][66]
itch.ioChoose "HTML Game", upload a ZIP whose entry point is index.html, or a single HTML file[63]ZIP: at most 1,000 files, 500 MB extracted, 200 MB per file, 240-character paths; filenames are case sensitive; absolute paths starting with / fail; requests for a missing path return 403 rather than 404; other domains must use HTTPS[63]

Expanded article table

itch.io embeds the game in an iframe and requires a click to start by default; it warns that audio may be muted in some browsers if auto-start is enabled. Projects marked Mobile Friendly launch in fullscreen on mobile devices regardless of the desktop embed setting.[63] Because a relative base and relative loader paths both matter there, the Phaser template's base: './' and 'assets/...' paths are a good fit, while a root-absolute loader path is not.[62][63]

A static host can serve the game files, but it does not supply an authoritative multiplayer server, private API credentials or persistent shared state. Add those services only when the design calls for them. Vite's VITE_ environment variables are exposed in client code during bundling; they must not contain private service keys.[21] If the game calls a paid model during play, use an appropriate server-side design and test limits and failure behavior separately.

Before sharing widely, ask someone who has not used the editor to load the deployed link, understand the controls, finish or fail, restart and report a problem. Record the release commit and retained build so a later edit can be compared with the version players actually received. Continue with Testing and Releasing AI-Assisted Games for broader release checks and platform disclosures.

Common problems and fixes

ProblemLikely causeFix
Blank page and a CORS error when index.html is opened directlyBrowsers block ES module scripts loaded from file:// URLs[59]Serve the project over HTTP (npm run dev, npx vite or npx serve .)[24][59]
Assets load in development but 404 after deploying to a GitHub Pages project siteRoot-absolute paths or base left at /[19][20]Set base for the subpath and use relative paths or import.meta.env.BASE_URL for runtime URLs[20]
Works locally, net::ERR_ABORTED 403 on itch.ioFilename case mismatch, an absolute path, or a request for a folder[63]Match case exactly, use relative paths and reference files, not folders[63]
No sound until the player clicks; console warns that the AudioContext was not allowed to startAutoplay policy[12][56]Start or resume audio from a click or tap; in Phaser, check this.sound.locked[56][57]
Fullscreen button does nothing on iPhoneFullscreen API is not available on iPhone Safari[55]Make the layout work without fullscreen on phones
Fullscreen fails inside an embedNot called from a user gesture, or the iframe lacks allowfullscreen[9][54]Call it from a pointerup handler; check the host's embed settings
Drag or swipe stops partway on a phoneThe browser takes over the gesture and fires pointercancel[53]Apply touch-action: none to the game surface when you handle pointers yourself[53]
Game runs faster on a 120 Hz or 144 Hz displayMovement per frame instead of per elapsed time[49]Scale by the frame delta or use a fixed simulation step[50][51]
Large movement jump or stuck key after switching tabsFrames stopped in the background; the gap is simulated at once[10][50]Pause on hidden, clear held input, and clamp the catch-up time[50][58]
Replays or multiplayer desynchronize between browsersFloating-point differences between engines, as in Battle Drones[70]Use a fixed step and deterministic math for simulation-critical code; test across browsers
Assistant writes Phaser 3 code that fails in Phaser 4Training data predates Phaser 4's renderer, filter and Canvas changes[31]Pin the version in the instructions file and load the migration skill[31][33]
Assistant produces a three.min.js r128 script tagOutdated CDN pattern[32]Use npm and Vite, or an import map with one version for library and addons[24][32]
Three.js scene looks stretched or blurry after resizeCamera aspect or drawing-buffer size not updated[23]Update camera.aspect and call renderer.setSize(width, height, false) from the canvas size[23]
Agent cannot click anything in the gameAccessibility snapshots do not see objects drawn on a canvas[41]Expose game state for checks, enable coordinate tools (--caps=vision or --experimentalVision), or add HTML controls[41][46]
A private API key appears in the shipped bundleIt was put in a VITE_ variable[21]Move the call to a server or function; rotate the key

Expanded article table

Real-world examples

These are first-person accounts. Their times, outcomes and metrics are self-reported.

  • Pieter Levels, fly.pieter.com. On 22 February 2025 Levels wrote that he had made his first game, a flight simulator, "100% with Cursor" in about three hours, that the whole simulator was one HTML file, and that he sometimes had to go back to previous versions and repeat a request to fix a problem.[72] See Cursor and vibe coding.
  • Simon Willison, Battle Drones. Willison described 12 major iterations and 321 commits by coding agents, mainly Claude Code. Early versions used Phaser; he later asked Claude Opus 5.5 to "Port this game to three.js", and the 3D models were constructed by Claude in TypeScript code. He wrote that static HTML and JavaScript is "a fantastic medium" for game development, that the game works offline as a Progressive Web App, and that he reviewed almost none of the code. Agent-written release notes cited 1,200 AI-versus-AI test matches; those figures are not independently audited. He later added online multiplayer through a small WebSockets relay on Fly.io, estimated at no more than $2.19 a month.[70]
  • Simon Willison, other browser games. On 28 October 2026 Willison wrote that all of his games so far were HTML and JavaScript on static hosting, generally GitHub Pages or Cloudflare Pages, and that models "already know libraries like three.js and Phaser". He reported building one, Tree Appreciator, in the regular Claude chat interface with a single prompt and no follow-up, and suggested starting with "Build a mobile-friendly three.js game where..." and switching to Codex or Claude Code once a project passes the proof-of-concept phase. He also said no project represented more than four hours of his own work, plus many more hours of Claude Code running by itself.[71]

The hub's developer examples section covers these and other projects, and its costs and usage limits section collects usage-limit observations.

Continue reading

More from See also

More to read

Topics:AI AgentsAI Code GenerationDeveloper ToolsSoftware Development

References

  1. ^1 ^2Phaser. What is Phaser?. Official documentation. Accessed 30 October 2026.
  2. ^1 ^2Three.js maintainers. Three.js README. Official repository. Accessed 30 October 2026.
  3. ^Phaser. Making your first Phaser Game. Official Phaser 3 tutorial. Accessed 30 October 2026.
  4. ^1 ^2Phaser. Installing. Official documentation. Accessed 30 October 2026.
  5. ^npm. npm ci. Official CLI documentation. Accessed 30 October 2026.
  6. ^1 ^2 ^3 ^4 ^5 ^6Phaser. Phaser Vite Template. Official template README. Accessed 30 October 2026.
  7. ^1 ^2Phaser. Scenes. Official documentation. Accessed 30 October 2026.
  8. ^Phaser. Input. Official documentation. Accessed 30 October 2026.
  9. ^1 ^2 ^3 ^4Phaser. Scale Manager. Official documentation. Accessed 30 October 2026.
  10. ^1 ^2MDN. Page Visibility API. Accessed 30 October 2026.
  11. ^1 ^2Vite. Static Asset Handling. Official documentation. Accessed 30 October 2026.
  12. ^1 ^2MDN. Autoplay guide for media and Web Audio APIs. Accessed 30 October 2026.
  13. ^Google. Inspect network activity. Chrome DevTools documentation. Accessed 30 October 2026.
  14. ^MDN. Optimizing canvas. Accessed 30 October 2026.
  15. ^Microsoft. Actions. Playwright documentation. Accessed 30 October 2026.
  16. ^Microsoft. Locators. Playwright documentation. Accessed 30 October 2026.
  17. ^1 ^2 ^3Microsoft. Emulation. Playwright documentation. Accessed 30 October 2026.
  18. ^Jan Wunderlich, Markus Kleffmann and Sebastian Lempert. An Exploratory Case Study of LLM-Assisted Refactoring and Gameplay Feature Generation in an Endless Runner Game. arXiv:2606.21171, 19 June 2026. Preprint.
  19. ^1 ^2 ^3 ^4 ^5Vite. Deploying a Static Site. Official documentation. Accessed 30 October 2026.
  20. ^1 ^2 ^3 ^4Vite. Building for Production. Official documentation. Accessed 30 October 2026.
  21. ^1 ^2Vite. Env Variables and Modes. Official documentation. Accessed 30 October 2026.
  22. ^Three.js. Making a Game. Official manual. Accessed 30 October 2026.
  23. ^1 ^2 ^3Three.js. Responsive Design. Official manual. Accessed 30 October 2026.
  24. ^1 ^2 ^3 ^4 ^5 ^6 ^7Three.js. Installation. Official manual. Accessed 30 October 2026.
  25. ^1 ^2Phaser. Using the Create Phaser Game app. Official tutorial (2 April 2024, page as served 30 October 2026). Accessed 30 October 2026.
  26. ^1 ^2npm. phaser. Package registry entry (latest 4.2.1, published 9 July 2026; 4.0.0 published 10 April 2026). Accessed 30 October 2026.
  27. ^npm. three. Package registry entry (latest 0.186.1, published 24 September 2026). Accessed 30 October 2026.
  28. ^Phaser Studio. Phaser 4 vs Phaser AE: What's the Difference and Which One Is for You. Phaser news, 23 July 2026.
  29. ^Ryan Merket. Phaser's Richard Davey turns a decade-old game framework into an AI game agent. RuntimeWire, 1 July 2026.
  30. ^1 ^2Phaser. llms.txt. Agent index for phaser.io, including the docs MCP server. Accessed 30 October 2026.
  31. ^1 ^2 ^3 ^4 ^5Phaser Studio. v3-to-v4-migration skill. `skills/` directory of the phaser 4.2.1 npm package (28 SKILL.md files; also present in 4.0.0 and 4.1.0). Accessed 30 October 2026.
  32. ^1 ^2 ^3Three.js. llms.txt. Documentation index and instructions for large language models. Accessed 30 October 2026.
  33. ^1 ^2Anthropic. Extend Claude with skills. Claude Code documentation. Accessed 30 October 2026.
  34. ^1 ^2Anthropic. Connect Claude Code to tools via MCP. Claude Code documentation. Accessed 30 October 2026.
  35. ^1 ^2 ^3Microsoft. Installation. Playwright documentation. Accessed 30 October 2026.
  36. ^1 ^2Microsoft. Web server. Playwright documentation. Accessed 30 October 2026.
  37. ^Microsoft. Evaluating JavaScript. Playwright documentation. Accessed 30 October 2026.
  38. ^Microsoft. Clock. Playwright documentation. Accessed 30 October 2026.
  39. ^Microsoft. Mouse. Playwright API reference. Accessed 30 October 2026.
  40. ^Microsoft. Touchscreen. Playwright API reference. Accessed 30 October 2026.
  41. ^1 ^2 ^3 ^4 ^5 ^6 ^7 ^8 ^9Microsoft. Playwright MCP. GitHub README (npm @playwright/mcp 0.0.83, published 28 September 2026). Accessed 30 October 2026.
  42. ^1 ^2 ^3Microsoft. playwright-cli. GitHub README. Accessed 30 October 2026.
  43. ^1 ^2Chrome DevTools team. Chrome DevTools for agents (chrome-devtools-mcp). GitHub README (npm chrome-devtools-mcp 1.12.0, published 22 October 2026). Accessed 30 October 2026.
  44. ^Chrome DevTools team. Client configurations. chrome-devtools-mcp documentation. Accessed 30 October 2026.
  45. ^1 ^2 ^3 ^4 ^5Chrome DevTools team. Tool reference. chrome-devtools-mcp documentation. Accessed 30 October 2026.
  46. ^1 ^2Chrome DevTools team. Configuration. chrome-devtools-mcp documentation. Accessed 30 October 2026.
  47. ^1 ^2Anthropic. Use Claude Code with Chrome. Claude Code documentation. Accessed 30 October 2026.
  48. ^Anthropic. Best practices for Claude Code. Claude Code documentation. Accessed 30 October 2026.
  49. ^1 ^2MDN. Window: requestAnimationFrame() method. Accessed 30 October 2026.
  50. ^1 ^2 ^3 ^4 ^5MDN. Anatomy of a video game. Accessed 30 October 2026.
  51. ^1 ^2Glenn Fiedler. Fix Your Timestep!. Gaffer On Games, 10 June 2004.
  52. ^MDN. Pointer events. Accessed 30 October 2026.
  53. ^1 ^2 ^3MDN. touch-action. Accessed 30 October 2026.
  54. ^1 ^2MDN. Element: requestFullscreen() method. Accessed 30 October 2026.
  55. ^1 ^2MDN. browser-compat-data: api/Element.json. requestFullscreen support entry for Safari on iOS. Accessed 30 October 2026.
  56. ^1 ^2 ^3 ^4Phaser. Audio. Official documentation. Accessed 30 October 2026.
  57. ^1 ^2MDN. Web Audio API best practices. Accessed 30 October 2026.
  58. ^1 ^2Phaser. Game. Official documentation (pause, resume, hidden and visible events). Accessed 30 October 2026.
  59. ^1 ^2MDN. JavaScript modules. Accessed 30 October 2026.
  60. ^Vite. Shared Options. Official documentation (`base`). Accessed 30 October 2026.
  61. ^Vite. Command Line Interface. Official documentation. Accessed 30 October 2026.
  62. ^1 ^2Phaser. template-vite: vite/config.prod.mjs. Official template configuration. Accessed 30 October 2026.
  63. ^1 ^2 ^3 ^4 ^5 ^6 ^7itch.io. Uploading HTML5 games. Creator documentation. Accessed 30 October 2026.
  64. ^GitHub. GitHub Pages limits. GitHub Docs. Accessed 30 October 2026.
  65. ^Cloudflare. Limits. Cloudflare Pages documentation. Accessed 30 October 2026.
  66. ^1 ^2Cloudflare. Direct Upload. Cloudflare Pages documentation. Accessed 30 October 2026.
  67. ^1 ^2MDN. WebGL best practices. Accessed 30 October 2026.
  68. ^MDN. WebGPU API. Accessed 30 October 2026.
  69. ^1 ^2Three.js. WebGPURenderer. Official manual. Accessed 30 October 2026.
  70. ^1 ^2 ^3Simon Willison. Battle Drones: a vibe coded RTS with audio-based multiplayer. Simon Willison's Weblog, 20 October 2026.
  71. ^Simon Willison. The October inflection point for vibe game development. Simon Willison's Weblog, 28 October 2026.
  72. ^Pieter Levels (@levelsio). Post announcing fly.pieter.com. X, 22 February 2025.
  73. ^1 ^2Vite. Getting Started. Official documentation. Accessed 30 October 2026.
  74. ^Phaser. Loader. Official documentation. Accessed 30 October 2026.
  75. ^1 ^2Microsoft. Page. Playwright API reference (`page.waitForFunction()`). Accessed 30 October 2026.
  76. ^Microsoft. Visual comparisons. Playwright documentation. Accessed 30 October 2026.
  77. ^Microsoft. Keyboard. Playwright API reference. Accessed 30 October 2026.

Improve this article

Add missing citations, update stale details, or write a clearer explanation. Anyone can edit: changes go live when you publish, and every edit is checked by an editor.

3 revisions · v3 · 7,766 words · full history

Fact-checks are independent of edits: a reviewer re-verifies the article against its sources and stamps the date. How we verify

Research and drafting on this wiki are AI-assisted, under named human editorial standards. How AI is used here

Reviewer note: xg28 phase 3 independent verification (~96 sources, ~175 claims incl. every install command, CLI flag, MCP tool name and version string); 4 minor fixes applied pre-publish

Cite this page: AI Wiki. "Developing Browser Games with AI." aiwiki.ai, updated 30 Oct 2026, fact-checked 30 Oct 2026. CC BY 4.0. https://aiwiki.ai/wiki/developing_browser_games_with_ai

Edit article