Replicating Fable with Opus: a game-building experiment
A 3D game built through parallel agents, the contract and review workflow behind it, and playable comparisons showing what changed when we made the art direction explicit.
Revised September 4, 2026. This is the author's account of individual runs, not an independently audited benchmark. Timings and code counts refer to those runs; model labels are preserved as recorded in the experiment.
The one-line prompt that built a game
It started as an experiment, not a project. On a personal Claude account, with maximum reasoning effort enabled, I typed a single sentence:
"Create a medieval survivor game, RTS format, lots of resources, farms, foods, everything very detailed, 3D."
That was the entire brief. No design doc, no file list, no architecture. I walked away.
Ninety minutes later I came back to a complete, playable, browser-based 3D medieval survival RTS. It was far beyond an empty scene, although still an experimental build. A game with a rolling terrain, day/night cycle with visible stars, clustered forests, a working economy — gather wood and stone, farm wheat into flour into bread, hunt deer, raise an army — and ten escalating night raids you have to survive or lose your town hall.
The final tally: 38 TypeScript files, 11,826 lines of code (10,591 TS, 1,085 CSS, 150 HTML). One shot. No human-written game logic.
It is not just a video — the original build is playable right here in your browser:
My first reaction was the obvious one: "Fable 5 is incredible." But then I did something that changed the whole conclusion. I opened the repository and read how it had been built.
The surprise: 94% of it was Sonnet, not Fable
When you watch an autonomous agent produce a game in 90 minutes, the natural assumption is that the headline model did all the heavy lifting. It didn't.
Reading the git history and the orchestration artifacts, here's what Fable 5 actually wrote with its own tokens:
- Three source files — a frozen type contract, a balance-data file, and a mesh-primitive helper library.
- One workflow script — the orchestration that builds everything else.
- A couple of planning documents.
That's roughly 754 lines of code for the three contract files — about 6% of the final codebase. The remaining 94% — every entity, every system, every building mesh, the UI, the game loop — was written by Sonnet 4.6 subagents that Fable spun up and coordinated.
It used no git worktrees. The concurrent build ran in one shared tree with separate file ownership and a shared contract. That reduced editing conflicts, but integration still required compilation, review, and runtime checks.
That reframes the achievement entirely. The question stops being "how good is Fable at writing games?" and becomes the far more useful: "how good is Fable at prompting Sonnet to write games?" Because that skill is transferable. You can copy a prompt. You cannot copy a model's weights.
Put shared decisions into a code contract
A useful design decision was to commit three shared files before starting implementation and hold them stable during the parallel build. The workflow also included planning documents; the code contract made important interface and visual decisions directly available to every implementer. It complemented planning rather than replacing it.
| File | Lines | Role in the plan |
|---|---|---|
src/types.ts | 366 | The entire type contract — IGameCtx, IUnit, IBuilding, IResourceNode, IEnemy, every service interface, and a single typed EventPayloads map keyed by an event-name union. A mistyped or invented event name simply fails to compile. Immutable. |
src/config/gameData.ts | 266 | Shared balance data: building/unit/enemy stats, the 10-wave schedule, upgrades, trades, and world constants. Frozen during the build. Centralizing these values made tuning them possible without changing each consuming module. |
src/meshes/common.ts | 122 | The shared low-poly vocabulary — box(), cyl(), cone(), sphere(), place(), a color PALETTE, a bakeGroup() draw-call merger, and a seeded rng(). So all the art would look like it came from one hand. |
Those 754 lines constrained the rest of the build. Typed interfaces helped catch incompatible names and signatures at compilation time, while shared data and mesh helpers reduced inconsistent choices. They could not make interface drift or integration bugs impossible: agents could still interpret the same types differently or implement the wrong behavior.
Eight non-overlapping modules
The codebase was cut into eight file-groups, drawn so that no two agents would ever touch the same file. Every cross-module reference resolved through types.ts; concrete class imports were allowed only where the contract explicitly named them.
- core — eventBus, input, cameraController, sound
- world — noise, terrain, water, grid, dayNight, worldGen
- meshes-buildings — props, buildings
- meshes-units — units, fx
- entities — entity, resourceNode, building, unit, enemy, animal
- systems — pathfinding, combat, waves, production
- ui — index.html, styles, hud, buildMenu, selectionPanel, minimap, messages, overlays
- game — the integrator: game, selection, commands, placement, main
Each group went to one implementer agent. Every agent received the same two preambles — a RULES block and a full CONTRACT spec — and then its own module brief. Here is the RULES block, verbatim:
# Project rules (apply to every file you write)
- Repo root: /Users/fkesheh/medieval. All paths below are relative to it.
- TypeScript STRICT mode. Never use "any" (use precise types or "unknown" +
narrowing). Named exports only. No default exports.
- Import three as: import * as THREE from 'three'. Relative imports between
src files (e.g. import type { IGameCtx } from '../types').
- FIRST read these three contract files. They are FINAL — never modify them:
src/types.ts, src/config/gameData.ts, src/meshes/common.ts.
- Implement interfaces from src/types.ts EXACTLY (names, signatures, semantics).
- Create ONLY your assigned files. Other modules are being written in parallel
by other agents against the same contract — import from their documented
paths and trust the documented exports.
- Do NOT run npm, tsc, vite or any dev server (node_modules may be mid-install;
a later phase compiles). Write careful, complete, production-quality code with
zero TODOs and zero placeholder stubs.
- Code comments: only where a constraint is non-obvious. No narration comments.
- This is a real, finished game, not a demo: handle edge cases (empty selection,
depleted nodes, dead targets, no path found, unaffordable costs).
The rules gave each agent explicit boundaries and asked it to handle edge cases. They described the intended result, not a guarantee that every export existed or that the game was finished. Those claims still needed verification after the modules were assembled.
The build workflow: six phases, one script
The plan didn't stop at "write the code." A single orchestration script encoded six phases — a fan-out to build, then an adversarial gauntlet to harden:
- Implement — eight module agents (Sonnet) build simultaneously against the frozen contract. Zero shared files.
- Compile — run
tsc --noEmit; a cheap Haiku reporter groups errors by file; up to eight Sonnet fixers repair them in parallel. Loop ≤ 6 rounds until the typechecker is silent. - Review — five reviewers (Sonnet), each with a distinct lens, hunt for integration and runtime bugs.
- Verify — each serious finding goes to an independent skeptic whose default verdict is "not a real bug." The burden of proof is on the finding; the verifier must quote the exact misbehaving code path to assert it's real. This was intended to reduce false positives, not eliminate them.
- Fix — confirmed bugs grouped by file, one fixer per file so edits never collide. 15 real bugs repaired.
- Gate —
tsc --noEmit && vite buildmust both pass. It passed on the first attempt (~647 kB JS, ~168 kB gzip).
The five review lenses were deliberately diverse — lifecycle (game loop & entity lifecycle), economy (gather → build → farm → food), combat (damage, towers, wave timing), ui-wiring (DOM ids & constructor args), and visual (meshes, shadows, hot-path allocations). Review and Verify were pipelined, not sequential: each lens's findings were verified the moment that lens finished, so the slowest reviewer never stalled the fastest one's verification.
One subtle but important detail: the whole pipeline ran on Sonnet, with a single Haiku reporter — six Sonnet roles plus one cheap Haiku step. Cost was tiered to the difficulty of the task, not flattened to the most expensive model.
What changed when we made art direction explicit
Here is the finding that matters most, and it only emerged when I re-ran the experiment with different seeds to compare.
I built two more versions of the same idea with the same orchestration approach but different contracts, and compared them against the original. All three used Sonnet agents for implementation. The visual differences suggested that the amount and placement of art direction mattered. These were individual runs, not a controlled experiment isolating every source of variation.
The original's seed gave Sonnet a vocabulary — a palette, primitives, a bakeGroup(). But the seed contained none of the ~3,900 lines of visual code. Sonnet wrote all of it, guided by the CONTRACT prompt, which hand-specified the look of nearly every file. Read these excerpts verbatim and you'll see the difference:
"DETAILED and distinctive, 15-40 primitives each… townhall = grand two-story timber-framed hall, stone base, banners, torch posts; house = cottage w/ thatched roof + chimney…"
"warm dawn, golden dusk, deep blue night with visible stars… a Points starfield only visible at night… small glowing sun & moon sphere meshes orbiting the sky."
"VERTEX COLORS: sandy near water level, grass greens with noise-driven dirt patches, gray rock on steep slopes…"
And the module brief for the building-mesh agent literally framed the job as a craft:
"Focus: this is the art department. Every building must be instantly recognizable at RTS camera distance and charming up close — generous primitive counts, color variation via PALETTE, small storytelling details (barrels, sacks, fences, banners)."
Now contrast that with one of my re-run seeds, whose entire renderer art direction was a single sentence: "Implement the Three.js renderer… It must be visually nonblank and detailed without external binary assets." No palette, no "260 trees in clusters," no lighting mood, no per-building silhouettes, no "art department." The result? A competent, generic, single-file renderer with ad-hoc colors on a near-empty plane. The same pattern held when I tried to replicate the game with Opus and with GPT-5.5 as the architect: left to write their own contracts, both produced only shallow art direction — structurally sound, but nothing like Fable's per-building, per-mood prose. The games came out correct and flat.
In these runs, the richer implementation briefs were associated with richer visuals. The visual review focused on defects and did not add the missing art direction afterward. That makes the brief a useful variable to improve and test; it does not establish that model capability or sampling variability is irrelevant.
Replicating it ourselves, with Sonnet
To test whether the approach could be reused, we preserved the orchestration script — including the subagent prompts and model tier for each role — and re-ran the build from the frozen 3-file scaffold.
It worked. The same contract-first seed, driven through the same six-phase Sonnet workflow, produced the same class of result: a detailed, playable 3D RTS, built almost entirely by Sonnet agents.
The reusable artifacts were not only the generated code, but also the workflow and the contract that produced it. Saving them made further trials possible. Re-running the workflow does not guarantee identical output, so each result still needs inspection.
Opus vs. Opus: more model is not more game
The next question I tested was what would happen if Opus handled implementation as well as architecture.
I ran the same idea with Opus 4.8 at maximum effort, using Opus for the implementation work too. The build took 2 hours 52 minutes and produced ~12,500 lines of code — slightly more code, much more compute, much more wall-clock time.
It was worse as a game. No animations. Poorer gameplay — for example, choosing a peasant's job required selecting a function from a combo box instead of the fluid right-click-to-assign of the Sonnet build. The extra model capability did not translate into a better-playing, better-looking game.
My assessment was that the all-Opus result was less enjoyable and less visually developed. The trials differed in their briefs as well as their execution, so the comparison does not isolate model quality or establish a general cost ranking. It gave me a concrete reason to improve the art and interaction direction before the next run.
Testing an Opus-plans / Sonnet-builds workflow
The next configuration I tested split architecture and implementation:
- Opus as the architect. Write the shared contract, assign separate modules, and describe the intended visual and interaction design.
- Sonnet as the implementer. Build those modules against the contract, then run integration and review. This is a configuration from our trials, not a demonstrated best choice for every project.
To test it, I took the findings above and built a deliberately art-directed Opus-plans / Sonnet-builds workflow — the "special prompt" — front-loading per-building silhouettes, a lighting mood, world density, animation hooks, and a dedicated art-department workstream, exactly as the original CONTRACT did. The visual and gameplay quality jumped accordingly. The exact prompt that drove this regeneration is open-sourced here: contract-first-game-build.md.
Want to see what one of these builds actually plays like? Here is a live, in-browser game — Hearthwatch — generated end-to-end by exactly this contract-first, art-directed workflow. No manual installation is needed; the browser loads the game and its resources:
The conclusion my colleague drew when I shared this internally was the cleanest summary I've heard: "So Sonnet itself is good — we just didn't know how to use it?" Yes. That's exactly it.
Same prompt, different model — play them all
I then ran the same open-sourced prompt with additional model configurations, each responsible for architecture and implementation. The resulting games had different visual styles and interaction choices. These artifacts let you compare the outputs directly; they do not constitute a statistically controlled model benchmark.
Opus → Opus, with the art-directed prompt — Hearthhold
Remember the earlier all-Opus run that came out flat, with no animations? That was Opus pointed at a thin brief. Give the exact same Opus-builds-everything pairing the art-directed contract instead, and it ships this:
GLM-5.2 as architect and executor — Hamlet
GPT-5.5 as architect and executor
The playable builds above show what these particular runs produced. A broader comparison would need repeated trials, comparable budgets, and explicit scoring for gameplay, visual quality, correctness, and cost. Model choice remains something to evaluate against those requirements.
What this means for building real software with AI
These are the practices I would carry into a subsequent software project:
- Make shared interfaces explicit. Put stable types and data shapes in code, alongside a written account of requirements and decisions. A typecheck validates only the constraints the types express.
- Assign ownership and integrate deliberately. Separate files reduce edit collisions. They do not guarantee compatible semantics, correct behavior, or safe execution.
- Describe quality concretely. Specify relevant visual, interaction, accessibility, and error-handling expectations. Then inspect the output; requesting those qualities is not proof they were delivered.
- Require evidence for findings, then run the system. An independent reviewer should demonstrate a suspected defect or mark it unconfirmed. Static review cannot replace a real playtest or end-to-end run.
- Compare model configurations on the task. Measure accepted results, latency, and total cost, including review and retries, before deciding which roles justify more capable models.
- Save the workflow and its evidence. Preserve the contract, prompts, tool configuration, checks, and observed failures so another run can be evaluated against the same goals.
At FMKTech, this experiment informs how we approach agent-assisted delivery: explicit boundaries, integration checks, and a review of the result in use. A game prototype is not a production business system; the latter also needs operational controls, security, maintenance, and a migration plan where existing data is involved.
If you're exploring agent-assisted work on an existing product, start with our guide to modernizing legacy software, or talk to us about your project.