- Phaser 3.90 Matter, autoUpdate:false — deterministic stepSim(dt), harness-driven. - Render contract: raw Matter body = truth, flat black silhouette follows. - Rng (mulberry32) ported from toastsim; dev harness on window.__s. - Exit-bar measured: box drops y=80 -> settles 68 frames -> rests y=608.05, gap -0.05px flush on floor, landed:true. Verified in-browser + screenshot. - scripts/gen-assets.sh (2D-only flux via MODELBEAST) firing in background. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
217 lines
12 KiB
Markdown
217 lines
12 KiB
Markdown
# サンドニエット SANDONIETTE — Build Brief 1: The Puppet, The Food, The Samurai
|
||
|
||
## STATUS (update this as you go — a cold session reads here first)
|
||
- **M0 ✅** vite+ts+phaser(3.90)+Matter skeleton, typecheck green, harness (`window.__s`)
|
||
installed. Box drops from y=80, settles in **68 frames**, rests **y=608.05**
|
||
flush on the floor (gap −0.05px, `landed:true`). Verified in-browser + screenshot.
|
||
- Stack: Phaser Matter, `autoUpdate:false` — sim steps only via `stage.stepSim(dt)`,
|
||
so the harness drives frame-by-frame. Render contract: raw Matter body = truth,
|
||
flat silhouette `Prop.view` follows it (`Stage.addBody`). `Rng` = mulberry32.
|
||
- Assets: `scripts/gen-assets.sh` (2D flux only) firing on MODELBEAST →
|
||
`public/assets/img/`. Log: `~/.jobs/sandoniette-assets.log`. Game never blocks on art.
|
||
- **Next: M1** puppet (`sim/marionette.ts`) — the dangle.
|
||
- Run: `npm run dev` (:5173). Harness verbs live in `src/dev.ts`.
|
||
|
||
|
||
|
||
*"SHUBATTO! SANDO-NI-ETTE!!!" — read this file and execute it. It is written to
|
||
work cold: a fresh session with no other context should be able to build this
|
||
game from here. Sibling project: `~/Documents/toastsim` — do NOT touch it, but
|
||
DO steal its discipline (this brief tells you exactly what to steal).*
|
||
|
||
## 0. What this game is
|
||
|
||
A 2D shadow-puppet kitchen. You are a **silhouette marionette chef** operated
|
||
by strings — and only by strings. You cut comical food, stack it into
|
||
sandwiches, carry the dish across the stage, and serve it to your one
|
||
customer: a giant, permanently unimpressed **samurai**, glaring down at the
|
||
kitchen like a health inspector from hell. Do badly — drop things, mangle the
|
||
cut, stack a leaning tower, serve late — and his katana unsheathes a little
|
||
more. At maximum: **SHUBATTO** — one lightning slash, your strings are cut,
|
||
and the chef collapses into a floppy ragdoll amid flying ingredients. Restart.
|
||
|
||
The comedy IS the physics: puppeteer lag, pendulum swing, food with mass.
|
||
The difficulty is "cunty hard but fair" — Loot Cycle Inc. tension, not
|
||
Sekiro. Every failure must be readable as YOUR wobble, never RNG.
|
||
|
||
Fictional inspiration stack (for taste, not code): Loot Cycle Inc. (physics
|
||
triage under a ticking fail-state), Ratatouille (tiny chef, big kitchen),
|
||
1997 bargain-bin PS1 import energy (Pepsi Man voice, broken-Engrish UI).
|
||
|
||
## 1. The design law (inherited from TOASTSIM, adapted)
|
||
|
||
1. **One game, not minigames in a trench coat.** Cut, carry, stack, serve are
|
||
all the same verb — *move a pendulum with intent* — scored by the same
|
||
judge. A new mechanic must ride the string sim or improve it for everyone.
|
||
2. **The judge is the voice.** The samurai is toastsim's judge generalized:
|
||
named criteria with bands, a line bank with 8+ lines per criterion, the
|
||
scorecard as the emotional payoff. He speaks subtitled Japanese barks with
|
||
deadpan Engrish translations — *"SANDO... ga... yugande iru."* ("The
|
||
sandwich. It leans.")
|
||
3. **Physics is the comedian, the sim is honest.** No canned animations for
|
||
gameplay outcomes — if the tower falls, it falls because the center of
|
||
mass left the base. Comedy from real consequences, always.
|
||
4. **Nothing ships unverified.** The dev harness drives REAL input
|
||
deterministically and every milestone commits with measured numbers.
|
||
This is toastsim's single most valuable organ. Transplant it first.
|
||
5. **Failure must be readable.** The player must be able to say what they did
|
||
wrong from the wreckage alone. If a fail reads as random, it's a bug in
|
||
the DESIGN even when the code is correct.
|
||
|
||
## 2. Tech decisions (made — don't relitigate)
|
||
|
||
- **Vite + TypeScript + Phaser 3 (Matter physics build)**. One engine
|
||
dependency; Matter gives joints/constraints for the marionette and food.
|
||
Same repo shape as toastsim: `src/sim/` pure logic, `src/scenes/`
|
||
presentation, `src/dev.ts` harness, `npm run typecheck` must stay clean.
|
||
- **The strings are Verlet-ish chains of small Matter bodies + pin
|
||
constraints** (stiffness < 1 so they stretch visibly under abuse). If
|
||
Matter's constraint jitter fights back, fall back to pure-math Verlet
|
||
ropes rendered as polylines with only the ENDS as Matter bodies —
|
||
pre-authorized judgment call.
|
||
- **Silhouettes that carry physics are procedural polygons** (flat black
|
||
vector shapes with visible pin-joints, like Balinese/Japanese shadow
|
||
puppets). Generated art is for backdrops, food faces, the samurai, title —
|
||
never for collision shapes. (Toastsim law: silhouettes are gameplay.)
|
||
- **Determinism**: fixed-dt stepping (`app.step(dt)` equivalent — Phaser's
|
||
`update` driven manually in harness mode), seeded RNG only. No `Date.now()`
|
||
in sim code.
|
||
|
||
## 3. The bones to build (in order; each is a pure module + a scene + harness verbs)
|
||
|
||
### 3.1 `sim/marionette.ts` — the puppet
|
||
Classic marionette rig: a **control bar** (the player's proxy) + strings to
|
||
head, two hands, two knees. Mouse position = bar position; mouse tilt comes
|
||
from horizontal velocity; hold LMB = lower the bar (crouch/reach), release =
|
||
lift; RMB or Space = the GRIP (both hands clamp). That's the whole control
|
||
surface — depth comes from the pendulum, not the input map.
|
||
- Puppet body: 9 segments (head, torso ×2, upper/lower arms, thighs/shins as
|
||
single limbs to start), pin joints with angular limits.
|
||
- **The lag is the game**: strings transmit force with ~0.15s of felt delay
|
||
(stiffness/damping tuned, not a literal buffer). Walk = rhythmic bar sway
|
||
that gets the legs swinging. It must feel like Getting Over It's honesty
|
||
with Octodad's dignity.
|
||
- Harness: `__s.bar(x,y)`, `__s.sway(freq, amp, secs)`, `__s.grip(on)`,
|
||
`__s.pose()` → joint angles, `__s.settle()` → frames-to-rest.
|
||
- **Exit bar (M1)**: pendulum settle time measured; a scripted sway walks the
|
||
puppet 6 stage-units without face-planting; grip picks a box off the floor
|
||
10/10 times in harness. Feel: dangle a hand over a table edge — it should
|
||
make you smile before any food exists.
|
||
|
||
### 3.2 `sim/food.ts` + cutting
|
||
Food = 2D convex-ish polygon bodies with mass, friction, a `face` sprite,
|
||
and toastsim-style material params: `skin.resistance`, `flesh.moisture`
|
||
(wet food SPLATS — a 2D mess decal + slippery floor patch), `wobble`
|
||
(jelly-class food jiggles as soft constraint clusters).
|
||
- **The cut**: cleaver in gripped hand; a cut commits when blade-edge speed ×
|
||
angle-through-food crosses threshold (a slow push SQUASHES — moisture
|
||
splats, no cut). Pieces are real bodies with the parent's face split
|
||
across them. Cut quality = blade angle + speed + follow-through, scored as
|
||
**clean / ragged / crime** (crime = squashed).
|
||
- Harness: `__s.spawnFood(id, x)`, `__s.swing(from, to, secs)`,
|
||
`__s.cutStats()` → pieces, area CV (toastsim's pieceCV, straight port of
|
||
the idea), splat mass.
|
||
- **Exit bar (M2)**: a practiced harness swing produces 2 clean pieces with
|
||
area CV < 0.1; a slow push produces 0 pieces + splat > 0; a wild fast
|
||
swing sends a piece flying offstage (and that's CORRECT).
|
||
|
||
### 3.3 `sim/stack.ts` — the sando
|
||
Sandwich = ordered layers stacked by carrying pieces onto a plate zone.
|
||
Stability is honest Matter stacking, but SCORED: center-of-mass drift per
|
||
layer, overhang, lean angle. The order ticket names the target
|
||
(bread-filling-bread minimum; later: the katsu sando, the "tower" specials).
|
||
- Harness: `__s.stack()` → layers, lean°, COM drift; `__s.nudge(force)` for
|
||
the earthquake test.
|
||
- **Exit bar (M3)**: 3-layer sando stands; lean° reported; nudge test
|
||
topples a bad stack and not a good one; toppled food splats/rolls.
|
||
|
||
### 3.4 `sim/samurai.ts` — the judge + the fail state
|
||
The anger meter is toastsim's judge wearing armor. **Anger events**: drop
|
||
(+small), splat (+medium), crime-cut (+medium), served-wrong (+large),
|
||
served-late (+ramps), offstage food (+large). **Cooldown**: a clean serve
|
||
sheathes a little. Meter states = katana unsheath stages (0/25/50/75%:
|
||
eyebrow → hand-on-hilt → 10cm of blade → full glare, screen vignettes).
|
||
- **THE STRING CUT**: at 100% — freeze 300ms, slash line across the screen,
|
||
ALL string constraints deleted in one frame, ragdoll collapse, ingredients
|
||
launched with the impulse of the slash, gong. Score card over the wreckage.
|
||
It must be fast, unfair-FEELING but earned, and funny every single time.
|
||
- Serve = dish carried into the counter zone + a bow (hold still 0.5s — the
|
||
dwell commit, straight from toastsim).
|
||
- Scorecard groups: **THE CUT / THE SANDO / THE SERVICE / THE FLOOR** (the
|
||
floor = mess+drops; the bench criterion reborn). 8+ lines each, bilingual
|
||
bark + Engrish subtitle. Grade S–D; S = "…umai." and a single approving nod.
|
||
- **Exit bar (M4)**: full loop playable — order in, cut, stack, carry, serve,
|
||
scored; harness plays a clean run (B or better) and a disaster run (strings
|
||
cut) deterministically; numbers in the commit message.
|
||
|
||
### 3.5 Orders, days, escalation (M5)
|
||
Ticket rail like toastsim's days: Day 1 = one open sando. Escalate
|
||
ingredient count, wetness (tomato before you've earned it is cruelty),
|
||
required cuts, and the samurai's patience ramp. **One new pressure per day,
|
||
never two.** Day N bar: each day introduces its thing and is beatable by the
|
||
harness with a scripted-but-honest run.
|
||
|
||
### 3.6 The juice (M6)
|
||
Title screen: giant brush-stroke 「サンドニエット」, the voice line (text
|
||
crawl + gong if no audio yet: **SHUBATTO!! SANDONIETTE!!!**), attract-mode
|
||
ragdoll. Shamisen sting + slash whoosh + wet splats (Web Audio, synthesized
|
||
or CC0 — no paid assets). Unlockable strings as difficulty modifiers —
|
||
**rubber** (bouncy, +lag +comedy), **chain** (heavy, slow, precise), **silk**
|
||
(tighter, for cowards) — pre-authorized to defer if M5 lands better without.
|
||
|
||
## 4. Assets — MODELBEAST, 2D ONLY (this game needs no meshes)
|
||
|
||
Steal `scripts/gen-assets.sh` from toastsim as the template, keep only the
|
||
flux path (`run_flux` + `run_cutout`). **Machine facts current as of
|
||
2026-07-17** (verified the hard way): client lives at `~/MODELBEAST` (NOT
|
||
~/Documents), token is `MB_TOKEN` in `~/Documents/backnforth/.env`, export
|
||
`MB_HOST=http://100.89.131.57:8777` (m3ultra queue). Node m4's venvs are
|
||
dead — every stage retries up to 10× with 60s pauses until a healthy node
|
||
takes the job; keep that loop until told m4pro is healed.
|
||
|
||
Style token (fix it, reuse it everywhere):
|
||
`"japanese shadow puppet theatre, flat black silhouette with cut-out
|
||
details, warm washi paper backdrop, edo period, high contrast, hand-crafted
|
||
paper texture"`.
|
||
|
||
Batch 1 (fixed seeds, start at 301): washi/shoji stage backdrop, wooden
|
||
stage floor boards, the samurai in 5 anger states (SAME seed, expression
|
||
phrase varies — the toastsim judge-portrait trick), title brushwork art,
|
||
paper order tickets, food FACES (bread, tomato, cheese, katsu, egg —
|
||
comical, slightly alarmed), splat decals, the katana slash frame.
|
||
Fire asset jobs FIRST, build procedural while they render, `loadProp`-style
|
||
catch-fallbacks so the game never blocks on art.
|
||
|
||
## 5. What to steal from toastsim, literally
|
||
|
||
Open these files and port the PATTERN (not the code — different engine):
|
||
- `src/dev.ts` — the harness: real input driven deterministically, helpers
|
||
returning stats objects, `window.__s` installer. THE most important file.
|
||
- `src/sim/cutting.ts` — pieceCV and the cvWord ladder; material params.
|
||
- `src/game/judging.ts` + `lines.ts` — criteria bands, weighted groups, the
|
||
line bank shape and its voice discipline.
|
||
- `OPUS-BUILD-BRIEF-2.md §0` — the verify discipline and "known traps" habit:
|
||
keep a STATUS section at the top of THIS file updated as you go, so any
|
||
session can pick up cold.
|
||
|
||
## 6. Milestone order and the bar
|
||
|
||
M0 bootstrap (vite+ts+phaser+harness skeleton, typecheck green, a Matter box
|
||
drops onto the stage floor) → M1 puppet → M2 cutting → M3 stacking → M4
|
||
samurai + full loop → M5 days → M6 juice. One commit per milestone, message
|
||
= what you measured. Screenshots when visuals change. If a feel is wrong,
|
||
fixing feel beats advancing — the three feels that must be RIGHT: **the
|
||
dangle** (M1), **the clean chop** (M2), **the string cut** (M4).
|
||
|
||
Pre-authorized judgment calls: all tuning constants; Verlet fallback (§2);
|
||
segment count on the puppet; deferring string unlockables; cutting a food
|
||
type that won't behave. Do NOT add: paid/cloud dependencies, accounts,
|
||
telemetry, or 3D. Do not touch `~/Documents/toastsim` or `~/MODELBEAST`
|
||
except through `mb`.
|
||
|
||
The bet: toastsim proved the bones (harness, honest sim, judge voice, free
|
||
assets) make a game feel handmade and alive. Sandoniette bets the same bones
|
||
survive transplant into slapstick — that "verified physics comedy" is a
|
||
genre, and we can build it in milestones without ever shipping an unfunny
|
||
fall or an unfair death.
|