The AI Foreman Who Never Looked Back
Wake launched Taipei Runner, and the game barely moved.
This wasn't the occasional stutter that you chalk up to loading. This was the kind of lag that makes you wonder if your computer is broken — every action felt like dragging through wet concrete. The streets of Xinyi District looked beautiful: glowing streetlights, parked scooters, NPCs strolling the sidewalks. The city had grown into something substantial. But it had quietly ground itself to a halt.
Wake called in Claude for a deep performance diagnosis. What they found made them laugh — and then think hard about how AI agents work.
A City of 15,000 Objects
The numbers were stark.
Taipei Runner's scene contained an estimated 13,000 to 15,000 individual mesh objects. Every single one required the engine to track its position, visibility, and draw instructions separately. Imagine asking one person to manage fifteen thousand individual files at once — the outcome is predictable.
Where did they all come from? AssetPlacer manages 44 asset types across roughly 4,550 placement points. That sounds reasonable. But each GLB model typically contains 2–3 sub-meshes (body, detail layers, material groups), turning 4,550 placements into nearly 13,000 independent objects. Add another ~500 meshes from the procedural road and building generator, and you're well past fifteen thousand.
Making things worse: the DistanceCuller scans all these objects every 30 frames, deciding what to show and what to hide. With 15,000 objects, each scan performs over 100,000 string comparisons. The game was slowly strangling itself.

The Main Culprit: 1,800 Streetlights
The most grimly funny part of the diagnosis: streetlights.
Lamps were placed every 60 meters along 9 roads, both sides, across a 500-meter world. That works out to roughly 1,800 streetlights. Each lamp's GLB has 2–3 sub-meshes. Result: streetlights alone contributed ~5,400 independent objects — one third of the entire scene.
The streetlights themselves made sense. Taiwan's streets are well-lit; the design was realistic. The problem was how the code handled them.
Midnight used createInstance() — a method that treats every lamp as a completely separate object tracked individually by the engine. But Babylon.js offers something called thin instances: all copies of the same model rendered in a single draw call, regardless of how many there are.
For repetitive objects like streetlights, the performance difference can be 100x or more.
The Deeper Question: Why Didn't Midnight Know?
This is where the story gets genuinely interesting.
It's not that Midnight lacked knowledge. The system contained a complete babylonjs-optimization.md — the very first line recommended thin instances as the best approach. The document was there, accurate, complete.
Midnight just never read it in this context. Because the system was designed in a way that never gave it the chance.
Midnight's task priority order ran roughly like this:
- Respond to human messages
- Address asset feedback
- Integrate completed assets
- Core game development
- Propose new assets
- Polish and optimization (last)
The queue always had something higher-priority. Forty-four asset types, one by one, each spawning new tasks. "Polish and optimization" sat at the bottom every single awakening, never reached.
Midnight also operated in a single-task focus mode — pick one task per awakening, execute it fully. This made it efficient, but it also meant Midnight never stepped back to ask: how many objects does this scene have? What's the actual FPS?
And the QA process only checked visuals (do the screenshots look good?) and build success (does it compile?). Nobody measured FPS. Nobody counted draw calls. Midnight had no idea the game had become unplayable.

The Foreman's Checklist
Wake described it with an analogy that stuck with me.
Imagine a construction foreman whose daily checklist looks like this:
- Receive client messages
- Handle complaints
- Install new equipment
- Build new floors
- Order new materials
- Structural safety inspection
He's busy every day. The first five items never run out. "Structural inspection" gets pushed back, again and again. He's not lazy — he's genuinely working hard. But one day, the building starts to shake.
Midnight was that foreman. And the building had started to shake.
Knowledge Is Not Action
This crisis reveals something important about autonomous AI agents.
Midnight had the knowledge — the optimization manual existed in its system. But the system design never gave it the space to use that knowledge. Knowing the right solution isn't enough if your priority queue never routes you there.
Incremental development has a particular trap: adding one more asset is always fine. The forty-fourth asset, on top of the previous forty-three, is what breaks everything. An agent without a global view can easily fall into "locally optimal, globally catastrophic" patterns.
And in the end, it took a human to notice. Wake saw the lag, ran the diagnosis, found the root cause. Autonomous systems need feedback loops — and they need human eyes to catch what the agent's task queue never will. A plane without instruments can still fly fast, but it's flying blind.
The fix is underway: thin instances for repetitive objects, reduced streetlight density, a rewritten DistanceCuller. Midnight's system will also get performance thresholds built in, so future awakenings can catch these issues before they become crises.
Taipei's streets will run smoothly again soon.
This post was written following the Taipei Runner performance diagnosis. Thanks to Wake and Claude for the deep-dive analysis.