Thoughts · · 12 min read

Agentic Speed, Human Control in Game Dev

Can I Keep Up With My Own Agent?

Everyone can one-shot a game now. I wanted to know if you can keep working on it afterwards. My hypothesis: keep everything in plain text (KDL, CSS, scripts), give each kind of change one home, and build the vocabulary before the content. Then I tested it with one prompt, a round of fresh-agent edits, and a full retheme.

gamedevkdlcssstorybookagents

My feed is a steady stream of “AAA game in one prompt” posts. A dimension-hopping cyberpunk drifter. A downhill BMX racer “above what a AAA studio would ship.” Plenty of them open with “I’ve never made a game before.”

Jaxum's title screen over a rainy neon-lit cyberpunk street, with New Game and chapter controls.Jaxum

“build me a AAA 3d game about a dimension hopping cyberpunk drifter”

mnm9678's prompt and one-shot claim · Play
Descent's race countdown: outlined BMX riders at a mountain starting gate, with position, speed, and route profile on screen.Descent

“at a level of craft above what a AAA studio would ship.”

Vyom's BMX prompt, archived by SamePrompt · Play
Actual screenshots captured from the linked demos on September 29, 2026: Jaxum's title scene and Descent's starting grid. Quotes are excerpts from their prompts. The one-prompt build histories are creator-reported; these captures show the live demos as they exist today.

Then there’s Claude of Duty, whose prompt opens with “I want you to build a first-person shooter at the level of the most recent Call of Duty games.”

These posts on their own are simply incredible. But after the hundredth vibecoded example I stopped being impressed and started wondering what these people were left with. Is the game fun? Could they ship it? Could they iterate? Respond to user feedback?

It seems the answer was mostly “no” for pretty obvious reasons, but I really wanted to see if I could “one-shot” a game in such a way that the agent would be free to do what it does best, write a lot of code, and I would be left with a project I could confidently iterate on.

I’ve been a game dev for over two decades; so I have some ideas on how we can achieve this for casual web based games.

I put those ideas to the test and tried one shotting a game in as few prompts as possible. In about 3 hours I was playing this on my phone:

GRAVEWAKE on my phone: the banshee wave, then the Lich. Thirty seconds, video only.

That’s “GRAVEWAKE”. A surprisingly fun twin stick shooter for mobile. Here’s how it was made.

My hypothesis

Here was my thinking going into this: I don’t want to work through a game engine GUI or depend on a half-working MCP server. Everything should stay in plain text. PixiJS is the rendering layer, and TypeScript handles the game logic.

But the actual theory is this: I should rarely need to read the generated TypeScript myself. I don’t want the code to become the interface I use to make changes. Agents are already very good at programming, and I’m happy to leave that domain to them.

Fast iteration with agents does not come from being intimately familiar with the codebase. It comes from building a strong bridge between the human and the agent, where both sides communicate using the same language, formally and unambiguously.

To test this I instructed my agents to not start by writing code, but to instead create the words, concepts, and vocabulary that define the game.

KDL and CSS

The vocabulary is written in two plain-text languages that work as one. KDL says what things are. CSS says how they look. They refer to each other by name. Here are three of GRAVEWAKE’s enemies in KDL:

enemy "ghoul" name="Ghoul" hp=4 r=0.55 score=25 model="ghoul" blurb="Crouches, rears back, and throws itself down a line. Step aside." {
dash 0.7 17
}
enemy "necromancer" name="Necromancer" hp=6 r=0.55 score=60 model="necromancer" blurb="Keeps its distance and knits the dead back together." {
keep-away 9 3.2
heal 5 3
}
enemy "lich" name="The Lich" tier="boss" hp=260 r=2 score=2000 model="lich" spawns="banshee" blurb="A dead king with six blades at its back, and a spiral you have to thread." {
keep-away 7 1.4
spiral 0.18 4
burst 3.2 9 80
summon 7 3
}

You don’t need a game dev degree to read this. Every enemy is a few properties plus a handful of behaviour words the agent created.

keep-away and spiral are not reserved words in KDL. The game code defines each word exactly once, as a small pure function in one registry. The Lich, the first boss, has no special code at all. It’s four words that already existed, stacked.

