Audit items 4 and 5. Both are shell behaviour a finished game is expected to have and this one
never did.
THE RUN NOW STARTS WHEN YOU START IT. cards.js has emitted `ui:start` on title dismissal since
round 1 and its own comment says nothing consumes it — so the simulation ran behind the title
card. Measured: 1.2 s of sitting on the title moved the ship and burned 1.2 s off the par clock
before the player had touched anything. L1's first checkpoint (s=20) and the opening comms line
both fired while the title was still up, and a hazard could hurt you before you started, which is
why cards.js already carries a player:damage auto-dismiss as a safety valve. Now `started` gates
step(); verified s stays at 2 across a second of real frames.
PAUSE ON BLUR. The ui:pause consumer has worked since round 2 and nothing but the pause key ever
produced for it. Alt-tab mid-fight and you came back to a dead ship. blur + visibilitychange now
produce it.
AUDIO HEARS THE PAUSE. engine.js handled eleven bus events and not that one, so the bed, drone,
grain scheduler and heartbeat ran at full level behind the pause card, and the heart slewed to
its resting 50 bpm while you read and snapped back on resume. Ducks the master rather than
suspending the context — the heartbeat's pump schedules against ctx.currentTime and would wake
up in the past.
FAILURE HAS A FACE. Every failure mode in this game presented as the same black rectangle, so a
player could not tell "loading" from "crashed" and had nothing to report:
- loadLevel did `return mod.getLevel(id)` without awaiting, so a rejection escaped its own
try/catch and killed module evaluation. A mistyped ?lvl= now falls back to the stub tube
(which names itself STUB ESOPHAGUS) instead of a dead page.
- frame() re-armed its rAF at the BOTTOM, so one throw anywhere stopped the loop forever while
the canvas kept showing the last good frame. Body is guarded; the loop always re-arms.
- bus.emit ran listeners bare, so a throw in one subscriber aborted the rest of the emit — and
player:state is emitted every frame from inside step(), so one bad frame in the HUD took
combat and audio down with it. Listeners are isolated now.
- webglcontextlost had no handler at all: a laptop sleeping left a white page with a floating
HUD.
All four now land on a FEED LOST card naming the cause, in the game's own voice.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|---|---|---|
| .claude | ||
| docs | ||
| pipeline | ||
| tools | ||
| web | ||
| .gitignore | ||
| README.md | ||
GUTS — a fantastic voyage through the alimentary canal
Browser flight-combat game (three.js, zero build step). You pilot ENDO-1, a microscopic probe, mouth → rectum through five anatomically-grounded biomes: ride peristalsis down the esophagus, cross the acid sea of the stomach, thread the villi forest of the small intestine, survive the gas swamps of the colon. Fantastic Voyage × Star Fox × Descent.
- Design: docs/GDD.md · Look: docs/ART_BIBLE.md
- Architecture + contracts: docs/TECH.md
- Asset generation: docs/PIPELINE.md
- How we work (lanes/rounds): docs/PROCESS.md — read this first if you are a lane
- Current round: docs/LANES/ROUND1_INSTRUCTIONS.md
Run it
cd web && python3 -m http.server 8140
# http://localhost:8140 — the game
# http://localhost:8140/?stub=1&dbg=1 — stub world + debug HUD (works from day 0)
No bundler, no npm, no build. ES modules + import map; three.js r175 vendored in web/vendor/
(never CDN — the game must run offline). Before you finish a session: bash tools/qa.sh.
The one-paragraph tech pitch
The whole canal is a spline. World position is (s, θ, ρ) — distance along the canal,
angle and radius in the cross-section. Tube walls are chunked extrusions along
parallel-transport frames; peristalsis and breathing are vertex-shader displacement
(cheap, squishy, everywhere); collision is an analytic radius test (no physics engine).
Caverns (mouth, stomach) switch the same controller to free 6DOF. Look is synthetic
scanner — dark endoscope void, SEM-style monochrome walls with FLUX-generated detail
textures, emissive-coded enemies. Everything is seeded + deterministic, and every asset is
optional at runtime — the game boots and plays asset-free with procedural fallbacks.
Hard laws (all lanes)
- Determinism. No
Math.randomoutsidejs/core/rng.js. Seeded RNG only. qa.sh greps. - Assets are optional. Missing texture/model/audio ⇒ procedural fallback, never a crash.
- Eyeball everything. Visual work isn't done until a screenshot is in
docs/shots/laneX/. - Perf budget: ≤300 draw calls, 60 fps on M-series / mid laptop. Measure via
window.DBG. - Free-first. Local MODELBEAST generation before any paid API; fal.ai is a gated fallback (John's go). See PIPELINE.md.
- Git: stage only your exact paths, commit + push each session, never
git add ..
Deploy
Live: https://partly.party/gutsy/ — bash tools/deploy.sh (QA gate → rsync web/ minus
dev/ → docker cp into forum-nginx → verify). No build step; web/ ships as-is.
Two things to know before you touch it, both documented at length in the script header:
- It's ephemeral. Content lives in the container's own layer (like blobbo/glytch), so a
docker compose up --force-recreateor nginx image bump wipes it — re-run the script. The durable fix is a bind mount inforum/docker-compose.yml(what cratewars/roguelike use), which costs a forum-nginx recreate and brief partly.party downtime. - A 404 there returns HTTP 200. nginx's catch-all
try_files ... /index.htmlserves the arcade index for any missing file. Verify on content, never on status codes.
Origin: ssh://git@100.71.119.27:222/monster/guts.git.