← ryanday. ~/ writing / one-shot ~12 min read
Build log · 2026-09-01 → 2026-09-04

I one-shotted a 3D strategy game

Fifty-two hours, one prompt, and the only word I typed after it was continue. Everything below is the running game, screenshotted by the agents that built it.

Elapsed
51 h 56 m
Commits
63
Lines of src
29.0 k
Lines of tests
7.0 k
Modules
15
Review rounds
5
Critiques
31
Frames shot
1,436
The finished game at night: a hex island, a lit base camp, glowing blue flora and a research HUD. Play Understory  pure local · no install · no login
  • desktop only, it will not work on your phone
  • no tutorial, no onboarding, nothing, which turns out to be a lot of what this article is about

A few days ago I saw someone one shot part of a 3D game on Reddit with Fable 5.1 and immediately had one of those I want to build something moments. The post included the prompt that had been used to make it, so I thought, what the heck, let's see how Fable 5.1 performs on creating a game from scratch.

I have always liked Advance Wars, so I started thinking about that basic structure: a little board, units you move around, and turns where the world changes. So I gave Claude a vague prompt about a science team exploring some alien plant life, on top of the template from the Reddit post, and let it rip.

The whole prompt
Goal

Build a AAA-class, turn-based hex strategy game in Three.js (latest release) + Vite, plain ES modules, from this empty folder. Advance Wars in structure, but with no combat: the opponent is a living alien forest that grows and spreads on its own turn. The player leads a small science team whose goal is understanding, not conquest — earn enough research to complete an expedition objective before the forest overruns the team. The bar is AAA: photographic PBR materials, physically plausible sun/sky/shadows, atmospheric depth, a forest that glows at night, believable animated units. Never programmer art. The world must read as a place first and a board second.

Design the game yourself: the terrain types, the flora species and their spread rules and interactions, the unit roles and their actions, the research economy, supply, fog of war, objectives, and loss conditions. Write it all down in DESIGN.md before building. The one constraint is that every mechanic must be about reading and shaping a living environment, never about fighting it.

How to work