Here’s one of those words, exactly as the agent wrote it:

/** Hold `range` u from the ship, strafing sideways while in the band; `speed` u/s. */
'keep-away': (range, speed) => ({
mem: (r) => [r() < 0.5 ? 1 : -1],
moves: true,
tick: (m, w, e, dt) => {
const [ux, uy, d] = toward(e, w.s.player.x, w.s.player.y);
const radial = d > range + 1 ? 1 : d < range - 1 ? -1 : 0, side = m[0]! * 0.6;
steer(e, (ux * radial - uy * side) * speed, (uy * radial + ux * side) * speed, dt);
if (Math.abs(e.x) > T.ARENA_W_U / 2 - 1.5 || Math.abs(e.y) > T.ARENA_H_U / 2 - 1.5) m[0] = -m[0]!; // turn back at the walls
},
}),

Ten lines, one job, and the KDL arguments map straight onto it: keep-away 9 3.2 holds 9 units from you, moving at 3.2 units a second.

This is where I think agents really accelerate game dev. An agent can reason “I need five new words to build this feature,” write five small functions very quickly, and each one is easy to get right because each one is simple. The feature I ultimately asked for ends up being a composition of these words in KDL.

Now the look. Every enemy id in the KDL has a matching CSS token, and every color in GRAVEWAKE, down to each enemy’s body in the 3D clearing, lives in that one palette:

--enemy-ghoul: #8b9173;
--enemy-necromancer: #5d5869;
--enemy-lich: #9490ad;
--soulfire: #5cc8ff;

The render numbers that usually hide at the top of a shader file live there too:

--look-camera-tilt-rad: 0.62;
--look-exposure: 1.35;
--look-moon-key: 2.2;
--look-wizard-scale: 1.5;

Want a brighter moon or a bigger wizard? That’s an edit I can make myself, at human speed.

Here are five of GRAVEWAKE’s real models with their real KDL and palette:

The Lichboss · keep-away · spiral · burst · summon
Edit either source, then apply. Try a word the game doesn't know.
GRAVEWAKE's real models, loaded on demand: each one a Blender script in the repo, with four paint slots and three clips. The KDL picks the model and must use the game's words; the CSS colours the body, cloth, trim and glow slots, and a wave's tint overrides the body. The game's own moonlit shader is simplified here.

One more nice thing about CSS: it animates. GRAVEWAKE’s screens are KDL too, and every wave opens with a banner:

panel class="banner" if="banner" key=(bind)"bannerKey" {
text class="banner-kicker" bind="bannerKicker"
text class="banner-title" bind="banner"
}

Its whole animation is a @keyframes rule:

.banner { animation: banner-in 2.4s ease-out forwards }
@keyframes banner-in {
0% { opacity: 0; scale: 1.6 }
12% { opacity: 1; scale: 1 }
80% { opacity: 1 }
100% { opacity: 0; translate: 0 -10px }
}

This isn’t a browser. The game draws with Pixi, and a small CSS engine in the repo reads these files.

Storybook for the eyes

Storybook is where the agent and I look at the same thing. Every situation I care about is a named story that mounts the real renderer: the Lich’s spiral filling the screen, a Deadeye’s line locked on the wizard a beat before it fires. I nitpick them by eye. The agent runs each story’s play function, which asserts the move actually happens, to check its own work.

GRAVEWAKE in Storybook: the Enemies section lists all fourteen of the dead, and the Lich story shows the boss filling the clearing with a spiral of pale spells around the wizard.
The Enemies/Lich story: the real game, one enemy, one situation. Its play function fails unless the Lich has more than twenty spells in the air.

Prompting

It started as PRISMFALL

I baked all my directions on KDL and CSS and Storybook into a skill I call the game-bible (hoping my agents would take it seriously) and opened up a fresh claude code instance in an empty folder and typed one prompt:

I want a AAA twin stick shooter for mobile devices with amazing graphics and a diverse enemy set. Use the game-bible skill for the architecture.

