Build a game with Claude Code and debuff
This walkthrough takes a new debuff project from an empty folder to a change that's been tuned, reproduced and verified, with Claude Code doing the implementation and you making the calls. It uses the basic template, where buff, the debuff mascot, slides left and right and pops. The same loop works in any debuff game.
You need Node.js and Claude Code. Any MCP client works; the commands below are what Claude Code shows.
1. Set up
npm create debuff@latest my-game
cd my-game
npm installThe new project already has what the agent needs:
.mcp.jsonstarts the debuff MCP server,@debuffjp/mcp.AGENTS.mdlists the commands, the layout, the work loop and the rules.CLAUDE.mdpoints to it.scenarios/move-and-pop.jsonis a first test, with screenshot baselines.
Start the dev server and open the URL it prints:
npm run devThen start Claude Code in the same folder:
claudeClaude Code asks once whether to trust the project's MCP server. Approve it, and /mcp should list debuff as connected.
2. Live or headless
Ask the agent to look at the game. It calls game_start, which picks a mode for you:
- Live, when your browser has the game open under
npm run dev. The agent attaches to your tab and pauses it. The dev overlay (toggle it with the backtick key) shows an agent badge, and its log tab lists everything the agent does, each with an undo.game_stopresumes your game and disconnects. - Headless, when nothing is open, or when the agent asks for
mode: "headless". It gets a private copy in a headless browser, and time moves only when it callsgame_step. That makes it good for checks that must come out the same every time.
Use live when you want to watch and steer, and headless for unattended work like fuzzing or checking a fix.
3. Flag a moment and have the agent reproduce it
Play for a bit. When something looks or feels wrong, open the overlay's log tab, type into the field ("flag this moment for the agent…"), and press Enter:
buff overshoots when I let go of the arrow key
Then tell the agent to look at your notes. It calls game_notes and gets your words, the tick, the scene's inspect() state at that tick, and the frames before it. You don't need to explain where buff was or what you pressed.
Next, ask it to reproduce the moment. It calls game_replay with the note's tick:
{ "file": ".debuff/replays/replay-tick-412.json", "ticks": 412, "steps": 37, "warnings": [] }That file is a scenario containing every key, pointer and param change since the page loaded. debuff uses fixed steps and a seeded RNG, so running it replays your session exactly:
npx debuff scenario .debuff/replays/replay-tick-412.jsonNow the agent has a reproduction it can run as often as it likes, and so do you. warnings lists anything the replay couldn't capture, such as slow motion. You can also save or copy a replay yourself from the same log tab.
4. Tune
Overshoot is a feel problem, and feel lives in params. The template declares buff's movement like this:
const tuning = game.params.define("player", {
speed: { type: "number", default: 140, min: 0, max: 600, units: "px/s" },
accel: { type: "number", default: 1200, min: 0, max: 5000, units: "px/s²" },
});The agent reads these with game_params and changes them with game_set_param, which rejects values outside the range. The quickest way in is the tune-feel prompt, which Claude Code shows as a slash command:
/mcp__debuff__tune-feel stop faster when I let goThe prompt tells the agent to propose two or three variants, each a small set of param changes with a reason, and to wait for your go-ahead before trying one. It applies a variant in your tab, plays a few seconds and captures a frame. You try it, undo it from the overlay if you don't like it, and pick. Only then does it write the chosen defaults into game.params.define(...).
You can do the same by hand in the overlay's tune tab, which has a slider for every param.
5. Verify with scenarios and fuzz
A change isn't done until it's been checked. Two commands do that, and the agent runs both because AGENTS.md tells it to.
Scenarios. npm test runs scenarios/*.json headless, checks each expect against inspect() state, and compares captures with their baselines:
npm testThe replay from step 3 can become a regression test: ask the agent to copy it into scenarios/ and add an expect on the state that was wrong, such as buff's vx a few ticks after release. If a capture changes on purpose, the agent should show you the diff image and ask before accepting it with npx debuff scenario --update.
Fuzz. npx debuff fuzz plays the game with random but plausible input for simulated minutes:
npx debuff fuzzEach crash, stuck state or back-and-forth loop is saved as a scenario in .debuff/fuzz/ that replays it exactly. To fix a finding, the agent replays that file with npx debuff scenario <file>, fixes the cause, and replays it again until it passes. --seed repeats a run, and the command exits non-zero when it finds something, so you can run it in CI.
If a runtime error shows up along the way, game_errors explains it. kind says whose problem it is (your-code, debuff-usage, debuff-bug or outside), and where gives the source line. The fix-errors prompt walks the agent through fixing and re-checking.
6. Creative calls stay with you
The agent does the implementing, reproducing, tuning runs and testing. You decide what the game should be. That split is written into the tools:
AGENTS.mdand every MCP prompt tell the agent that art, feel, difficulty, story and names are yours, and to propose two or three options instead of deciding.- Param changes in live mode show up in your overlay with an undo.
- Screenshot baselines change only with your approval.
In practice that means the agent comes back with "here are three ways to make the stop snappier, with captures" rather than a finished decision. You still play it, and you still choose.
Next
- Working with AI agents: the interfaces in one table.
- Testing with scenarios: the scenario format in full.
- Tunable params: types, ranges and the tune tab.
- How debuff compares with other engines for coding agents.