1. Architecture first. Before any feature code, write ARCHITECTURE.md: one folder per subsystem (you choose the breakdown, including a demo map), a shared world data model, the public API each module must expose, the events it emits, units (metres, +Y up), determinism (seeded RNG only; the forest's turn must be fully reproducible from the seed and action log), a performance budget (≥50 fps at 1080p, ≤1500 draw calls) and an asset policy (CC0 only: Poly Haven, ambientCG, or procedural; if an /assets folder is present, prefer its models and textures over procedural generation, otherwise generate procedurally but keep the loading path in place). Isolate module failures so one broken module never takes the game down.
2. Build the verification loop before the game. A headless-Chrome screenshot tool that loads the app, waits until ready, sets a camera preset, time of day and optionally a turn number, and writes PNG + a JSON log (console errors, fps, draw calls). Every module also ships a "showcase" mode that stages a representative scene of just that module. No agent may claim anything it hasn't screenshotted and looked at.
3. Fan out. Use multi-agent orchestration ("ultracode"). One builder agent per module, each owning only its folder. Run in waves ordered by dependency, with the demo map last. Between waves, one integrator agent (the only one allowed to touch core) applies builders' core-change requests and fixes the seams.
4. Gauntlet every module. After each builder round, a separate critic agent (a brutal AAA art director who writes no code) takes its own screenshots at several times of day and zoom levels, checks the API contract, console errors and perf, and scores 0–10 against reference screenshots of AAA alien-forest and nature environments: 10 = indistinguishable, 8.5 = AAA with nits, 7 = good indie, 5 = programmer art. A separate design critic plays ten turns headlessly and checks that the forest's growth creates real decisions rather than noise. Pass = ≥8.5 with zero errors. Below that, the builder gets the ranked issue list and goes again, up to 4 rounds.
5. Final gate. A whole-game critic scores the demo map across a full playthrough. Then blind judges get pairs of screenshots labelled only A and B (ours vs. AAA reference, order shuffled) and say which looks better and why.
6. /loop until every critic passes. Persist scores and open issues to docs/STATUS.json so each iteration resumes from the weakest module, not from scratch.

Rules

- Never inflate scores. Report real numbers, failed rounds and what is still missing.
- Never edit another module's folder. Core changes go through the integrator.
- Keep the dev server running and the app loadable at all times; other agents are screenshotting it.
- Do not ask me questions. Make routine decisions yourself, state assumptions, keep going.

Start now.

My prompting strategy was typing “continue”

I kept it all in one thread. I used up something like 3 five hour Claude Code sessions of pure Fable 5.1 for the planning and the implementation before I used my weekly limit of Fable. When all my Fable ran out I went and got a second Claude Max account (I am now one of those people...). Then I ran out of Fable on that one too and moved to ol' Opus 5. The whole time, every time I hit a session limit, I waited for the next session and typed “continue.” That was all the steering I did.

The build was extremely subagent-heavy. One builder agent per module (terrain, plants, units, interface, fog) owning only its own folder, and a critic assigned to each one whose job was to keep finding more work. I watched the status files change as modules moved through review rounds.

One word in that prompt did more work than all the others: ultracode. That is the switch for Claude Code's workflow orchestration, and once it was on it leaned on it hard. The main session fired 23 workflow runs, ten written fresh and thirteen re-runs of a script it had already saved to disk and edited. It ran five rounds. Here is what one of them looks like.

phase('Fix') phase('Critique') 6 builders at once 1 critic per module ui overlay units flora fog terrain headless Chrome rig one browser at a time machine-wide lock 8 at once = load avg 100+ 8.5PASS 8.0fail 7.7fail 7.5fail 7.1fail 7.0fail pass bar 8.5 done never touched again ranked issue list only the modules under the bar go again docs/STATUS.json every score, every open issue integrator the only one who edits core
One round. Builders fan out, every one of them funnels through a rig that can only drive a single browser at a time, and then a critic per module reads the frames and scores them. Scores are the critics' own from round 3; six of the fifteen modules are shown. The interface is the only thing that ever cleared the bar, which is why it is the one lane that exits.

That lock is also the answer to why a round took so long. Six builders can edit six folders at once, but the moment they want to look at what they did, they line up single file. The queue exists because the first version had no queue: eight agents launched Chrome at the same time, the load average went past 100, and every capture timed out.

Each shot in that queue is slower than it sounds, because it is not a snapshot. The tool loads the page, waits for the game to report ready, waits again for it to settle, re-applies the camera and the hour, measures 90 frames, and only then saves the PNG. I re-timed them while writing this, on an idle machine: a terrain preset costs about ten seconds, and the full game with the HUD up closer to thirty. Terrain's fourth round alone cites 35 shots, and there are 1,436 frames in the archive. Somewhere between seven and fourteen hours of this run was the machine photographing itself, one picture at a time.

The part that is not in that picture is the blind judging, which happens after all the reviews in one round are done. It puts a game frame next to a real photograph, labels them A and B, alternates which slot the game is in, tells the judge nothing about where either image came from, and explicitly forbids it from speculating about which one is the render. A judge that guesses “this is a game” scores its guess instead of the image.

One of the first things it built was not the game. It was a camera rig: a headless Chrome harness that loads the game, waits for it to settle, points a camera at one of eight fixed presets, sets the clock to a given hour, and saves a PNG next to a JSON file recording the commit, the GPU, the draw calls and the frame time. Every image on this page came out of that rig.

The grey captions are its filenames. tod is time of day on a 24-hour clock: tod 12 is noon, tod 22 is ten at night, tod 6 and tod 18 are dawn and dusk. turn 10 is the game turn. The word before those is which of the eight camera presets it shot from, and T+ is hours since the first commit.

T+00:00 → 02:29

Hour zero is a field of coloured cylinders

The first thing the rig ever photographed was a contact sheet proving two cameras could see the same grid. For the next two and a half hours the world is untextured primitives on flat ground. This is what the game's data model looks like before any art exists to hide it.

Two side-by-side flat renders of a green hex grid on black.
The first frame in the archive, a two-up contact sheet checking that the loader and the camera agree about where the board is.
core · contact / T+00:00
Hex board of flat-shaded cylinders in tan, grey and blue with coloured cones marking flora.
The forest simulation, running and correct, drawn as cylinders and cones. Species are colour codes.
forest · high · tod 12 / T+00:12
Hex board with a blue river snaking through tan and grey tiles, red and orange markers scattered.
Map generation lands: a river, terrain bands, resource sites. Still no lighting model, so every tile is its own flat colour.
maps · high · tod 12 / T+01:06
Low-angle view over large flat yellow hexagons, a few teal mushroom shapes at the horizon.
The camera module gets its eight presets. This is marsh, the same framing used in the shoreline comparison further down.
camera · marsh · tod 12 / T+01:06
Flat sand plane with scattered rocks and thin white pole-like trees under a black sky.
Sky benchmarking with everything stripped out: bare ground, no post-processing, no shadows. The trees are placeholder poles.
sky · mid · tod 12 · bare / T+01:08
Terrain with a river, overlaid with translucent amber hexagon highlights and a dotted path.
First overlay pass, and the first frame where the board reads as a board: range rings, a dotted move path, hex highlights over real ground. Looked pretty good already!
overlay · mid · tod 12 · wip / T+02:29
T+08:18 → 24:39

Ground, sky, and losing condition

Overnight the world acquires a physically-based surface and an atmosphere. Each module gets its own showcase scene, a materials bench or a sky dome or a terrain field, so a critic can score it in isolation before looking at the assembled game.

Sunlit sand plain scattered with dark rocks and pale bare trunks, blue mountains on the horizon.
Sky round 1: an actual sun, aerial perspective, mountains on the horizon. The ground underneath is still a single flat material.
sky · camp · tod 12 / T+08:18  /  art 7.0
Material test bench: four large spheres in white, black, chrome and speckled blue on a grey slab.
The materials bench (chalk, basalt, metal, lichen) is shot on its own so roughness and albedo can be judged without terrain features confusing the agent.
materials · closeup · tod 12  /  art 7.0
The whole hex island seen from above in daylight, camp buildings clustered at centre, HUD panels at the edges.
First assembled game frame: terrain, materials, sky, units, fog of war and HUD in one shot. Everything past this point is refinement.
game · camp · tod 12 · turn 10 / T+08:35
Wide terrain view: a blue river cutting a broad tan and grey basin under haze.
Terrain mid-round: river banks, peat beds, plateaus and boulders are in, and the height field finally carries relief instead of noise.
terrain · wip / T+19:38
None of that surface is downloaded. Every texture set in the game (albedo, normal, roughness, ambient occlusion, height) is generated at load time from seeded noise, so the look is identical on every machine. There are a dozen CC0 photographs from Poly Haven in the repo, but they are not assets. They are the critic's reference set, the pixels it holds the game frame next to when it decides whether wet rock looks like wet rock.

I could not understand why it cared so much about fog

The terrain and the atmospheric fog kept failing review. I remember watching yet another round start and thinking, man, how much better can the fog get? Fog is fog. It felt like the agent was spending too much time on this.

Then I saw the before and after.

The board after dark

game · camp · tod 22 · turn 10

The same overhead camera at hour 22. The early night frame is a black hole with a few sprites floating in it. The later one has a moon key light, fog of war lit by the same sky as the ground, lamps on the camp, a rover with its headlights on, a red glow coming off the thermal vent, and bioluminescence doing the rest. Drag the divider to see the difference.

The final game is nowhere near a modern AAA production, but it does feel like a real indie game. I can click on these little robots, move them through the glowing underbrush, and watch the plants spread around them. Ten years ago I think people would have paid real money for something at this level. Now I can just put it on my website for free, which is exactly what I did: it is here.

T+34:31 → 51:56

What the last third of the run bought

On the morning of the third day my Fable ran out for good and I switched to Opus. That handover is a real commit in the repo, called fable 5.1 checkpoint. 42 of the 63 commits came after it, along with 6,744 added lines across 75 files. All giant review / refinement sessions.

I went back, checked that commit out into a worktree, and ran the same eight camera presets against it that I ran against the finished game. Same preset, same hour, same turn, both sides. Left is where Fable stopped. Right is where it finished.

Terrain from altitude

terrain · high · tod 12

On the left the board is a flat overhead wash with no horizon at all, and if you look you can see faint straight lines cutting across the frame: every off-board value was computed from distance to the board's bounding box, so the world ended in a rectangle with square corners. On the right there is a sky, a mountain range, and a height field that carries relief all the way to the horizon.

Flora at night

flora · closeup · tod 22

Left: trunks with stripes painted on them, standing on a flat green wash. Right: the mirelace filament network reads as a lit surface, the emberveil vents throw orange light onto the ground around them, the ground has cracks, and the mountains sit back in aerial perspective. The rule the critic kept citing is that a glow is a light, not a decal. A luminous plant has to put light on the dirt next to it.

The shoreline

terrain · marsh · tod 12

Here is the one where I looked at it and thought, hang on, the before is better. I checked the critic's round 4 notes, it logged two regressions in the water in its own words. The open water came out flatter than the round before it, a 60-pixel patch dropping from 0.0123 standard deviation to 0.0022, because two changes that round fought each other: one added a 23-metre swell, the other flattened the body colour back out again. And the new shallows band became “a hard-edged, high-frequency cyan speckle crust that reads as digital noise,” the brightest thing in the frame after the sky. Get close to it and that all reverses: the same water measures 0.0497 at the closeup preset, the swell reads properly, and the critic called one of the night closeups a frame it would ship. It is the middle distance, the one you actually play at, that went flat. There was never a round 5 for terrain, so it still looks like that. I'll probably fix it later.

The assembled board at night

game · camp · tod 22 · turn 10

Look at the clock in the top left of the checkpoint frame. It says 12:00, Day 3, Noon, with a sun icon, over a scene rendered at ten at night. The HUD was not following the time-of-day override. The same tool call against the finished build reads 22:00, Night. The flora went from flat blue blobs to lit volumes in the same pass.

The units

units · closeup · tod 22

Here is one that barely moves, which is the point. The rigs are the same rigs. What you notice if you flick the divider back and forth is dirt: the ground picks up mottling and scattered stones and dust, the hulls stop looking freshly unwrapped, and the lamps throw real cones onto it. The rover on the right is the clearest tell. Same six wheels, but it now sits in the ground instead of on it and reads like equipment that has been driven somewhere. Meanwhile the round's actual work was invisible, a 54% cut to the triangle budget that took render time from 3.6 ms to 1.9 ms.

The interface + fog

ui · mid · tod 12 · turn 10

The panels are identical, which is the honest result: the interface was the one module that ever cleared the critic's 8.5 pass bar, and once it passed nothing touched it again. But look at what is behind them, because this frame is mostly fog and the fog moved a lot. On the left the unknown is one smooth pale wash and there is a soft dark ellipse sitting under the turn banner to keep the text readable. On the right the mist has broken into banks with structure, it rolls over the ridge instead of sitting flat on top of it, the bedding in the exposed rock reads through, and the ellipse is gone because the text no longer needs the help. The numbers changed too, and that is the design critic: research targets went from 60/60/30 to 100/90/50 and the airdrop dropped from +6 supply a turn to +3. It quietly made the game harder while I was not looking.

Then I tried to play the game

Actually playing it exposed the part of the prompt I had not thought through.

The rules are complicated because there are many kinds of plants, and each one grows differently or reacts to other things in the environment. On top of that, there was no guidance about what each unit did, how to get points, or how to do the objectives that were listed. The game did nothing in the way of onboarding.

The agent did not build a tutorial because I never asked for one. I also did not create a reviewer whose job was to start cold and complain whenever the game became confusing. The art critic was relentless because I had given it a very specific idea of visual quality, but I had not defined a goal for playability. There was a design critic, and it did review the game, but it was scored on whether ten turns contained real decisions and whether the thing was winnable. Not on whether any of it was discoverable, and not on whether it was fun.

Retro on code review

My brain is constantly thinking about code review, because that is what I do all day now with AI-generated code. Agent code review I have traditionally done by topic. Check correctness, security, performance, and whether the code is simple enough to maintain. Those are all good things to check, but this loop taught me that for code review to work it needs to be more personalized to the repo. It has to know what kind of project it is looking at.

what I have been doing the same four questions correctness security performance simplicity asked of every module, whatever the module is what I am reviewing what it actually fails at so the critic is terrain + fog the game's rules an HTTP API a shared library other projects, same idea reads as programmer art nobody knows what to do falls over on bad input easy to call wrong a brutal art director shoots it at four times of the in-game day someone playing it cold I never asked for this one someone hammering the contract latency assumptions, failure behaviour someone misusing it on purpose hunting confusing invariants
The generic review is not wrong. It just asks the same thing of a shader and a rulebook, which is why it sits on its own over there, unconnected to anything. The other way round starts from what the thing can actually fail at and lets that pick the reviewer. The top two rows are this game. The dashed one is the critic I never wrote, which is exactly why the game shipped with no idea how to teach you to play it.

I am still thinking this through, but I do not think every project needs the same giant review workflow. It needs reviewers aimed at the places where that particular project is likely to fail.

What made the art critic work was that it could not score anything it could not point at. Its rubric says so directly: every criterion has to be something you can point to in a PNG or read out of a JSON log, and if you cannot point at it, do not score it. That is why the loop produced 1,436 screenshots. The reviewer needed evidence.

Right now these loops are also incredibly token-heavy, on top of being slow. A round is one or two hours of wall clock, and a real chunk of that is just the queue at the browser. Then every one of those frames has to be looked at by something that charges by the token. Just getting all the code generated before anything could be tested took a long time.

In retrospect, I wish I had used Fable 5.1 for the planning and overseer work, then handed the actual code execution to Opus. Fable did a surprisingly good job keeping the whole thing organized and reviewing everybody else's work while I stayed hands-off, which is the job I should have saved its tokens for. Opus could have handled the implementation instead of me burning all those Fable sessions on it.

Overall, had a lot of fun doing this project. I would just check in every couple hours and check out the screenshots and progress. Now that there is something working, I definitely want to improve the game to make it more fun. I want to make the game simpler, build multiple levels that add to your knowledge one piece at a time, and make exploring more rewarding once you understand what you are discovering.

But it will have to wait until next week, when my limits reset.

Thanks for reading everyone :)