About forty minutes later it handed back PRISMFALL: admittedly not a AAA game at all but instead a bloom-soaked neon arena with thirteen enemies, two bosses, ten waves, upgrades and synthesized audio. 182 unit tests and 23 Storybook tests pass.

The part I cared about was the commit log. The first commit doesn’t contain a single line of game:

82f8441 Foundation: copy the declarative UI engine, content loader and clock ...
5a34e05 Core: run state, the step rule, 12 behaviour words, 8 upgrade effects, content ...
00dd08e Shell: stores, actions, runtime, 3D arena, twin-stick input, synth audio, screens
dbee809 Foundation guards: vocabulary catalog with a drift test, screen-import guard, ...
94cc55d Stories: one per enemy, arena, shared prefabs. 23 stories pass headless

Engine, then words, then shell, then guards, then stories.

PRISMFALL in Storybook: the Enemies section lists all thirteen enemies, and the Bulwark story shows two shielded cubes facing the player's ship.
One story per enemy, each with a play function that asserts its signature move on the real game: the bulwark's shield blocks a frontal shot, the lancer telegraphs before it charges, the mender actually heals.

Three more requests

Then the real test: short requests, one at a time, each to a fresh agent that had never seen the project. It had the repo, the catalog and the skill, and nothing else.

“Make it an ice level”

Add an ice wave after wave 6: every enemy in it is icy blue, and it ends with a Colossus that’s twice as big as normal.

Waves had no way to recolor or resize an enemy, so the agent’s first commit was pure foundation: a palette tint for the wave and a scale for each spawn. Then the feature:

wave "ice" title="Hoarfrost" tint="--ice" {
spawn "skeleton" count=8 gap=0.4
spawn "banshee" count=3 gap=1 at=3
spawn "ghoul" count=2 gap=1.2 at=6
spawn "colossus" scale=2 at=12
}

It also told me the level was too hard. It ran the balance bot before and after, and the median run dropped from wave 10 to wave 7, because a full-health boss now shows up four waves early. It didn’t quietly nerf the boss to make its numbers look better. It said to lower its health if I want it beatable. That’s one number, and I know where it lives.

“Add a sniper”

New enemy: a sniper that hangs back at the edge of the arena, paints a laser on you for a second, then fires one very fast shot. Put a couple in the later waves.

The original PRISMFALL sniper story: two needle-shaped snipers at the right edge of the neon arena, both red lasers locked onto the player's ship.
The Enemies/Sniper story, captured from the commit that added it: both lasers locked onto the ship, a beat before they fire.

Nothing in the catalog could “paint, then fire,” so the agent built one new word, snipe. “Hang back at the edge” didn’t need anything new. In a 34-unit arena, keep-away 17 is against the wall. I was sure it would invent a new word lurk-at-edge or some equivalent. It didn’t.

enemy "deadeye" name="Deadeye" hp=5 r=0.6 score=70 model="archer" blurb="Draws at the treeline and paints you red. When the line freezes, move." {
keep-away 17 3
snipe 3.2 1 42
}

“Bulwarks aren’t fun”

Bulwarks are the top killer in the sim and they’re not fun. Make them fair.

The vaguest of the three. “Fair” isn’t a number, and the easy fix is to halve their health. Instead the agent went and found out why. The sim’s “killer” was just whichever enemy was nearest when you got hit, so it logged what actually did the damage. Bulwarks really were the problem, at 29 of 100 deaths, and every case was the same: a shield wall pushing the ship into a corner, front first.

enemy "bulwark" name="Bulwark" tier="elite" hp=22 r=0.95 score=90 model="cube" ... {
chase 1.6
keep-away 4 1.6
shield 120
}

It presses in, stops four units short, and you can still get round the back. Same health, same shield, same speed. Bulwark deaths went from 29 to 4 and the median run didn’t move.


When the game couldn’t say the thing, the agent grew the vocabulary first. When it could, the change was a few lines I can read in ten seconds.

Make it ‘AAA’

PRISMFALL’s enemies were still glowing primitives: a cube, an icosahedron, a torus. A full reskin was the real stress test. Could a whole new look land without touching the rules?

