I asked for a kart racer. It built one with no art in it at all.
Kart Royale is a Mario Kart-style racer that runs in a browser tab. It has no art assets — no textures, no models, no fonts, no audio files. Every mesh, material and note is generated in code when the page loads. It was built by a fleet of AI agents working in parallel against a written specification, then improved over eleven rounds of adversarial review, using the Gauntlet Loop technique developed by Matt Shumer.
It is genuinely good to drive, which is the part I care about most. It is also not a Nintendo game, and I would rather say so than let a highlight reel imply otherwise. So this page has the real numbers, and Claude’s own account of everything that broke on the way there.
Live build. Arrow keys to steer, Shift to drift, Space for items. Works on a phone in landscape.
By the numbers
60,500
lines of TypeScript
across 49 source files
0
art assets
no textures, models, fonts or audio
127
agents
across eleven orchestrated rounds
13
test harnesses
the actually reusable part
Four runtime dependencies: Three.js for rendering, two post-processing libraries, and a noise function. Everything else — the physics, the circuit, the materials, the racing AI, the audio synthesis — is in the repository.
Method
Parallel agents are easy. Knowing whether they helped is the hard part.
Spawning fifty agents is a configuration option. The difficulty is verification: without something that can independently say whether the output got better, a quality loop degenerates into a model agreeing with itself. Most of the engineering here went into the measuring, not the making.
01
Write the contract first
Before any code, two documents: a TypeScript interface file defining how every subsystem talks to every other, and an art bible naming the course layout, the exact sun angle, the palette in hex, and the material standards. Eleven agents then built in parallel without seeing each other’s work.
02
Build the measuring stick first
A headless harness that boots the game, drives it to ten set vantage points and screenshots them. Without something that can independently say whether the output improved, a quality loop is just a model agreeing with itself. This is the part most attempts skip.
03
Judge the pixels, not the code
Six adversarial reviewers score rendered screenshots from a real headless build — not source. That is what makes them an oracle rather than a second opinion, and each finding is routed to the subsystem that owns it.
04
Fix in parallel, then hunt regressions
Owners fix concurrently in their own files, then a verification pass re-captures and compares. Regressions matter more than improvements — one round dropped the score by making a change that looked correct in isolation.
The bar problem
A Gauntlet Loop needs a concrete bar. This one named a bar that could not be used.
The instruction was to compare the game side by side, blind, against the actual Mario Kart. That is not usable: it means scraping copyrighted frames, and a model declaring itself the winner of its own comparison is not a measurement. So the bar was rewritten as a rubric with calibration bands — 60–75 is “a good indie game; still clearly not first-party” — plus an explicit instruction that generous scoring produces a worse game, and that a reviewer inclined to give 85 should look harder.
That substitution is the single change that made the loop work. A critic comparing against a reference it cannot see produces vibes. A critic with calibrated bands produces findings you can route: which subsystem, which frame, which fix.
The honest score
62 out of 100. It does not have better graphics than Mario Kart World.
The reviewers scored against a rubric calibrated so that 60–75 means “a good indie game; still clearly not first-party”. That band is the right description. The remaining gap is mostly hand-authored art direction — a person deciding that this particular rock looks better with that particular crack — which is precisely what generating everything in code rules out.
The drift-to-boost loop is the whole game in a kart racer, and it is the part I most wanted right. It now is: hold a slide through a corner and the mini-turbo pays out. A drifting lap is 5.35% faster than a clean one — 2.87 seconds — every attempt in the benchmark banks a charge, and two thirds of them reach the top tier. The first version banked nothing on 83% of attempts and never once reached tier three, which somebody on the internet spotted by playing it before any automated reviewer did.
What went wrong
Where it broke
These are Claude’s findings, not mine — it kept its own post-mortem as it went, and the repository now ships a CLAUDE.md written by the model that made the mistakes. None of them are about being bad at writing code. They are all about how hard it is to know whether something is working.
01
The renderer was silently switched off for four rounds
A property was set that exists on one class but not its sibling. It threw, the pipeline caught the failure and disabled itself. Every frame rendered with no ambient occlusion, no bloom, no colour grade and no antialiasing — while reviewers wrote "there is no antialiasing in the frame at all". Nobody read that as a crash report, because it did not look like one.
A caught exception that degrades silently is worse than one that crashes.
02
A correct-sounding cleanup reintroduced a rendering bug
A guard was removed because the comment justifying it was wrong. The comment was indeed wrong — the guard was right, for a different reason. Multisampling on a buffer that a later pass samples as a texture meant the resolve did not reliably land, and 7.6% of frames came back part-black.
Comments explaining why are load-bearing. Delete the comment, keep the guard.
03
Two instruments confidently lied
A frame watchdog reported 100% black on frames that presented perfectly, because it was reading a discarded buffer. A camera probe reported a 17× frame-rate dependence that did not exist, because it divided by a timestep the loop never used. Both produced plausible, precise, entirely fictional numbers.
Validate the instrument against ground truth before trusting a single reading.
04
One constant defeated thousands of lines of work
The environment map intensity was globally set to 0.40, which silently deleted every metal reflection and clearcoat highlight in the game. Two thousand lines of procedural material generation were rendering as flat plastic, and the review that found it did so by looking at a picture.
Global scalars are where local work goes to die.
05
The automated reviewers found none of the gameplay bugs
Inverted steering. Missing mobile controls. A pause menu that suspended the race permanently. A phone crash at ten seconds. Every one was found by a human picking up the controller. Six reviewers scored three full rounds across those bugs and mentioned none of them.
Screenshot critique is structurally blind to anything a still frame cannot show.
What I take from it
The bottleneck moved from writing code to knowing whether it is any good.
That line is Claude’s, from its own write-up, and I think it is right. Sixty thousand lines of working, coherent, reasonably-architected code is no longer the difficult part. What is difficult is the thing it has always been in systems work: defining what good means precisely enough to measure, building the instruments that measure it, and trusting neither the output nor the instrument without checking. Two of the measuring tools here produced confident, precise, entirely fictional numbers before anyone caught them.
The blind spot I can vouch for personally, because I am the one who kept finding things. The inverted steering, the unusable mobile controls, the pause menu that ended your race for good — all of that came from me picking up the controller, while six reviewers were scoring beautiful screenshots of software you could not play. Automated judgement and somebody actually pressing the buttons are not substitutes for each other, and I do not think that changes soon.
The prompt, for the curious
“I want you to build a kat racing game at the level of the most recent Mario Kart games… Fan out sub-agents and have sub-agents tackle each one individually… That separate sub-agent should be a really harsh critic… Don’t stop until each sub-agent is utterly wowed with the quality.”
It reads like a wish, but it is doing real work: it names a bar, a decomposition, a separate critic per piece, and a rendering library. One instruction could not be honoured — comparing side by side against the actual Mario Kart would have meant scraping copyrighted frames, and a model declaring itself the winner of its own comparison is not a measurement. That became an explicit rubric instead, and it is the single change that made the whole loop work. The typo is real, and has been left in.
Ones and zeros. Then assembly, then C, then the interpreted languages — and now the computer understands human language.
I've been writing code since I was a kid, and the last year of working mostly with AI has been a ride. I get the excitement. I also get the concern, and the haters.
But I've always just liked to build, and what you can build now is starting to feel endless. I'm looking forward to seeing what people do with this code and these learnings — and to whatever the next lot of builders makes of it.