toastsim/OPUS-BUILD-BRIEF.md
monster 10adb268a0 M0: scaffold — Vite+TS+Three+Rapier, slice sim core, asset pipeline
The bread slice is the whole game's canvas, so it's built first and properly:

- Field: a 128x128 scalar field with bilinear sample, a mass-conserving brush,
  self-levelling relax, and the masked statistics the judge will read.
- Slice: extruded loaf silhouette (domed top), planar-projected UVs so the sim
  grid lines up with the bread, per-texel silhouette mask, and four fields
  (browning/dryness/spread/damage) packed into one RGBA8 texture per frame.
- Custom shader: browning ramp with blotchy char, crumb/crust/inclusions,
  spread wetting + opacity + thickness relief, gloss, gouge exposure, and
  flat data views for the judge screen.
- bread.ts / spreads.ts: the archetypes and the rheology the difficulty
  curve will live in.
- scripts/gen-assets.sh: reproducible fixed-seed asset generation through
  MODELBEAST (flux_local -> bg_remove_local -> hunyuan3d_mlx). All on-device.

Legibility work that mattered: butter rendered invisible at first. Fixes were
a gamma-style wetting darkening (a multiply *brightens* pale crumb), a normal
perturbation taken in UV units rather than raw texel deltas (~30x too small),
and pulling the lighting back under 1.0 so ACES stops desaturating yellow to
cream.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 21:16:18 +10:00

