Build a puzzle world with AI. Watch how it breaks. Fix it without losing the idea.
Try it: https://levelproof.vercel.app
Describe a small 3D puzzle world in plain language. A live model compiles your words into typed operations — the rooms, ramps, bridges, keys, switches, and doors that make the puzzle, plus the scenery that makes it look like what you said. Then a deterministic playtester explores every reachable state and answers three questions before you accept anything: Can it be won? Does it follow your rules? Can a player get stuck? When the answer is bad, you see the failure itself — a ghost replays the doomed route — and you get fixes that the engine has already checked.
Pick Blank canvas and describe a whole puzzle in one sentence, or press one of the build chips (Haunted forest keep, Frozen observatory, Pirate cove, Lava temple). While the model works you watch its plan and each operation stream in; the finished world appears as a preview you approve.
The kit is deliberately small and exact: a 16×16 grid, up to three floor levels, flat/ramp/bridge modules, up to 3 keys, 4 one-shot switches, and doors that require keys, require switches, or seal once a switch fires. Everything the kit cannot simulate is expressed as scenery: environments (meadow, forest, swamp, desert, snow, volcanic, cavern, sea, space, city), day/dusk/night lighting, five building styles, 29 procedural landmark props (a dragon, a throne, a fountain, braziers, water and lava tiles…), and key looks (torch, lantern, gem, crown, scroll, keycard…). The engine never reads scenery — a dragon never blocks a door — and the preview says so.
Play the level drops you into the world you just made: the camera eases in behind an animated adventurer and follows it as it runs, W / ↑ always walks away from you (controls are relative to the camera; drag to look around, Camera: overview to see everything), keys vanish into your inventory, doors open and seal as the engine says, and reaching the goal without breaking a rule ends in a Level complete card. Stuck? Hint (H) asks the checker for the next move of a shortest winning route from exactly where you stand, and the moment you walk into a dead end the panel says so — even while moves remain — because every continuation has already been explored.
Guards and timed gates — still proven. Worlds can have guards who pace a fixed beat, one room per turn, and gates that open on a schedule (the drawbridge is down two turns in four). Every move is a turn, and Wait a turn (Space) lets you let a guard go by. A red ring marks where each guard steps next; pips on a gate's lintel show its schedule. Walk into a guard (or trade places with one) and you are caught: back to the start, empty-handed. The checker explores every position at every turn of the cycle, so the same three answers still hold — and it is stricter: a spot where a guard would catch you whatever you did counts as getting stuck, so in an accepted world every capture was avoidable. Try The Sentry in the scene list.
An endless adventure the engine keeps honest. Win a level and press Continue the adventure (Surprise me, Harder, Easier, or say where next). The AI writes the next chapter — a new world that follows the story so far and adapts to how you played (hints, dead ends, restarts) — and the engine must prove it fair before you step in: winnable, nobody can get stuck, rules hold, and its shortest route inside the difficulty band the engine chose. The title card shows the proof: ✓ Verified solvable · N states checked · shortest route M moves. The chapter's story mentions only what the world contains: anyone it names is a character standing in it.
Characters who know the way. Ask for a merchant, a guide, a wise elder, or a robot helper and they stand in the world. Walk up and press Talk (T): the engine works out your next objective from exactly where you stand — the same search as Hint — and the AI says it in the character's voice. The server recomputes those facts itself and refuses a line that skips the objective or names anything that is not in the scene; the engine's own plain words appear at once and stay if the AI cannot answer, labeled. Characters are scenery: they block, trade, give, and open nothing.
Talk to the world while you play it. Found it too easy? Press Too easy — make it harder, Add a trap here (where you are standing), Add a secret room, Make it spookier, or just type what you want. The AI changes the level you are standing in, the engine checks the change before you see it (can be won · nobody gets stuck · rules hold), and Play the new version drops you straight back in. If every AI attempt still cannot be won, the engine offers its best checked repair of one instead, and says so (the AI's change could not be won, so the engine fixed it: …). Play → react in words → the world changes → play again. It is the same movement engine the checker explored — nothing the player can do was left unchecked. The verifier's replays use the same character as a translucent ghost: blue for a winning route, red for the route that fails.
On The Balcony Vault, press Key + locked door, apply it, then press The switch trap. The trap is legal but deadly: a player who presses the switch before taking the key seals the gallery door behind them. Can a player get stuck? turns to Yes, the stranded floors turn red, and Show the problem replays the exact verified route as a ghost and pauses on the decisive move. Why did this fail? asks the model to narrate the engine's own facts — the server recomputes the verdict first and checks every id the model writes against the scene.
Every proposal is checked by the engine before you see it; the preview card shows the engine pre-check next to the Apply button. When a build that only adds things cannot be won — the model locked the key behind its own door — the engine's concrete findings go back to the model once (recomputed on the server, never taken from the client) and the revision is shown with a disclosure of what the engine found. Edits that remove things are shown exactly as asked: breaking a level can be the point.
On a failing draft you get two kinds of repair:
- Ask the AI to fix it — the model proposes a fix that keeps your idea; the engine judges it before you see it.
- Find checked repairs — a deterministic search (≤ 24 candidates, ≤ 4 operations) over five template families: gate the bypass with a keyed door, relocate the trapping switch, move the key onto the shortcut, dissolve the trap, or move a key out from behind the door it opens. Every candidate is fully re-verified; repairs never weaken a rule, move the goal, or grant inventory. Among equally small fixes, the one with the longer winning route wins, so a challenge stays a challenge.
The model never emits raw scene JSON. It composes typed operations against an
authoritative scene summary — add/move/remove modules (including bridges
directly over lower corridors), lay out whole areas and corridors in one step
(a 4 × 4 courtyard, an eight-room gallery — so “make the biggest level you can”
comes back as a 70-room world in about 12 seconds, still fully checked), place keys and switches, set door conditions
(requiresKey, requiresKeys — every listed key — requiresSwitch,
closesAfterSwitch, and cycle for timed gates), add or remove guards with
a patrol route, move the spawn or goal, rename places, set scenery,
place landmark props and talking characters, and propose collectBeforeGoal, passThrough, and
switchNecessary design requirements (which you approve — the model cannot
weaken your rules). Ambiguous requests come back as one clarifying question;
mechanics the kit cannot simulate come back as an honest unsupported card
with alternatives.
Click anything in the scene — a floor, key, switch, door, guard, or landmark — and describe the change relative to it (“move this behind that door”). Select something you care about and press Keep these: proposals and repairs that would change it are refused.
- Can it be won? (solution) — is any goal reachable, with a playable route?
- Does it follow your rules? (design requirements) — does every winning route satisfy the active requirements? A keyless bypass alongside a keyed route still fails.
- Can a player get stuck? (recovery) — can every reachable non-goal state still win? Reverse search finds the dead ends and witnesses the earliest.
Exploration is exhaustive. The kit's limits (256 modules × 3 keys × 4
switches) cap the state space at 32,768 states, and the verifier's bound is
exactly that, so every check explores every state. Guards and timed gates
add the turn within the world's cycle to the state (the cycle is at most 12
turns), and a level is only valid if rooms × key sets × switch sets × cycle
still fits the same bound — so hazard worlds are exhaustive too. Being
caught restarts the level, and a restart never counts as a way out; the check incomplete
outcome exists only as a safeguard if the limits ever grow. Player, ghost, verifier, and repair search all call the same core
step() over the same catalog polylines; a witness only plays back if it
replays through that engine from the real initial state.
Compiles run through OpenRouter on google/gemini-3.8-flash with low
reasoning effort (fallback google/gemini-2.5-flash-lite). Measured on
2026-09-25 (small samples; see docs/model-eval.md):
- Direct evaluation battery (27 calls): 27/27 schema-valid, 26/27 semantically correct on the first try, 4/4 ambiguity and 2/2 rule-protection cases, median 4.7 s.
- The fixed 30-case suite plus 12 repeats against production after deploy: 42/42, median 4.5 s (41/42 on the local API the same day).
- Every example chip, twice each: 24/24 (prompt-11; the four blank-canvas builds re-checked on the final prompt-12: 4/4).
- Eight one-sentence worlds on the blank canvas: 8/8 valid on the first try,
7/8 winnable (the eighth is what the revision loop is for), typically
5–12 s. The previous model (
gemini-3.7-flash) took 20–33 s on the same prompts and produced 6/8 valid.
Latency varies with how long the model chooses to reason: most edits finish
in 3–12 s, and a large build occasionally takes 30 s or more, which is why the
plan and operations stream live. Identical requests — same level, prompt,
selection, kept objects, conversation, prompt version, and model
configuration — are served from an exact-match cache: in memory per server
instance and, when Upstash is configured, in a shared store for 7 days
(entries contain the prompt text). The UI always shows Fresh compile or
Cached result, with model, cost, and attempts in Generation details.
The example chips are prewarmed after each deploy (scripts/prewarm.ts).
Re-measured on 2026-10-24 after adding talking characters and the story rule
(prompt-16, adventure-4, npc-1; details in
docs/model-eval.md):
- Eight opening chapters: 8/8 fair; every role the prompt named was placed (5/5); every story named only characters that stand in the world (8/8); every character's line passed the server's checks (8/8, median 4.1 s).
- The judge battery: 17/18 builds winnable (median 7.5 s), 4/4 honest refusals, 4/4 edits, and reasonable answers to the judgement calls.
- Guards are the weak spot today: 1 of 6 guard and timed-gate builds was accepted (5 of 5 on Sep 29). The previous prompt scored the same today, so the model's behaviour changed. The commonest miss is a new key placed behind the door it opens; the repair search now fixes exactly that. A failing editor build stays a draft with its failure replayed (one click to the checked repairs); a change asked for while playing comes back repaired; adventure chapters are repaired too, and dismiss a guard that alone makes them unfair.
npm install
cp .env.example .env # add your OpenRouter key (and optionally Upstash)
npm run dev # Vite dev server; also serves /api/compile and /api/explain
npm run verify # typecheck, lint, 362 unit tests, production build
npm run test:e2e # Playwright browser journeys (fixture-backed, no model calls)Live-model scripts are opt-in, budget-capped, and stop on unknown cost:
npx tsx scripts/chip-battery.ts --confirm-live-ai --budget-usd 0.50 # every chip, graded
npx tsx scripts/build-battery.ts --confirm-live-ai --budget-usd 0.30 # one-sentence worlds
npm run eval -- --confirm-live-ai --budget-usd 0.40 --models google/gemini-3.8-flash
npx tsx scripts/prewarm.ts --confirm-live-ai --budget-usd 0.40 --url https://levelproof.vercel.app
npx tsx scripts/prop-gallery.ts forest night # share links showing every prop (visual QA)The deterministic core (src/core) has no DOM, renderer, React, or network
imports — the same engine runs in tests, the browser, and the serverless API.
The renderer adapts its quality on slow devices (resolution first, then
terrain shadows and particles). Details: architecture,
the movement model, and the
evaluation report.
Submission code is MIT. Built with Three.js, React, Zustand, Zod, Vite, and
Vitest (all MIT); typography is Space Grotesk Variable and IBM Plex Mono (SIL
OFL 1.1, via Fontsource). The playable character is "Characters Matt" by
Quaternius (CC0 1.0; see
public/models/LICENSE.md). Every other 3D asset
— terrain, trees, the dragon, the props — is built procedurally from Three.js
primitives. No AI-generated assets are used; the model only ever compiles
typed operations at runtime. Runtime services: the OpenRouter API (Google
Gemini models, see above), Upstash Redis for rate limits and the shared
cache, and Vercel hosting. Pre-window planning notes are private and excluded
from this repository.
AI-assisted code. Much of this code was written with AI coding assistants (Anthropic's Claude and Google's Gemini), directed, reviewed, and tested by the author, who ran and recorded every measurement in this repository.
Commit history. The first commit (ca78118, Sep 25) holds the work from
Sep 11 to Sep 25 in one piece; that period is recorded day by day, with its
measurements, in DECISIONS.md. Every commit since is a
single step.
Built for the AI Builder Hackathon 2026 (Victoria VR).






