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

12 KiB
Raw Blame History

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

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):

./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):

# 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.