Driven by the live fresh-eyes finding: a naive racer finishes with 0% paint — the core mechanic is currently skippable. G makes paint route-guaranteed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2.7 KiB
2.7 KiB
Lane H — Ghost replay + title screen
Owns: src/ghost/, src/ui/, demos/lane-h.html, src/demo/lane-h.ts.
Never touches: contracts, world, game, main, package.json, other lanes' dirs.
No new deps.
Mission
Two pieces that turn a tech slice into a GAME you return to: a ghost of your best run to race against, and a title card that explains the game in five seconds (and unlocks audio).
Deliverables
src/ghost/recorder.ts—GhostRecorder: onrace:started, record the blob root's position + uniform scale at 15Hz into a flat Float32Array-backed buffer (cap ~5 min). Onrace:finished, if this run is the new best, save quantized (int16 centimeters is plenty) + the time to localStorageblobbo:ghost(base64). Keep total under ~200KB.src/ghost/player.ts—GhostPlayer: if a saved ghost exists, onrace:startedspawn a translucent ghost blob (simple sphere + eyes is fine, opacity ~0.35, no physics — kinematic visual only) and replay it in race time with linear interpolation between samples. Despawn on finish/respawn. The ghost must be OBVIOUSLY not-you (ghostly white/desaturated, slight bob).src/ghost/install.ts—installGhost(world, blob)wiring both to events.src/ui/title.ts—installTitle(world): full-screen overlay on boot:- big BLOBBO wordmark (styled CSS bubble text is fine — thick white letters, rainbow paint-drip accents, Comic-adjacent but tasteful),
- one-line pitch: "Race. Get painted. Paint is power.",
- controls card: WASD/stick · Space/A jump · R/Start restart,
- a fat PLAY button. On click/keypress/gamepad-button: fade out,
emit
ui:playon world.events (audio unlocks on this), start allowed. - While Rapier/world is still booting, show the same overlay with a wobbling "loading…" blob dot instead of PLAY (installTitle can be handed a promise).
- Show saved best time + "👻 ghost ready" if
blobbo:ghostexists.
- Demo
demos/lane-h.html+src/demo/lane-h.ts: fake a 20s scripted "run" (a ball on a circle), record it, finish, then restart and race the ghost. Title screen in front. Debug key G wipes the saved ghost.
Acceptance
npm run buildpasses; demo shows title → play → run 1 records → run 2 shows interpolated ghost racing the circle; localStorage survives reload; wipe works.ui:playemitted exactly once per page life; overlay never reappears mid-game.- Ghost replay drift vs recorded path < one blob radius at all sample points (it's the SAME deterministic path in the demo — verify numerically).
- No edits outside owned paths.
Events
Consume race:started, race:finished {time,best}. Emit ui:play only.