The valley I had just laid out was 448 metres across, and from the square you could not see the other side of it. The haze was full at the edge of what the game loaded, so the keep on its crag, the chapel on its rise and the far shore of the mere dissolved into one pale wall.
That was the look problem. The cost problem was worse: the relayout's measurements showed the first-person frame was mostly spent on things a long way off.
On 5 October the agents built an answer in a morning: draw the far land cheaply as baked tiles, draw the middle distance's trees as flat cards, and in exchange load full detail less far.
The far shore of the mere from the jetty, 5 October, with far tiles and tree cards.
Where it stood
The relayout to my map landed early that morning, and my first request was to see it: "can you load up a dev server for me to play what we have on so far ... including any mazes etc".
The village was over its frame limits, and I accepted that until the next spec measured again. I also asked whether those limits were made up at all (the answer was half yes: the frame limits come from real refresh rates, but the 8 and 6 ms streaming budgets "were picked by agents and you accepted them", and everything had been measured on my own fast Mac), adding "especially when we start to look at integrating meshy models etc". I kept the milliseconds and loosened something else: "Lets relax on the initial Kb download, I dont mind this being raised a little."
Under the hood On High, first person at the village spawn took 13.2 ms a frame. Everything past 60 m cost 4.6 to 5.1 ms of it: about 575 woodland trees at full detail (one model was 45,000 triangles), field walls and cottages, all loaded out to 192 m.
Tiles and cards
The plan, which went through two Codex review rounds before I saw it, had two parts.
A far tile is 128 metres of land baked into one simple mesh with no textures: lanes painted into the ground's colour, every building simplified and tinted with the average colour of its own textures. A small map says which chunks are loaded in full, so each patch of ground is drawn once, near or far, never both.
A tree card is a tree drawn as a flat picture that turns to face you. The bake photographs each tree from eight sides and the game shows whichever matches where you stand, lit so a card still darkens on its shaded side.
One ruling went to me: build it as specced and judge the haze and the card swap once I could see them? I answered with the morning's other questions: "go with your recommendation elsewhere". In the same message I saved the chapel. Its new spire, built that morning because the chapel did not read as a chapel from the square, had pushed the village's code over its size ceiling, and trimming it was an option. "raised js limit is fine, dont cut the chapel 0.7kb".
The agents built it in four pieces, all merged by lunchtime: Low's slow building steps split into smaller ones, the far ring itself, the bake, then the cards.
From the tithe barn, before: the village is a smudge in the haze.
The same spot after, with the same light and settings. The spire was already there; now you can see it.
Under the hood Each tile is capped at 200 KB. The final cap of 15,000 triangles a tile left the whole village's far land at 16 tiles and about 1.3 MB, and on High the full-detail ring came in from 192 m to about 128 m.
Did the cards look like trees?
The test that mattered was whether you could tell. The agents shot the north woods with cards and with every tree as its full model, in summer and winter. Side by side they are hard to tell apart. The swap itself was still a hard switch, left for me to judge when I walked it.
The north woods: every tree a full model (left) against cards past 64 m (right), in summer and winter. Period evidence from the cards piece.
Then the credits ran out
Early that afternoon my Claude usage ran out with the last piece, far-4 (its fixes and the performance tuning), half done and an art stream running beside it. I opened Codex and asked it to take over: "I've run out of credits. I want you to take over... What I'm worried about is they were in the middle of working when we ran out of usage."
The Codex agents recovered the interrupted measurements and carried on. An hour later I wanted a clean stop: "Can you get art and far-4 to a sensible stopping point?" Then I talked myself into finishing instead: "the moment I feel like we're a bit fragmented with far-4 not merged. I know we haven't done performance acceptance and reviews..." And: "Okay, let's do that one final far-4 piece so we can properly merge and remove its worktree."
The number that did not move
The view was the easy half. The plan had written down a target, 9.5 ms for that first-person frame, and four tuning steps in order: cards closer, full detail closer, lighter tiles, the far edge pulled in. The best reached 11.5 ms. After the art merge and the last fixes, the final measurement was 13.0.
Under the hood Final High numbers at the village spawn: first person 13.0 ms against 9.5 (missed), overhead 11.1 against 11.5 (met), streaming updates 5.2 to 5.7 ms against 8. No shader compiled while drawing on any walk.
The landing note recorded a miss, not a new limit, and handed the number to my next request, sent as I went AFK: independent audits, starting with "General performance of the app when playing".
Drawing the far land cheaply had worked. It just was not where most of the frame went: the spec had already guessed, for the overhead view, that the rest was the shading of what stands near you.
The view at the top of this post, before: the same jetty, the far shore lost in haze.
A stall inside three.js
That night the agents chased a hitch walking towards the proving ground: one callback took 44.5 ms because five shader programs were still linking mid-play. The cause was a known three.js bug, already fixed upstream, so they backported that fix as a pinned patch rather than writing their own shadow code. The hitch fell from about 40 ms to 12.9. The next morning's commit recorded the first-person investigation "without speculative tuning": find the cost before you change anything.
What I took from it
Measure first, and write the target down. The tiles fixed the view for very little, but the written target is what stopped a better view passing for a faster frame.