1import{j as e,S as n,N as i,B as r,L as o}from"./index-BfigybZr.js";function d(){return e.jsxs("div",{className:"my-10 grid grid-cols-1 md:grid-cols-2 gap-px bg-border/40 rounded overflow-hidden border border-border/40 text-sm",children:[e.jsxs("div",{className:"bg-background p-5 space-y-3",children:[e.jsx("p",{className:"text-xs font-semibold uppercase tracking-widest text-muted-foreground mb-4",children:"Lovable â human in the loop"}),[["Describe","tell it what to build in words"],["Generate","Lovable edits the code"],["You play it","you are the only sensor"],["You spot the problem","describe it back in words"],["Fix attempt","may or may not land"],["Repeat","until you give up or it works"]].map(([s,t],a)=>e.jsxs("div",{className:"flex gap-3 items-start",children:[e.jsx("span",{className:"mt-0.5 w-5 h-5 rounded-full bg-muted flex items-center justify-center text-[10px] text-muted-foreground shrink-0",children:a+1}),e.jsxs("div",{children:[e.jsx("span",{className:"font-medium text-foreground",children:s}),e.jsxs("span",{className:"text-muted-foreground",children:[" â ",t]})]})]},a))]}),e.jsxs("div",{className:"bg-muted/20 p-5 space-y-3",children:[e.jsx("p",{className:"text-xs font-semibold uppercase tracking-widest text-muted-foreground mb-4",children:"Claude Code + MCP â agent in the loop"}),[["Explore","game_look_around, game_teleport"],["Identify","log issues + ideas to TASKS.md"],["Implement","edit source, run dev server"],["Verify","game_save_screenshot, compare"],["Commit","changelog entry, git push"],["Repeat","agent decides what's next"]].map(([s,t],a)=>e.jsxs("div",{className:"flex gap-3 items-start",children:[e.jsx("span",{className:"mt-0.5 w-5 h-5 rounded-full bg-foreground
1/10 flex items-center justify-center text-[10px] text-foreground shrink-0",children:a+1}),e.jsxs("div",{children:[e.jsx("span",{className:"font-medium text-foreground",children:s}),e.jsxs("span",{className:"text-muted-foreground",children:[" â ",t]})]})]},a))]})]})}function l(){return e.jsxs("div",{className:"my-8 border border-border/40 rounded overflow-hidden text-sm",children:[e.jsx("div",{className:"bg-muted/20 px-5 py-3 border-b border-border/40",children:e.jsx("span",{className:"text-xs font-semibold uppercase tracking-widest text-muted-foreground",children:"Sequential (old) â 35+ calls"})}),e.jsxs("div",{className:"bg-background p-4 overflow-x-auto",children:[e.jsx("div",{className:"flex items-center gap-1 text-[11px] font-mono text-foreground/60 whitespace-nowrap",children:["teleport A","look","screenshot","teleport B","look","screenshot","teleport C","look","screenshot","â¯"].map((s,t)=>e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx("span",{className:"px-2 py-1 bg-muted/60 rounded",children:s}),t<9&&e.jsx("span",{className:"text-muted-foreground",children:"â"})]},t))}),e.jsx("p",{className:"text-[11px] text-muted-foreground mt-2",children:"Each call requires agent inference before the next one fires. 35 calls à 3â5s = >2 minutes minimum."})]}),e.jsx("div",{className:"bg-muted/20 px-5 py-3 border-y border-border/40",children:e.jsx("span",{className:"text-xs font-semibold uppercase tracking-widest text-muted-foreground",children:"Batch capture (new) â 2 calls total"})}),e.jsx("div",{className:"bg-background p-4",children:e.jsxs("div",{className:"grid grid-cols-2 gap-3 text-[11px]",children:[e.jsxs("div",{className:"border border-border/40 rounded p-3 space-y-2",children:[e.jsx("p",{className:"font-semibold text-foreground",children:"Plan route a priori"}),e.jsx("p",{className:"text-muted-foreground font-mono",children:"[{x:0, z:14}, {x:18, z:38}, â¦]"}),e.jsx("p",{className:"text-muted-foreground",children:"Pure reasoning. 0 MCP calls."})]}),e.jsxs("div",{className:"border border-border/40 rounded p-3 space-y-2",children:[e.jsx("p",{className:"font-semibold text-foreground",children:"game_capture_frames(route)"}),e.jsx("p",{className:"text-muted-foreground font-mono",children:"â [frameâ, frameâ, â¦, frameâ]"}),e.jsx("p",{className:"text-muted-foreground",children:"All N frames back in one response."})]})]})})]})}function c(){const s=[{label:"Route A",name:"Exterior",desc:`street-level coverage 2building facades, scale, pedestrians`},{label:"Route B",name:"Interior",desc:`inside pub, R7, Harrods 3lighting, furniture, depth`},{label:"Route C",name:"NPC density",desc:`social spaces 4crowds, phrases, behaviour`},{label:"Route D",name:"Narrative",desc:`R7 desk, tube, Angel 5protagonist journey gaps`}];return e.jsxs("div",{className:"my-8 border border-border/40 rounded overflow-hidden text-sm",children:[e.jsxs("div",{className:"bg-muted/20 px-5 py-3 border-b border-border/40 flex items-center justify-between",children:[e.jsx("span",{className:"text-xs font-semibold uppercase tracking-widest text-muted-foreground",children:"Exploration mode â M routes à N frames"}),e.jsx("span",{className:"text-xs font-mono text-muted-foreground",children:"4 calls fired simultaneously"})]}),e.jsx("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-px bg-border/40",children:s.map((t,a)=>e.jsxs("div",{className:"bg-background p-4 flex flex-col gap-1",children:[e.jsx("span",{className:"text-[10px] font-mono text-muted-foreground",children:t.label}),e.jsx("span",{className:"font-semibold text-foreground text-sm",children:t.name}),e.jsx("span",{className:"text-[11px] text-muted-foreground whitespace-pre-line leading-relaxed",children:t.desc})]},a))}),e.jsx("div",{className:"bg-muted/20 px-5 py-3 border-t border-border/40",children:e.jsx("span",{className:"text-xs font-mono text-muted-foreground",children:"â MÃN frames returned together â analyze full set in one reasoning pass â findings logged â triage"})}),e.jsxs("div",{className:"bg-background px-5 py-3 border-t border-border/40",children:[e.jsx("span",{className:"text-xs font-semibold uppercase tracking-widest text-muted-foreground",children:"Verification mode â 1 targeted route"}),e.jsxs("p",{className:"text-[11px] text-muted-foreground mt-1",children:["After implementing: single ",e.jsx("code",{className:"bg-muted px-1 rounded",children:"game_capture_frames"})," call covering only the changed zone. Confirm it looks right. No parallel needed."]})]})]})}function h(){const s=[{label:"Explore",note:`game_look_around 6game_teleport`},{label:"Identify",note:`log to TASKS.md 7prioritise`}
7,{label:"Implement",note:`edit source files 8dev server hot-reload`},{label:"Verify",note:`game_save_screenshot 9compare visually`}];return e.jsxs("div",{className:"my-8 border border-border/40 rounded overflow-hidden text-sm",children:[e.jsx("div",{className:"grid grid-cols-4 gap-px bg-border/40",children:s.map((t,a)=>e.jsxs("div",{className:"bg-background p-4 flex flex-col items-center text-center gap-2",children:[e.jsx("span",{className:"w-7 h-7 rounded-full bg-foreground/10 flex items-center justify-center text-[11px] font-semibold text-foreground shrink-0",children:a+1}),e.jsx("span",{className:"font-semibold text-foreground",children:t.label}),e.jsx("span",{className:"text-[11px] text-muted-foreground whitespace-pre-line leading-relaxed",children:t.note})]},a))}),e.jsxs("div",{className:"bg-muted/20 px-5 py-3 flex items-center justify-between gap-4",children:[e.jsx("span",{className:"text-xs text-muted-foreground font-mono tracking-wide",children:"â screenshot feedback feeds back into Explore"}),e.jsx("span",{className:"text-xs text-muted-foreground font-mono tracking-wide hidden sm:block",children:"loop repeats autonomously â"})]})]})}const g=()=>e.jsxs(e.Fragment,{children:[e.jsx(n,{title:"I Asked Claude to Simulate My Life in London",description:"What happens when you give an AI agent first-person perception of its own 3D world. System design notes on Lovable vs Claude Code + MCP.",canonicalUrl:"/london/post",type:"article",publishedTime:"2026-03-13"}),e.jsx(i,{}),e.jsx("div",{className:"min-h-screen bg-background",children:e.jsx("div",{className:"mx-auto max-w-2xl px-6 py-16 pt-24",children:e.jsxs("article",{children:[e.jsx("div",{className:"mb-6 inline-block px-3 py-1 bg-amber-500/10 border border-amber-500/30 rounded text-amber-400 text-xs tracking-widest uppercase font-mono",children:"DRAFT [WIP]"}),e.jsxs("header",{className:"mb-10",children:[e.jsx("h1",{className:"text-3xl md:text-4xl font-semibold tracking-tight text-foreground leading-snug mb-5",children:"I Asked Claude to Simulate My Life in London"}),e.jsxs("div",{className:"flex items-center gap-4 text-sm text-muted-foreground flex-wrap",children:[e.jsx("span",{children:"By Will Thompson, Claude Code and Lovable"}),e.jsx("span",{children:"March 13, 2026"})]})]}),e.jsx("div",{className:"mb-10 -mx-6",children:e.jsx("video",{src:"/videos/2026-03-13/london-walkthrough.mp4",autoPlay:!0,loop:!0,muted:!0,playsInline:!0,className:"w-full"})}
9),e.jsx("div",{className:"border-l-2 border-foreground/20 pl-5 mb-10",children:e.jsxs("p",{className:"text-foreground/80 leading-relaxed",children:[e.jsx("strong",{children:"TL;DR"})," â I asked AI to build a playable 3D open world of my life in London. Phase 1 (Lovable) got the broad strokes right but couldn't improve details because it had no way to see what it built. Phase 2 (Claude Code + MCP) gave the agent first-person perception of the game â then the loop closed and the world started filling in on its own."]})}),e.jsx("div",{className:"mb-10 flex justify-center",children:e.jsx(r,{asChild:!0,size:"lg",children:e.jsx(o,{to:"/london",children:"Play London"})})}),e.jsxs("section",{className:"mb-14",children:[e.jsx("h2",{className:"text-xl font-semibold text-foreground mb-4",children:"The question"}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:"Could an AI build a simulation of my life in London? Not a map â a walkable open world. The pub near my flat, Harrods, Buckingham Palace, the DeepMind office. My dog."}),e.jsx("p",{className:"text-foreground/80 leading-relaxed",children:"The interesting thing wasn't whether it could. It was what happened when you changed how it verified its own work."})]}),e.jsxs("section",{className:"mb-14",children:[e.jsx("h2",{className:"text-xl font-semibold text-foreground mb-4",children:"The world"}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:"The sim has a specific protagonist: a DeepMind researcher, living in Angel, Islington. The game starts at their desk in R7 â DeepMind's office at King's Cross. From there, four side quests branch out across the city."}),e.jsx("div",{className:"border border-border/40 rounded-lg overflow-hidden text-sm mb-6",children:e.jsx("div",{className:"grid grid-cols-1 divide-y divide-border/30",children:[["London Fields","After-work pints with colleagues. A park, a pub, people with dogs, bikes leaned against the fence."],["Angel","Home. A flat above a café. Goose waiting at the door."],["Tower of London","A tourist landmark â Beefeaters, ravens, the Thames behind it."],["Hampstead Heath","Weekend walk with Goose. Wide open grass, kite flyers, a swimming pond."]].map(([s,t])=>e.jsxs("div",{className:"flex gap-4 px-5 py-4",children:[e.jsx("span",{className:"font-medium text-foreground w-36 shrink-0",children:s}),e.jsx("span",{className:"text-muted-foreground leading-relaxed",children:t})]},s))})}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:"Goose is a yellow Labrador. He's not a generic dog NPC â he's a named character who lives at specific places in the world. The agent knows to place him at Angel and Hampstead Heath, and to flag it as a gap when he's missing."}),e.jsx("p",{className:"text-foreground/80 leading-relaxed",children:"The world is London 2025, not a fantasy approximation of it. When the agent explores a zone and finds a medieval feast table, a lute-playing bard, or a wizard, it files those as bugs â not as charm. The narrative is the spec."})]}),e.jsxs("section",{className:"mb-14",children:[e.jsx("h2",{className:"text-xl font-semibold text-foreground mb-4",children:"What Lovable got right"}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:"The model derived almost everything from conversation. I described my life in London; it built locations I hadn't named. It placed a version of the DeepMind office without being told what it looked like. Pub NPCs arrived already named ALEX and MEEKO â unprompted. The world's broad shape was surprisingly accurate."}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-6",children:"The one thing I told it explicitly: I have a dog."}),e.jsx("img",{src:"/changelog/before-after/v2-lovable-aerial.png",alt:"Lovable-era world: pub entrance with ALEX and MEEKO name labels, broken triangles on ground",className:"w-full mb-2"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"The Lovable-era world at the pub entrance. ALEX and MEEKO labels visible inside â the model named them without being asked. Note the broken geometry on the ground: white triangles that had been there since the first build. I could see them. The model couldn't."})]}),e.jsxs("section",{className:"mb-14",children:[e.jsx("h2",{className:"text-xl font-semibold text-foreground mb-4",children:"What went wrong first"}
9),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-6",children:"The broad shape was right. Everything below the surface was wrong."}),e.jsxs("p",{className:"text-foreground/80 leading-relaxed mb-4",children:[e.jsx("strong",{children:"Walking through walls."})," The collision system was bolted on after the initial geometry. The first builds let you walk through every building. Harrods, the castle, the Palace â all solid-looking, all completely passable. The model had no way to discover this because it had never walked into one."]}),e.jsxs("p",{className:"text-foreground/80 leading-relaxed mb-4",children:[e.jsx("strong",{children:"Empty interiors."})," The structures were shells. The great hall of the castle had stone walls and nothing else. Harrods had three floors of floor plan and no departments. The Palace had a throne room with no throne. Walking in felt like walking into a box."]}),e.jsxs("p",{className:"text-foreground/80 leading-relaxed mb-4",children:[e.jsx("strong",{children:"Floating researchers."})," The R7 DeepMind office floor is on an upper level, reached by elevator. When the NPCs were first placed, their y-coordinate was set to 8.6 â correct for the office floor â but the actual mesh floor beneath them hadn't been rendered yet. They floated in mid-air above the lobby, facing slightly different directions, completely stationary."]}),e.jsx("img",{src:"/changelog/2026-03-15/researchers-floating.png",alt:"R7 researchers floating above the lobby with no floor beneath them",className:"w-full mb-2"}),e.jsx("p",{className:"text-sm text-muted-foreground mb-6",children:"R7 researchers placed at the correct y-coordinate before the floor mesh existed. They just hovered there, facing the wall."}),e.jsxs("p",{className:"text-foreground/80 leading-relaxed mb-4",children:[e.jsx("strong",{children:"Guards stuck in topology."}),` The castle and Palace perimeter guards spawned inside decorative bushes, clipped into walls, or half-submerged in the ground. The NPC walk logic ran fine â they were "moving" â they just weren't going anywhere because the surrounding geometry had absorbed them.`]}),e.jsx("img",{src:"/changelog/2026-03-15/guards-in-bushes.png",alt:"Castle perimeter guard half-clipped into a decorative bush",className:"w-full mb-2"}),e.jsx("p",{className:"text-sm text-muted-foreground mb-6",children:"A guard doing their duty, technically. The bush won."}),e.jsxs("p",{className:"text-foreground/80 leading-relaxed mb-4",children:[e.jsx("strong",{children:"The ducks."})," Every session that touched the pub or park resulted in more ducks. The first duck appeared legitimately â I have a dog, the model put a duck near the pond as something for the dog to chase. Then in a later session the agent swept the open water area, decided it looked sparse, and added more ducks. Then it added a fox. Then it decided the fox-duck tension needed a third duck to balance the scene. Then it added a whole family."]}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:"Nobody instructed any of this. The agent just kept finding the duck area and improving it, on its own terms."}),e.jsx("p",{className:"text-foreground/80 leading-relaxed",children:"There are currently 81 duck POV screenshots in the repo. No one instructed this either."})]}),e.jsxs("section",{className:"mb-14",children:[e.jsx("h2",{className:"text-xl font-semibold text-foreground mb-4",children:"The verification problem"}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:"The world had shape but the details were wrong. Castle interior: hollow. Harrods: no departments, no staff. Lighting: too dark. Geometry artifacts scattered across the ground. Every problem required me to walk into it, notice it, describe it in text, and wait."}),e.jsxs("p",{className:"text-foreground/80 leading-relaxed mb-4",children:["Lovable's response to this feedback was to build more scaffolding. It generated a Supabase Edge Function â 818 lines of server-side code â to assess game quality. It built a Kanban pipeline for tracking improvement ideas. An ",e.jsx("code",{className:"text-sm bg-muted px-1 rounded",children:"addressed_issues"})," table in the database. None of it could see the broken triangles or the empty room."]}
9),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-6",children:"Verification without perception is just bookkeeping."}),e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("div",{children:[e.jsx("img",{src:"/changelog/before-after/v2-lovable-street.png",alt:"Lovable era â broken geometry, dark lighting",className:"w-full"}),e.jsx("p",{className:"text-sm text-muted-foreground mt-1",children:"Lovable â artifacts, dark, old controls UI"})]}),e.jsxs("div",{children:[e.jsx("img",{src:"/changelog/before-after/v3-claude-street.png",alt:"Claude Code era â clean, NPC conversations",className:"w-full"}),e.jsx("p",{className:"text-sm text-muted-foreground mt-1",children:"Claude Code â fixed, bright, NPCs talking"})]})]})]}),e.jsxs("section",{className:"mb-14",children:[e.jsx("h2",{className:"text-xl font-semibold text-foreground mb-4",children:"Giving it eyes"}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:"The MCP server runs headless Chromium pointed at the game. It exposes tools the agent can call: teleport to a location, rotate the camera, take a screenshot, get nearby NPCs. The agent now inhabits the world it's building."}),e.jsxs("p",{className:"text-foreground/80 leading-relaxed mb-4",children:["I switched from Lovable to Claude Code, dropped the Supabase infrastructure, and replaced it with three agent skills â Explorer, Builder, Verifier â coordinated through a ",e.jsx("code",{className:"text-sm bg-muted px-1 rounded",children:"TASKS.md"})," file and a ",e.jsx("code",{className:"text-sm bg-muted px-1 rounded",children:"CLAUDE.md"})," that defines the workflow."]}),e.jsxs("p",{className:"text-foreground/80 leading-relaxed mb-6",children:["Lovable responded to the knowledge-persistence problem by building infrastructure: an 818-line Supabase Edge Function, a Kanban board, an ",e.jsx("code",{className:"text-sm bg-muted px-1 rounded",children:"addressed_issues"})," database table. Claude Code responded by writing files: ",e.jsx("code",{className:"text-sm bg-muted px-1 rounded",children:"CLAUDE.md"})," (behavioural spec), ",e.jsx("code",{className:"text-sm bg-muted px-1 rounded",children:"TASKS.md"})," (task backlog), memory files that persist across sessions, slash command skills that encode repeatable workflows. One system built plumbing around the problem. The other wrote things down."]}),e.jsx("pre",{className:"bg-muted/60 border border-border/40 rounded p-4 text-sm font-mono text-foreground/80 overflow-x-auto mb-4",children:`Claude Code âstdioâ MCP Server (Playwright) 10 â 11 localhost:8080/london 12 â window.__game 13 Three.js / React Three Fiber`})]}),e.jsxs("section",{className:"mb-14",children:[e.jsx("h2",{className:"text-xl font-semibold text-foreground mb-4",children:"The loop"}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:"The difference between the two systems is where perception lives."}),e.jsx(d,{}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-6",children:"In the Lovable loop, I was the sensor â the only way the system got visual feedback was through me describing what I saw. In the Claude Code loop, the MCP server is the sensor. The agent can walk around, look at what it built, and decide what to do next without me."}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-2",children:"The closed loop in detail:"}),e.jsx(h,{}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Each Verify step produces screenshots that feed directly into the next Explore. The agent isn't responding to instructions â it's responding to what it sees."})]}),e.jsxs("section",{className:"mb-14",children:[e.jsx("h2",{className:"text-xl font-semibold text-foreground mb-4",children:"What changed when it could see"}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-8",children:"The agent stopped waiting for instructions. Walking into the empty castle great hall it added a feast table, wall banners, and candles. Walking through Harrods it partitioned the floor into departments and added ornate pillars. It found the broken geometry and fixed it. The loop was the same â explore, identify, implement, verify â but the identifying was now happening autonomously."}),e.jsxs("div",{className:"grid grid-cols-2 gap-3 mb-8",children:[e.jsxs("div",{children:[e.jsx("img",{src:"/changelog/2026-03-13/post-castle-courtyard.png",alt:"Castle courtyard â servants and torches",className:"w-full"}),e.jsx("p",{className:"text-sm text-muted-foreground mt-1",children:"Castle courtyard â agents added after it walked in and saw nothing"})]}),e.jsxs("div",{children:[e.jsx("img",{src:"/changelog/2026-03-13/post-harrods-entrance.png",alt:"Harrods interior â pillars and staff NPCs",className:"w-full"}),e.jsx("p",{className:"text-sm text-muted-foreground mt-1",children:'Harrods interior â departments and staff, "The food hall is simply divine"'})]})]})]}),e.jsxs("section",{className:"mb-14",children:[e.jsx("h2",{className:"text-xl font-semibold text-foreground mb-4",children:"Perception grounded in perspective"}
13),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-6",children:"Once the agent had first-person tools, I extended them to the NPCs. The same tools that let the agent verify its own work â teleport to position, look around, screenshot â now let it see through any character's eyes. I called this NPC POV, and I expected it to be a verification tool: check sightlines, confirm NPCs aren't facing walls."}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-6",children:"It was more than that. The agent doesn't just verify from a perspective â it explores from one. Walking in as Meeko, a dachshund at ground level, surfaces different gaps than walking in as a six-foot player. The dog's eye view led to placing Big Ben at the south horizon â not instructed, it emerged from the constraint of being that close to the ground, looking for something on the skyline. The same scene from player height has Big Ben obscured by the pub."}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-6",children:"The subject of the perspective changes what gets built. Adele at the top of the castle looking out over the world she's at the centre of â the model had opinions about what she should see from up there. It added the R7 tower to the eastern horizon specifically because it was visible from the castle tower. Not from the ground. From there."}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-6",children:"This is different from standard screenshot verification, where you capture a scene to confirm it looks correct. Here, stepping into a specific vantage point is itself a design act. The model's decisions change based on who is looking and from where."}),e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("div",{children:[e.jsx("img",{src:"/changelog/2026-03-13/post-pov-meeko.png",alt:"Meeko the dog, looking toward Big Ben",className:"w-full"}),e.jsx("p",{className:"text-sm text-muted-foreground mt-1",children:"Meeko â ground level, facing Big Ben"})]}),e.jsxs("div",{children:[e.jsx("img",{src:"/changelog/2026-03-13/post-pov-adele.png",alt:"Adele from the castle tower",className:"w-full"}),e.jsx("p",{className:"text-sm text-muted-foreground mt-1",children:"Adele â castle tower, looking over the world"})]})]})]}),e.jsxs("section",{className:"mb-14",children:[e.jsx("h2",{className:"text-xl font-semibold text-foreground mb-4",children:"Extending the MCP layer"}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:"Once the agent could move through the world and see it, the next question was whether it could change it â not just edit source files, but act directly on the running sim. Three additions to the MCP toolset:"}),e.jsxs("p",{className:"text-foreground/80 leading-relaxed mb-4",children:[e.jsx("strong",{children:"Spawning."})," ",e.jsx("code",{className:"text-sm bg-muted px-1 rounded",children:"game_spawn_npcs"})," lets the agent drop multiple characters into the world at once â gentlemen, ladies, ducks, foxes, guards â at specified coordinates, with custom phrases. The NPCs behave identically to the hand-placed ones: they walk, pause, face the player, and respond to cheers. The agent used this to stress-test crowd density and to prototype new area layouts before committing them to source."]}),e.jsxs("p",{className:"text-foreground/80 leading-relaxed mb-4",children:[e.jsx("strong",{children:"Speed control."})," ",e.jsx("code",{className:"text-sm bg-muted px-1 rounded",children:"game_set_speed"})," sets a movement multiplier â 1Ã, 5Ã, or 10Ã. At 10Ã the agent can sweep across the full world map in a few seconds. At 1Ã it walks at normal pace for detailed inspection. Snapshot sampling scales with speed: when moving fast, the agent increases the fps parameter in ",e.jsx("code",{className:"text-sm bg-muted px-1 rounded",children:"game_joystick"})," proportionally so coverage per unit distance stays constant."]}),e.jsxs("p",{className:"text-foreground/80 leading-relaxed mb-4",children:[e.jsx("strong",{children:"Area-based culling."})," NPCs beyond 45 units from the player are now hidden and their per-frame update skipped entirely. On mobile, shadows are disabled and the render resolution is halved. The world is still fully loaded â no streaming â but the cost of traversing it is proportional to what's actually on screen."]}
13),e.jsx("p",{className:"text-foreground/80 leading-relaxed",children:"Together these make the MCP session faster and cheaper. The agent can do a 10Ã speed sweep to find empty areas, slow to 1Ã to inspect them, spawn test NPCs to check sightlines and spacing, and commit only what it decides to keep."})]}),e.jsxs("section",{className:"mb-14",children:[e.jsx("h2",{className:"text-xl font-semibold text-foreground mb-4",children:"What it should look like"}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:"The target aesthetic is GTA III / Vice City era â not photorealistic, but dense, atmospheric, and grounded. Every location should feel distinct. The pub should feel like a pub. R7 should feel like a glass office tower in King's Cross. Hampstead Heath should feel open."}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:"The current world is boxes with flat colours. Getting from here to there doesn't require changing the engine â Three.js is capable of the target quality. What it requires is working through the rendering stack in order of impact:"}),e.jsx("div",{className:"border border-border/40 rounded-lg overflow-hidden text-sm mb-6",children:e.jsx("div",{className:"grid grid-cols-1 divide-y divide-border/30",children:[["Post-processing","SSAO (ambient occlusion), bloom, tone mapping. One library install. The single biggest visual jump."],["Shadows","Buildings casting shadows on the ground. Stops everything looking like it's floating."],["Point lights inside buildings","Warm amber spilling from pub windows. Fluorescent white in R7. Makes the world readable at a glance."],["Fog","Thin distance fog. Makes the world feel larger and hides geometry edges."],["GLTF models","Replace box geometry with actual meshes for R7, the pub, and Harrods. Real facades, window geometry, architectural detail."],["PBR materials","Glass that reflects. Brick that has texture. Concrete that looks like concrete."]].map(([s,t])=>e.jsxs("div",{className:"flex gap-4 px-5 py-4",children:[e.jsx("span",{className:"font-medium text-foreground w-36 shrink-0",children:s}),e.jsx("span",{className:"text-muted-foreground leading-relaxed",children:t})]},s))})}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:"The question of whether to move to a different engine â Unity, Unreal, Babylon.js â comes up. For true GTA V quality, Unreal pixel streaming (rendering on a server, streaming frames to the browser) is the answer. But the cost is ongoing cloud spend and you lose the tight agent loop: the MCP server needs a running browser tab, not a streamed video feed."}),e.jsx("p",{className:"text-foreground/80 leading-relaxed",children:"The working theory is that post-processing + GLTF models gets us to GTA III quality, which is the right level for a London life sim running in a browser tab. If the world content justifies a heavier investment later, the migration path is there."})]}),e.jsxs("section",{className:"mb-14",children:[e.jsx("h2",{className:"text-xl font-semibold text-foreground mb-4",children:"The world doesn't have to run in real time"}),e.jsxs("p",{className:"text-foreground/80 leading-relaxed mb-4",children:["One thing that became clear when building the autonomous loop: the bottleneck isn't the game. ",e.jsx("code",{className:"text-sm bg-muted px-1 rounded",children:"game_teleport"})," is instant. ",e.jsx("code",{className:"text-sm bg-muted px-1 rounded",children:"game_get_world_info"})," returns every building, NPC, and landmark position in a single call. The bottleneck is the number of MCP round-trips â each one requires the agent to make a call, wait for the browser, get a result back, and decide what to do next. Seven zones at five calls per zone is 35 round-trips before you've started building anything."]}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:"The fix is to separate deciding from executing. Deciding where to look is cheap â it's pure reasoning. Executing the look is expensive â it's an MCP call. So do all the deciding first, then execute once."}
13),e.jsxs("p",{className:"text-foreground/80 leading-relaxed mb-4",children:["This is what ",e.jsx("code",{className:"text-sm bg-muted px-1 rounded",children:"game_capture_frames"})," enables. Instead of teleporting to each zone and waiting for a response before moving on, the agent plans a full route as a list of ","{x, z}"," waypoints â then fires a single call that visits every point and returns all frames together. The agent then analyzes the complete set in one reasoning pass."]}),e.jsx(l,{}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:"For exploration, the insight goes one level further. The goal isn't to see one route through the world â it's to see the world from multiple lenses at once: exterior/street-level, interior depth, NPC density, narrative completeness. Each lens catches different problems. The exterior pass sees scale issues and empty pavements that the interior pass misses; the narrative pass sees missing story beats that neither visual pass notices."}),e.jsxs("p",{className:"text-foreground/80 leading-relaxed mb-4",children:["The architecture for exploration is therefore M routes à N waypoints: plan four routes a priori, fire all four ",e.jsx("code",{className:"text-sm bg-muted px-1 rounded",children:"game_capture_frames"})," calls simultaneously in a single response, receive MÃN frames back, and analyze the entire set at once. Cross-referencing across routes matters â a dark exterior in Route A explains an empty interior in Route B."]}),e.jsx(c,{}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:"The distinction between exploration and verification is important. For verification â confirming that a specific change worked â a single targeted route is exactly right. You don't need four lenses on the area around the pub's entrance sign. You need two waypoints and one call."}),e.jsx("p",{className:"text-foreground/80 leading-relaxed",children:"A sweep that previously timed out at 40 minutes now completes in under 10. The world doesn't run faster. The agent just stops exploring it one call at a time."})]}),e.jsxs("section",{className:"mb-14",children:[e.jsx("h2",{className:"text-xl font-semibold text-foreground mb-4",children:"What the parallel sweep found"}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:"Once the 4-route architecture was running, the first sweep surfaced things the old single-route approach had missed entirely. Not because the world had changed â the same bugs were there. But because four lenses at once catch things one lens doesn't."}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:'The most obvious finding: 80 rubber ducks blanketing the main street. They were in every exterior frame of every route â on the pavement, the grass median, the approach to Harrods. The previous single-route sweep had noted "duck problem near pub" as a moderate issue. The 4-route cross-analysis made it undeniable: this was the single most visually dominant feature of the entire world, and it was wrong.'}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:"But the more interesting finds came from the routes that weren't looking for ducks. Route C (NPC density) caught a purple-dressed NPC floating several metres above the street â invisible from ground-level exterior shots but obvious from a density pass focused on character positions. Route A (exterior) caught Goose's name label rendering backwards, a mirrored billboard sprite that no interior or narrative pass would have hit. Route B (interior depth) confirmed the palace is pitch black â same emissive material bug as the castle had, not yet fixed."}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:'None of these were new bugs. They existed before the parallel sweep. The difference was coverage: a single route visits each zone once from one angle. Four routes visit the same zones from different perspectives and with different things to look for. The floating NPC was found by a route specifically asking "are there enough people and where are they?" â not a route asking "is the lighting correct?". The lens shapes what you see.'}
13),e.jsx("p",{className:"text-foreground/80 leading-relaxed",children:"The sweep fixed the duck problem (80 â 8, confined to a small park east of the pub) and logged the rest as backlog. Total cost: $0.75. The world gets a little more accurate each time the loop runs."})]}),e.jsxs("section",{className:"mb-14",children:[e.jsx("div",{className:"mb-6 inline-block px-3 py-1 bg-amber-500/10 border border-amber-500/30 rounded text-amber-400 text-xs tracking-widest uppercase font-mono",children:"COMING SOON"}),e.jsx("h2",{className:"text-xl font-semibold text-foreground mb-4",children:"We let it run for 24 hours"}),e.jsxs("p",{className:"text-foreground/80 leading-relaxed mb-4",children:["Once the loop was locked in â the explore â aggregate â auto-triage â implement â verify cycle described ",e.jsx("a",{href:"#the-loop",className:"underline underline-offset-2 hover:text-foreground transition-colors",children:"above"})," â we ran it unattended for 24 hours via ",e.jsx("code",{className:"text-sm bg-muted px-1 rounded",children:"scripts/perpetual-sweep.mjs"}),"."]}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:"No human in the loop. The agent explored every zone, identified what was wrong or missing, picked the top tasks by impact, implemented them, verified with screenshots, committed, and started again. Every sweep took 10â15 minutes. Every commit is in the log."}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4 italic text-foreground/50",children:"[ Results, screenshots, and SWEEP_LOG.md summary to be added after the run. ]"})]}),e.jsxs("section",{className:"mb-14",children:[e.jsx("h2",{className:"text-xl font-semibold text-foreground mb-4",children:"Where it is now"}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:"The loop is open-ended. Point the agent at an area of the world, tell it to explore and improve, and it runs. The changelog tracks what it did. Each session adds detail."}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:"The interesting question isn't whether AI can build a sim of your life. It's what the sim reveals about what the model thinks your life looks like â before you've corrected anything."}),e.jsx("p",{className:"text-foreground/80 leading-relaxed mb-4",children:"The full iteration history tracks what the agent did each session."}),e.jsxs("p",{className:"text-foreground/80 leading-relaxed",children:["The workflow has since been formalised into a single slash command â ",e.jsx("code",{className:"text-sm bg-muted px-1 rounded",children:"/game-sweep"})," â that runs the whole loop from a cold start: seven zone agents exploring in parallel, findings aggregated and triaged, build agents spawned with worktree isolation for each approved task, screenshots captured, changelog updated, and the change shipped. One command; the agent handles the coordination."]})]}),e.jsx("section",{className:"mb-16",children:e.jsxs("div",{className:"border border-border/50 rounded-lg p-6 bg-muted/10",children:[e.jsx("h2",{className:"text-lg font-semibold text-foreground mb-3",children:"Technical TL;DR"}),e.jsx("p",{className:"text-foreground/70 leading-relaxed mb-5 text-sm",children:"The design pattern here â agents that perceive before acting, accumulate skills across sessions, and verify through observation rather than instruction â has been independently arrived at by several research groups. In case you want the academic frame:"}),e.jsxs("ol",{className:"space-y-4 text-sm",children:[e.jsxs("li",{className:"flex gap-3",children:[e.jsx("span",{className:"text-muted-foreground shrink-0 mt-0.5",children:"1."}),e.jsxs("div",{children:[e.jsx("strong",{className:"text-foreground",children:e.jsx("a",{href:"https://arxiv.org/abs/2210.03629",target:"_blank",rel:"noopener noreferrer",className:"underline underline-offset-2 hover:text-foreground/60",children:"ReAct"})})," ",e.jsx("span",{className:"text-muted-foreground",children:"(Yao et al., ICLR 2023)"})," ","â interleaving reasoning traces with environment actions reduces hallucination and allows in-context error correction. The explore â observe â plan loop in this project is the same pattern."]})]}),e.jsxs("li",{className:"flex gap-3",children:[e.jsx("span",{className:"text-muted-foreground shrink-0 mt-0.5",children:"2."}),e.jsxs("div",{children:[e.jsx("strong",{className:"text-foreground",children:e.jsx("a",{href:"https://arxiv.org/abs/2305.16291",target:"_blank",rel:"noopener noreferrer",className:"underline underline-offset-2 hover:text-foreground
13/60",children:"Voyager"})})," ",e.jsx("span",{className:"text-muted-foreground",children:"(Wang et al., NeurIPS 2023, NVIDIA)"})," ","â an LLM agent in Minecraft that accumulates a persistent skill library across sessions, compounding capability without forgetting. Analogous to how ",e.jsx("code",{className:"bg-muted px-1 rounded",children:"CLAUDE.md"})," + ",e.jsx("code",{className:"bg-muted px-1 rounded",children:"TASKS.md"})," + memory files grow over time here."]})]}),e.jsxs("li",{className:"flex gap-3",children:[e.jsx("span",{className:"text-muted-foreground shrink-0 mt-0.5",children:"3."}),e.jsxs("div",{children:[e.jsx("strong",{className:"text-foreground",children:e.jsx("a",{href:"https://arxiv.org/abs/2304.03442",target:"_blank",rel:"noopener noreferrer",className:"underline underline-offset-2 hover:text-foreground/60",children:"Generative Agents"})})," ",e.jsx("span",{className:"text-muted-foreground",children:"(Park et al., UIST 2023, Stanford/Google)"})," ","â 25 agents in a Sims-like world with natural language memory, reflection, and multi-day planning. Closest prior work to the NPC autonomy questions raised here."]})]}),e.jsxs("li",{className:"flex gap-3",children:[e.jsx("span",{className:"text-muted-foreground shrink-0 mt-0.5",children:"4."}),e.jsxs("div",{children:[e.jsx("strong",{className:"text-foreground",children:e.jsx("a",{href:"https://arxiv.org/abs/2207.05608",target:"_blank",rel:"noopener noreferrer",className:"underline underline-offset-2 hover:text-foreground/60",children:"Inner Monologue"})})," ",e.jsx("span",{className:"text-muted-foreground",children:"(Huang et al., CoRL 2022, Google Robotics)"})," ","â closed-loop environment feedback fed back into LLM reasoning without retraining. The screenshot-as-feedback mechanism in this project is exactly this applied to a 3D virtual world."]})]}),e.jsxs("li",{className:"flex gap-3",children:[e.jsx("span",{className:"text-muted-foreground shrink-0 mt-0.5",children:"5."}),e.jsxs("div",{children:[e.jsx("strong",{className:"text-foreground",children:e.jsx("a",{href:"https://arxiv.org/abs/2406.10474",target:"_blank",rel:"noopener noreferrer",className:"underline underline-offset-2 hover:text-foreground/60",children:"SIMA"})})," ",e.jsx("span",{className:"text-muted-foreground",children:"(Google DeepMind, 2024â2025)"})," ","â Gemini-based embodied agent that learns to navigate 3D virtual worlds by self-directing tasks across multiple game environments. Most directly analogous to the full system described here."]})]})]})]})}),e.jsx("div",{className:"pt-8 border-t border-border/30 flex justify-center",children:e.jsx(r,{asChild:!0,size:"lg",children:e.jsx(o,{to:"/london",children:"Play London"})})})]})})})]});export{g as default};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.