Use when Codex is building or iterating on a web game (HTML/JS) and needs a reliable development + testing loop: implement small changes, run a…
Iterative web game development with Playwright-based testing, screenshot validation, and deterministic time-stepping.
Provides a structured workflow: implement small changes, run automated Playwright tests with controlled input bursts and frame pauses, capture screenshots and text state, then inspect and adjust
Requires window.render_game_to_text() to expose game state as JSON and window.advanceTime(ms) for deterministic frame stepping during automated tests
Includes a Playwright client script that executes action payloads (keyboard, mouse, frame counts) and buffers console errors for debugging
Enforces a test checklist covering movement, interactions, win/lose transitions, collisions, menus, and any request-specific features; screenshots are the source of truth for visual verification
Develop Web Game
Build games in small steps and validate every change. Treat each iteration as: implement → act → pause → observe → adjust.
Skill paths (set once)
export CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
export WEB_GAME_CLIENT="$CODEX_HOME/skills/develop-web-game/scripts/web_game_playwright_client.js"
export WEB_GAME_ACTIONS="$CODEX_HOME/skills/develop-web-game/references/action_payloads.json"
User-scoped skills install under $CODEX_HOME/skills (default: ~/.codex/skills).
Workflowdon't have the plugin yet? install it then click "run inline in claude" again.
formalized game code requirements (render_game_to_text and advanceTime as mandatory exports), added 8 explicit decision points covering server downtime, missing functions, malformed payloads, infinite loops, console errors, and state mismatches, expanded output contract with concrete json schema and errors.log format, added 6-point outcome signal checklist for validation.
use this skill when building or iterating on a web-based game (html/js) and need a tight feedback loop: write code, run automated tests via playwright, capture screenshots and game state, inspect results, adjust. the skill enforces deterministic testing (controlled input timing, frame pauses) and visual validation so you catch regressions and unintended behavior before shipping. run this every time you change game logic, collision detection, win/lose conditions, or ui.
playwright environment:
npm install playwright)$WEB_GAME_CLIENT (default: ~/.codex/skills/develop-web-game/scripts/web_game_playwright_client.js)$WEB_GAME_ACTIONS (default: ~/.codex/skills/develop-web-game/references/action_payloads.json)game code requirements:
window.render_game_to_text() function exported on the global window object. must return a json object representing current game state (player position, enemy positions, score, menu state, etc.)window.advanceTime(ms) function exported on the global window object. must step the game simulation forward by the given milliseconds without rendering to screen. used for deterministic frame-by-frame testing.external connections:
http://localhost:8000/game.html)environment setup:
export CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
export WEB_GAME_CLIENT="$CODEX_HOME/skills/develop-web-game/scripts/web_game_playwright_client.js"
export WEB_GAME_ACTIONS="$CODEX_HOME/skills/develop-web-game/references/action_payloads.json"
start the dev server: run your http server on localhost (e.g., python -m http.server 8000) and confirm the game loads at http://localhost:8000/game.html with no console errors.
write or update game code: make a single focused change (e.g., add jump mechanic, fix collision box, adjust menu text). keep changes atomic so regressions are easy to isolate.
define test actions: create or update an action payload (json array) describing the inputs to send to the game during the test. each action includes keyboard/mouse events, frame-advance counts, and pause durations. reference $WEB_GAME_ACTIONS for syntax.
invoke playwright client: run the web_game_playwright_client.js script with the game url, action payload, and output directory.
http://localhost:8000/game.html), action payload json, output dir (e.g., /tmp/game-test-01)node $WEB_GAME_CLIENT --url http://localhost:8000/game.html --actions '[{"type":"keypress","key":"ArrowUp","count":5},{"type":"frameAdvance","ms":100}]' --output /tmp/game-test-01capture state and screenshots: after each action or action group, the playwright client calls window.render_game_to_text() and takes a png screenshot of the viewport.
state-00.json, state-01.json, etc.) and png screenshots (screenshot-00.png, screenshot-01.png, etc.) written to output directorybuffer and log errors: the playwright client listens to console.error, console.warn, and uncaught exceptions during the test. errors are buffered and written to a errors.log file in the output directory.
errors.log containing all errors and warnings from the test runinspect results: review the screenshots in order (visual progression of the game) and cross-reference state json files to verify that game logic executed correctly. check the errors.log for any runtime issues.
run test checklist: verify at minimum: player movement (up/down/left/right), interactions (jumping, attacking, picking up items), win/lose transitions (game ends, score updates), collision detection (no clipping through walls), menu behavior (pause, restart), and any feature-specific requirements from the original request.
adjust and iterate: if any test fails or behavior looks wrong, go back to step 2, edit the game code, and re-run the playwright test. repeat until all checklist items pass.
if the game server is down or unreachable: playwright will timeout after 30s (default). check that the http server is running and the game url is correct before re-running the test. do not proceed to step 5 until the server responds.
if window.render_game_to_text() is missing or returns invalid json: the playwright client will log an error and skip state capture for that frame. you must implement render_game_to_text() before relying on state validation. screenshots will still be captured, but you lose the structured game state for comparison.
if window.advanceTime() is missing: the playwright client will attempt to use browser frame timing (requestAnimationFrame) as a fallback, but deterministic frame-stepping is lost. results become non-repeatable and hard to debug. implement advanceTime() before running regression tests on complex game logic.
if the action payload is malformed or references non-existent keys: playwright will throw a syntax error and abort the test run. validate your action json against $WEB_GAME_ACTIONS schema before invoking the client.
if a test run generates >100 screenshots or takes >60 seconds: the game loop may be in an infinite state (e.g., waiting for input that never arrives). check for blocking modals or game state that prevents progress. review the last screenshot and state json to identify the hang.
if console.error or uncaught exceptions fill errors.log: there is a runtime bug in your game code or a missing asset. fix the error in game code (step 2) and re-run the test. do not ship code with unhandled exceptions.
if screenshots look correct but state json does not match expected values: there is a mismatch between visual rendering and game logic state. this usually means render_game_to_text() is returning stale or incorrect data. validate your render function and ensure it reads directly from game state variables, not cached values.
directory structure after test run:
/tmp/game-test-01/
├── screenshot-00.png
├── screenshot-01.png
├── ... (one png per captured frame)
├── state-00.json
├── state-01.json
├── ... (one json per captured state)
├── errors.log
└── test-summary.txt (optional, playwright client may generate)
state json format (example):
{
"playerX": 256,
"playerY": 384,
"playerVelocityX": 0,
"playerVelocityY": 0,
"enemies": [
{"id": 1, "x": 100, "y": 200, "health": 3}
],
"score": 1250,
"gameState": "playing",
"menuVisible": false
}
screenshot naming: screenshot-NN.png where NN is zero-padded frame counter (00, 01, 02, ...). screenshots are png format, 1024x768 (or configured viewport size), and capture the full game canvas at the moment render_game_to_text() was called.
errors.log format: plain text, one error per line. each line includes timestamp, severity (error/warn), and message. example:
[2025-01-15 14:23:45.123] ERROR: TypeError: cannot read property 'x' of undefined (game.js:45)
[2025-01-15 14:23:46.456] WARN: asset 'sprite-enemy.png' not found
you know the skill worked when:
all test actions executed without playwright timeout or crash. check that the playwright process exited cleanly (exit code 0) and did not throw an unhandled exception.
screenshot sequence shows expected visual changes. stepping through screenshots in order (00, 01, 02, ...) should tell a coherent story of the game responding to your test inputs (e.g., player moving, enemies animating, score changing).
state json files match your expectations. for example, if you pressed "ArrowUp" 5 times, the state json at the last frame should show playerY decreased by the expected amount. state values should be deterministic and repeatable across test runs.
errors.log is empty or contains only expected warnings. no runtime exceptions, no undefined reference errors, no asset 404s. if you expect a specific warning (e.g., "sound not loaded"), confirm it appears exactly as written.
checklist criteria all show pass. movement works in all directions, collisions prevent clipping, win/lose transitions trigger correctly, menu responds to input, and any feature-specific behavior matches the request.
code review confirms the change is minimal and focused. if you added 200 lines to fix a jump bug, something went wrong. good iterations are <50 lines of code change per test cycle.
if any of these signals fail, go back to the "decision points" or "procedure" section, identify which step broke, fix it, and re-run. repeat until all signals pass. you can then commit your code and move on to the next feature.