145 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# TOASTSIM — Build Brief
**For: Claude Opus 4.8, executing in Claude Code on this Mac (M3 Ultra, cwd `/Users/m3ultra/Documents/toastsim`).**
**Approved by the owner. Build the whole thing; commit and push per milestone. Asset generation is free and pre-authorized — see §3.**
---
## 1. What we're building
**TOASTSIM** — a browser game about making toast, where the *inputs are analog* and the *output is judged*. Two connected activities:
1. **The Toast Round** — toast bread (timing + power), spread it (pressure/angle physics per spread type), scrape burnt bits (risk/reward), get scored by a deadpan judge.
2. **The Cutlery Drawer** — a 3D physics minigame: a tangled drawer of *similar-but-not-identical* metal cutlery; fish out the specific requested item against a timer, **while your toast cools** (cold toast = hard butter = harder spread round).
Tone: dry, characterful, slightly absurd. PowerWash-Simulator-style satisfaction with a scoring screen that makes you go "one more toast."
**Arcade loop:** a customer order appears ("dark toast, thin vegemite, NO burnt bits") → drawer → toast → spread/scrape → judgment → next order, escalating.
## 2. Stack & repo
- **Vite + TypeScript + Three.js** for everything (both scenes 3D — the toast slice is a real GLB you paint on). **@dimforge/rapier3d-compat** for drawer physics. No React needed; DOM overlay for UI is fine. Target 60fps in Chrome.
- Repo: the folder is empty. `git init`, build in it, remote is `https://gitea.partly.party/monster/toastsim.git` (exists, empty). Push `main` after each milestone with a clear message.
- Create `.claude/launch.json` with the Vite dev server so the app is previewable via the Browser pane. Verify visually with screenshots as you go — don't trust "it compiles."
- Generated assets go in `public/assets/` (committed — they're small GLBs/PNGs and reproducibility matters less than a working repo).
## 3. Asset factory — MODELBEAST (free, local, pre-authorized)
This Mac runs MODELBEAST, a job server wrapping local ML tools. **You are explicitly allowed to use it. Everything below is $0 (on-device).** Full reference: `~/Documents/MODELBEAST/AGENTS.md` — read it before your first job.
### Auth & basics
```bash
cd ~/Documents/MODELBEAST
source data/agent.env # sets MB_HOST + MB_TOKEN (guest: local ops only, free by design)
./mb ops -v # live operator catalog — trust this over docs
```
If the server is down (`curl http://localhost:8777/api/health` fails): `~/Documents/MODELBEAST/scripts/serve.sh`.
Guest tokens **cannot** run paid cloud ops (server-enforced 403) — never try to work around that; the local chain below is the intended path.
### The two pipelines you'll use
**Prompt → 2D image** (UI art, judge portraits, backdrops, labels, title screen):
```bash
./mb run flux_local -p prompt="..." -p model=flux2-klein-4b -p steps=4 -p seed=7 --wait --download out/
```
Fixed seeds = reproducible. 1024×1024 default; set `width`/`height` for backdrops.
**Prompt → 3D GLB** (bread, toaster, cutlery, jars, plate):
```bash
# 1. generate a clean "product shot" image of the object
./mb run flux_local -p prompt="<object>, single object centered on plain light grey background, soft studio lighting, 3/4 view, stylized chunky game asset" -p seed=<n> --wait
# 2. cut it out (biggest quality lever for image→3D)
./mb run bg_remove_local --asset <IMG_ID> --wait
# 3. mesh it (GLB + PBR texture, ~4-5 min, no login needed)
./mb run hunyuan3d_mlx --asset <CUT_ID> -p octree_resolution=256 -p texture_size=1024 --wait --download public/assets/models/
```
`blender_convert` exists if you need format conversion. `mflux_image_edit` for prompt-based image fixes.
### Job etiquette (hard rules)
- **GPU jobs run one at a time** (serial lane). Queue asset jobs early, then *keep writing code while they run* — check with `./mb jobs` / `./mb log <ID> -f`. Never spam retries on `queued`.
- First run of an operator may download weights (1030 min). Be patient; don't cancel.
- Don't delete assets you didn't create. `./mb rm-asset` your own intermediates when done.
### Art direction & asset manifest
Pick ONE style token and use it in every prompt for coherence. Recommended: **"chunky stylized claymation kitchen, soft matte plastic look, warm colors"** — chunky geometry hides image→3D artifacts, reads well at game scale, and gives cutlery thicker (physics-friendlier) forms.
Generate (3D via the pipeline above, one flux seed noted per asset so it's re-runnable):
| Asset | Notes |
|---|---|
| Bread slices ×4: white, thick-cut sourdough, multigrain, raisin | These are the paint canvases — need a clear flat top face. If Hunyuan's top-face UVs are unusable for painting, **fallback: procedural slice** (rounded-box extrusion in Three.js) with a flux-generated bread-crust texture — the paint tech matters more than the mesh source. |
| Toaster (2-slot, retro) | Hero prop of the toast scene |
| Cutlery: butter knife, dinner knife, steak knife, spreader; dinner fork, dessert fork; teaspoon, dessert spoon, soup spoon | The "similar but not identical" cast. If thin tines/blades come out mangled from image→3D, **model cutlery procedurally** (extruded 2D profiles) — silhouettes must be hand-controlled anyway for fair gameplay. Decide after one test generation. |
| Butter dish, peanut-butter jar, vegemite jar (fictional brand: "MITEY") | Bench props / selection UI |
| Plate, kitchen bench surface | Bench = one big flux texture on a plane is fine |
| Judge portraits ×5 expressions (neutral, intrigued, impressed, disappointed, horrified) | 2D, flux + bg_remove. Same character every time: describe them exactly, fix the seed, vary only the expression phrase. A stern older health-inspector type with a clipboard works. |
| Title art, order-ticket paper texture, UI icons | 2D |
**Normalize every GLB on load in code** (bounding-box → unit scale, recenter pivot) — don't hand-fix meshes.
## 4. Game design spec
### 4.1 Toasting
- Browning is a **2D scalar field** (~64×64) per slice face, advanced each tick by heat input; render it by compositing into the slice's diffuse texture (white → golden → brown → black, with char speckle past 0.85).
- Heat input = toaster power dial (110) × coil proximity map (hotspots near coil lines, cooler edges, slight random per-run bias so no two runs identical).
- Per-bread params: `thickness` (thermal lag), `moisture` (delays browning then accelerates — sourdough), `sugar` (raisin browns fast and burns faster).
- Player controls: power dial + lever down; **no timer readout** — you judge by smell cues (text wisps: "smells warm… toasty… SMOKY") and can pop early anytime. Popping early and re-toasting is allowed but re-toasting browns unevenly (field already has gradient).
### 4.2 Spreading & scraping — the star mechanic
- Knife follows the mouse over the slice's top face (raycast → UV → paint into spread field, a second 2D field with `amount` per texel).
- **Scroll wheel = knife angle.** Flat (low angle) = wide contact, gentle pressure, spreads. Steep = narrow contact, high pressure: scrapes spread *off* — or drags burnt char off (reduces browning field where >0.85, spawns crumb particles). Too steep + too fast on clean bread = **gouge** (permanent damage decal, integrity penalty). One unified control, three behaviors.
- Dip into the butter/jar to load the knife (visible blob on blade, depletes as you spread).
- **Spread rheology** per type: `{ yieldPressure, viscosity, pickupAmount, tempSoftening }`
- **Butter**: high yieldPressure when cold — below it the butter doesn't spread, it *tears the bread* (drags the surface, integrity damage). `effectiveHardness = f(butterTemp, toastWarmth)`; toast warmth decays in real time (drawer dawdling matters). Soft butter = dream mode.
- **Peanut butter**: high viscosity, big pickup — goes on thick, hard to get even, drags.
- **Vegemite**: low viscosity, tiny correct dose — the skill is spreading then **scraping most of it back off**. Judge wants a translucent film; opaque = massive penalty ("This is a hate crime").
- Wrong tool from the drawer applies modifiers: spoon = blotchy coverage, fork = tears, steak knife = gouge-prone.
### 4.3 The drawer
- Rapier scene: drawer box, 1014 cutlery pieces spawned tangled (drop-settle at load, cached). Order card shows a **silhouette** of the required item.
- Mouse grabs via spring joint (raycast to nearest piece, joint to grab point); pieces clatter and snag realistically. Lift the right item fully clear of the drawer rim to win.
- Timer = your toast cooling. Grabbing the *wrong* item and committing (button) lets you proceed with the wrong-tool modifier — sometimes the smart play.
- **Colliders: hand-authored compound primitives** (capsules/boxes) per archetype, NOT trimesh — stability and speed. Visual mesh can be the generated GLB.
- Start with 8 pieces; only raise the count once tangling feels good.
- Metal-on-metal clatter sounds are non-negotiable for feel (see §4.5).
### 4.4 Judging
Score 010, itemized so the player sees exactly why:
| Criterion | Measures |
|---|---|
| Browning match | mean browning vs. order target |
| Evenness | browning field variance |
| Coverage | % of face with spread in the target thickness band |
| Uniformity | spread field variance |
| Char | texels >0.85 remaining (scraping fixed it? no penalty) |
| Integrity | gouges, tears, over-scrape |
| Order compliance | right spread, right amount class (thin/normal/thick) |
| Time bonus | small — never dominant |
Judge delivers 12 dry lines keyed to the worst and best criterion (write a bank of ~40; e.g. "The coverage is admirable. The crater is not."). Show the toast rotating on a pedestal with a heatmap toggle. Grade stamp (S/A/B/C/F). **This screen is the game — make it excellent.**
### 4.5 Sound & feel
- Web Audio, synthesized where possible: toaster tick, lever clunk, pop (with slice launch physics!), knife scrape (pitch ∝ pressure), butter-tear rip, cutlery clatter (Rapier collision events → filtered noise bursts, velocity-scaled), judge stamp thunk.
- Screen-shake on pop, crumb particles on scrape, steam wisps off warm toast.
## 5. Milestones (commit + push each; verify in browser before moving on)
- **M0 — Scaffold**: Vite+TS+Three+Rapier boots, launch.json, placeholder cube scene, pushed. *Also: fire off the first MODELBEAST asset jobs now so weights download while you build M1.*
- **M1 — Toasting**: procedural slice + browning field + toaster controls + pop. Playable: make toast of any darkness.
- **M2 — Spreading**: knife, angle control, butter + vegemite + PB rheology, scraping, gouges. This milestone is where the fun lives — iterate until dragging cold butter genuinely feels bad in a funny way.
- **M3 — Judging**: full rubric, judge lines, grade screen, "next order" loop with 5 handwritten orders.
- **M4 — Drawer**: physics drawer with 8 pieces, silhouette orders, spring grab, toast-cooling link, wrong-tool modifiers.
- **M5 — Content & escalation**: 4 breads, order generator with difficulty curve (day 1: butter on white → day 7: thin vegemite on warm crumpet-thick sourdough with a steak knife because the drawer's a mess), persistent day/score.
- **M6 — Art & polish pass**: swap placeholders for generated assets, judge portraits, title screen, sound polish, perf check (60fps with drawer active).
At each milestone: run it, screenshot it, check the console is clean, then commit. If a milestone's feel is wrong, fixing feel beats advancing.
## 6. Judgment calls you're pre-authorized to make
- Procedural vs. generated meshes per asset (test generation first, choose per §3 fallbacks).
- Any tuning of the rheology/browning constants — the spec's numbers are starting points.
- Cutting scope inside a milestone to protect feel (e.g., 2 spreads that feel great > 3 that don't).
- Do **not** add paid/cloud dependencies, accounts, or external services. Do not touch anything in `~/Documents/MODELBEAST` except via the `mb` CLI as documented.