Coastal City Sandbox — a third-person open-world game in one HTML file you can double-click, and a gate that failed for the wrong reason
A drivable procedural coastal city — 8x7 road grid, working traffic lights, four vehicles, 26 ambient cars and 46 pedestrians routing around you, wall-clock day/night, a courier loop, and every single texture drawn in code at runtime — as ONE self-contained HTML file with no npm, no Vite and no build step. Then an independent gate re-measured every claim the builder made. It came back 10/11, and the one failure was the gate's fault, not the app's: it probed for a clock at a name the app doesn't use. That was the seventh time in one session the test was the broken thing.
What this is
A third-person open-world city you can walk around, drive through, and run deliveries in — as a single self-contained HTML file. No npm, no Vite, no bundler, no build step. You double-click it and it runs.
There is a sunny coastal city on an 8×7 road grid: sidewalks, lane markings, working traffic lights, storefronts with signage and awnings, alleys, palms, streetlights, parked cars, a beach, a boardwalk and an ocean with an animated shader. Four drivable vehicles. Twenty-six ambient cars and forty-six pedestrians that route the grid and get out of your way. A wall-clock day/night cycle, a courier delivery loop, a minimap, Web Audio, and local-first saves with JSON export and import.
Every texture in it was drawn in code at runtime. There is not a single image file.
The constraint is the exercise
The familiar version of this prompt targets a Vite project: npm, a multi-file source tree, an asset pipeline. This one had to survive as one file you can email to someone.
That is not a simplification. It is a different engineering problem. With no asset pipeline there is nowhere to put a texture, so every surface — asphalt, lane markings, storefront glass, sand, palm bark, car paint — has to be generated into a canvas at runtime and uploaded. The art direction is constrained by what you can draw procedurally in a few milliseconds, which is a real design pressure and not a cosmetic one.
The whole world builds in 51.5 ms: textures 39.5, city geometry 6.7, actors 1.9, the rest materials and sky.
The gate failed, and the gate was wrong
The builder reported its own numbers, which is exactly the report you should not trust. So a separate acceptance gate re-measured all seven checks independently.
It came back 10 out of 11. The failure was on the most important check: is the simulation paced by the wall clock, or by the frame rate?
The app was fine. It exposes its clock as __gameDebug.tod. The gate had gone looking for __gameState.timeOfDay, found nothing, and correctly failed rather than skipping — but it was failing a correct implementation because the assertion was wrong.
This was the seventh time in a single session that a reported failure turned out to be the test rather than the code. It is the reason the first rule is: check the assertion before you fix the implementation.
Corrected, the check passes cleanly. Day-fraction advance measured at 1.851×10-3 per second unthrottled versus 1.856×10-3 under 8× CPU throttle — a ratio of 1.002. The world keeps the same time regardless of how fast it is drawing.
The best thing in the builder's report was a caveat
When it ran its own pacing test, CPU throttling barely moved the frame rate. A weaker report would have quietly published the clean-looking number.
Instead it proved the throttle was actually applied — a JavaScript benchmark went from 2.7 ms to 56 ms, a 20.7× slowdown — and then explained why the frame rate hadn't moved: under software rasterisation the raster threads dominate, and they are not covered by that throttle. So it re-ran the test with a 320 ms main-thread hog instead, collapsed the frame rate 8.95×, and got 1.0000 simulated milliseconds per real millisecond, drift 0.000%.
That is the difference between reporting a number and taking a measurement.
Seven bugs, all found by running it
None of these are visible by reading the source:
- Top-level
THREE.*access that ran before the dynamic import resolved. - A GLSL shader reading
.zoff avec2. - A sky dome placed outside the camera's far plane, so it simply wasn't there.
- Toggling
light.visibleforcing a shader recompile every frame. - Stale player speed carried over after exiting a vehicle.
- Twenty metres of position drift across a save and restore.
- Vehicles wedging permanently against geometry — fixed with displacement-based stall detection that folds the forward vector back to the distance actually covered, plus a yaw-escape that points the nose away after two seconds. Velocity-based detection does not catch this, because a wedged car is still trying to move.
Prompt
Act as an autonomous senior game developer. Build a complete, playable
third-person open-world driving-and-walking game and leave it finished.
Work autonomously: create the file, run it in a real browser, test it, fix
what you find, and deliver something playable. Do not stop after planning.
Do not hand me code snippets to assemble. Do not ask me questions you can
answer by making a reasonable decision.
HARD CONSTRAINT — this is the whole exercise: it must be ONE self-contained
.html file that runs by double-clicking it. No npm, no Vite, no build step,
no bundler, no separate .css or .js files. Three.js from a CDN is allowed
and expected; everything else is inline. Every texture must be generated in
code at runtime — no image files, no downloaded assets, nothing copied from
an existing game.
Build a small, dense, sunny coastal city that feels alive:
- City blocks with roads, sidewalks, intersections, lane markings, traffic
lights, storefronts, apartments, a few towers, alleys, palms, streetlights
and parked cars.
- A third-person character with walk, run, jump and a smooth follow camera
that handles obstruction without clipping into geometry.
- At least three visually distinct drivable vehicles. Enter and exit on foot.
Arcade handling — weight and slide, not a simulator.
- Ambient traffic and pedestrians that route around the city and react to
the player rather than standing still.
- A day/night cycle driven by the wall clock, not by frame count.
- One repeatable objective loop with a clear start, success and failure, and
a restart that actually resets state.
- HUD with a minimap, and audio generated with the Web Audio API.
- Local-first: the world, player position and progress survive a reload via
localStorage, with JSON export and import.
Performance is a feature, not a nice-to-have. Use instanced geometry for
repeated scenery, pool anything you spawn repeatedly, cull by distance, cap
the device pixel ratio, and keep shadows cheap. It must hold a steady frame
rate on an ordinary laptop, and it must degrade rather than die on a slow one.
Scope discipline: no crafting, no multiplayer, no skill trees, no story
missions. If a feature turns out unreliable, ship the simplest version of it
that genuinely works instead of a broken ambitious one.
Before you tell me you are done, actually verify all of this in a real
browser and report the measured numbers, not your expectations:
1. It boots to playable from a cold load with zero console errors.
2. Driving, walking, entering and exiting a vehicle all work repeatedly, not
just once.
3. The player cannot fall through the world or get permanently stuck in
geometry.
4. Reload restores the saved world, and export then import round-trips it.
5. Frame timing is measured over at least ten seconds and reported honestly.
6. The simulation is paced by the wall clock: throttle the CPU and prove the
world advances the same amount per real second at a different frame rate.
7. It is usable on a phone-sized viewport with touch controls, and every
interactive control has a hit area of at least 44 by 44 CSS pixels.
When you are finished, reply with only: a short summary, the controls, the
measured numbers from the checks above, and anything you deliberately left
out and why.
Paste this into Claude, Cursor, or Copilot. Change one thing that matters to you.
What I learned shipping it
- Why removing the build step is not a simplification but a different engineering problem: with no asset pipeline, every texture has to be drawn into a canvas at runtime, which changes what the art direction can even be
- How to prove a simulation is paced by the wall clock rather than the frame rate, and why CPU throttling can fail to demonstrate it when a software rasteriser's threads aren't covered by the throttle
- Why an acceptance gate should FAIL rather than skip when it cannot measure something — and why the first suspect when a gate fails is the gate
- How displacement-based stall detection (folding the forward vector back to the distance actually covered) fixes vehicles wedging on geometry, where velocity-based detection does not
- Why 'it boots' is a useless check and 'it boots, drives, exits, re-enters and drives again five times' is a real one