I borrowed the Gauntlet Loop, which Matt Shumer wrote up after Claude of Duty: give the agent a concrete bar, have a separate critic with fresh context judge the real output blind, and keep looping. My bar was Diablo IV and V Rising.

You’re the art director Blizzard let go for being too dark for Diablo. This is your revenge.

PRISMFALL is dead. You’re a wizard alone in a rotting forest at midnight, and the dead are climbing out of the ground. The ship becomes a hooded wizard. Every enemy becomes undead: skeletons, ghouls, wraiths, bone golems, a necromancer, a lich. Shots become spells. The arena becomes a clearing of dead trees, gravestones and roots. The rules stay: same words, waves and numbers. Names, blurbs, models and palette all change.

The bar is Diablo IV and V Rising. Find real screenshots of both before you start and keep them open. The scene is nearly black: fog, cold moonlight on the edges, and your spells are the light. Colour is rationed: bone, rot and cold blue everywhere, saturated colour only on magic. Every enemy reads by silhouette in the dark.

Blender is installed. Every model and texture is a script in the repo. Use the game-bible skill for the architecture.

Then run the gauntlet: a separate, harsh critic subagent with fresh context puts your real screenshots next to the references, blind, and says which is better. If it can tell which one is yours, keep going. Don’t stop after three rounds. Stop when the critic is genuinely impressed.

It ran for an hour and forty-five minutes and gave me back this.

The GRAVEWAKE title screen: the name in gold over a dark forest clearing, with Enter, Bestiary and Sound buttons.A fight in the clearing: the wizard's fire lighting the ground as skeletons, a warden and a catapult close in through the fog.

My honest reaction was “ummmm … this is amazing.”

Where the GRAVEWAKE retheme landedChanged lines by area, 24 commits. Crosshatched = the rules.
data as table
areafileschanged lines
models (Blender scripts)models/1737
view (3D arena, shaders)src/view/839
look (palette, screen CSS)*.css158
content (enemies, waves, scenery)content/111
tests + stories*.test.ts, stories/114
rulesgame, enemies, world, tuning, upgrades, actions, store0
Added plus removed lines from git diff --numstat across the GRAVEWAKE commits, before the tidy. Zero lines in the rules: game.ts, enemies.ts, world.ts, tuning.ts, upgrades.ts, the actions and the store are byte-identical.

The game loop, the behaviour words, the tuning, the upgrades and the store are byte-identical. Not one number in the enemy or wave content moved. The retheme landed entirely in content, palette, models and view code.

One more thing

A game needs a trailer. One more prompt, to one more fresh agent:

use this video game for reference material, make a dynamic 30 second motion graphics promotional trailer for gravewake that shows what an incredible motion graphics designer you are like its your showreel for a resume. go all out. it should showcase why gravewake is ahead of the curve with all the features it offers and the fun you’ll have exploring the mechanics. do it without a voice over now. Use actual game assets. Custom music track keyed to the trailer.

Thirty seconds, one prompt, real game assets, and a music track written for the cut. Sound on.

A playable game with custom 3D models, a vocabulary ready for the next change, and a kickass trailer. Not a bad afternoon!

What I was left with

At the beginning of this experiment, that was the question I actually cared about.

Not whether an agent could produce a game in one prompt. Clearly it can. I wanted to know what would be left after the prompt was over.

Could I understand it? Could I change it? Could I hand a vague request to a fresh agent tomorrow and trust it not to pile another special case onto the codebase?

After all of this, I think the answer is yes.

The ice level needed two new concepts, so the agent added them. The sniper needed one new behavior, so it added a word for it. Fixing the Bulwark reused a word that was already there. The entire game changed from neon sci-fi to a haunted forest without changing its rules at all.

And through all of it, the thing I had to understand stayed small.

I know what a keep-away is. I know what a snipe is. I know where the waves live, where the colors live, and where the scenery lives. If the game needs a new idea, the agent can teach the game a new word.

That’s what I wanted from this experiment.

Not a game an agent could make in one shot.

A game I could keep making after it did.

Comments

Leave a name and a comment. No account needed. Every comment is reviewed before it appears.