Brainstorm mockup · no app code touched — every system below is a suggestion with exact numbers, demoed live. Serve the repo root (python app/serve.py 3000) — file:// + stale cache lies. All effects are synthesized DOM/WebAudio, zero assets, same philosophy as the shipped woody kit. Sound is on for the demos — buttons are the gestures.
Your mobile-game instinct, gated hard: a full-screen character cut-in fires only on committed sure kills. Because combat is deterministic, the client knows the kill before the animation — dmg = max(1, round(ATK × elemMult) − DEF), SUPER iff dmg ≥ hp.
| Case (real roster math) | dmg | vs hp | SUPER? |
|---|---|---|---|
| Queen 13⚔ vs Pawn 2❤/2🛡 | 11 | 2 | FIRES |
| Queen 13⚔ vs Tower 8❤/3🛡 | 10 | 8 | FIRES |
| Queen 13⚔ vs Legendary Tower 9❤/6🛡 | 7 | 9 | no — bounce |
| Same tower, fire attacker ×1.5 → round(19.5)=20−6 | 14 | 9 | FIRES — element planning flips it on |
| King attacks anything (9999⚔ classical pillar) | ∞ | — | always — Throne Duel finisher |
| AEGIS victim (e.g. god retinue queens) | dies, aura respawns | fires — body death is a kill; the respawn gets its own §4 ceremony | |
Frequency guard: SUPER only exists on lethal strikes, so it never interrupts quiet chess — most moves still play the shipped glide. Hover before committing shows the anticipation layer (red ☠ reticle = sure kill, gray = bounce), so the cut-in is a payoff, not a surprise tax.
The queen on d4 is yours (brass frame). Three targets marked: the pawn and tower are sure kills today; the legendary tower only becomes one when you switch the element to Fire. Hover a target to feel the reticle layer, click to commit.
| Dir | Look | Motion (×1) | Exit → impact | Sting (synth) |
|---|---|---|---|---|
| A · Divine Decree | Vertical obsidian banner wipes in from the left, gold edge; glyph emblem on a rotated diamond right; speed-line fan behind | banner clip-wipe 180ms in · hold 300ms · glyph scale 1.15→1.0 with 1-frame judder at 120ms | white flash 70ms → cut to board exactly on impact frame | noise riser 250ms → brass-weight thump 130→55Hz + shimmer ring |
| B · Manga Splash | Parchment panel rotated −7°, halftone dots, ink burst, huge italic stamp — comic-book kill | panel slides up 140ms · stamp slams scale 2.2→1.0 at 160ms with 2-frame hold · halftone scrolls | ink splash wipe 90ms → impact | riser → tighter crack burst (5200Hz transient) + low body |
| C · Ascension three.js-grade · approximated here in CSS 3D | Dark stage, rotating god-rays, emblem glyph on a 3D plane with parallax drift, chromatic-aberration title | plane descends translateY −60px→0 with rotateY drift 260ms · rays rotate 360°/1.4s · text chroma split settles | ray burst + flash 100ms → impact | riser → deepest thump (45Hz sub) + choir-ish stacked fifths shimmer |
Wiring (for the build): trigger computed where targetsFor/playableMove already evaluate resolveHit; sequence splices into animateMove (ui.js ~610) exactly where FX.bump starts today; state still mutates after animation in finishMove — the shipped pattern untouched. A reduce-motion / fast mode toggle ships with it (also honored for spectating).
The satisfying-shake ask, made mechanical. Define the overkill ratio r = dmg ÷ hp and pay out five tiers. The shake is a damped sine on the whole stage (camera feel, not glyph jitter): x(t) = A·e^(−t/τ)·sin(2πf·t + φ) with τ = 90ms, f = 20Hz, fresh random phase per axis per hit (never machine-gunned — same rule as the sound kit). Rotation rides along at 0.35× amplitude.
Try ATK 20 on the Knight (r = 18/5 = 3.6 → SAVAGE), then slide to 40 (r = 8 → ANNIHILATION), then drop to 8 (r = 1.6 → BRUTAL). Neighboring pieces jostle on SAVAGE+.
| Tier | r = dmg/hp | hit-stop | shards | board shake | tilt | zoom | extras |
|---|---|---|---|---|---|---|---|
| T0 · BOUNCE | < 1 (survivor) | — | — | glyph ±4px / 260ms (current) | — | — | slash + damage pop (current) |
| T1 · CLEAN CUT | 1 – 1.49 | 0ms | 12 | 2px / 120ms | 0° | — | dust ring, 8 motes |
| T2 · BRUTAL | 1.5 – 2.99 | 60ms | 16 | 5px / 220ms | 0.6° | — | ember ring at impact |
| T3 · SAVAGE | 3 – 5.99 | 100ms | 24 | 9px / 320ms | 1.2° | 1.02 | shockwave ring 1.4× square · 1 tile crack · neighbors jostle 3px |
| T4 · ANNIHILATION | ≥ 6 | 140ms | 34 | 14px / 420ms + 6px dip | 2.0° | 1.03 | white→element flash 90ms · 5 radial cracks · neighbors 5px · 45Hz sub-thump |
Hit-stop = the whole board holds one frozen frame at contact before the payout — the fighting-game trick that makes the release feel heavy. T4's dip drops the stage 6px on Y so the camera "feels weight". SUPER cut-ins stack on top (§1 plays, then the tier pays out).
Every hit (bounce and kill) bursts in the attacker's element, so the wheel becomes visible at board distance without touching the locked badge UI. Neutral/none keep today's white slash.
| Element | Burst (exact) | Decal |
|---|---|---|
| 🔥 fire | 12 embers (2.5–4px, orange→red, up-drift, 380ms) | scorch mark fading 2s |
| 💧 water | 10 droplets arcing down 320ms + splash ring 300ms | none (wet sheen 600ms) |
| 🌿 grass | 8 leaf shards spiraling out 420ms | none |
| ✨ light | 8 gold motes rising 400ms + 1 ray flash 120ms | none |
| 🌑 dark | 6 smoke puffs expanding/fading 500ms | none |
| ⚪ neutral | current crossed slash bars (kept as-is) | none |
PICKED 2026-09-13, with one refinement: the floating damage number also tints to the attacker's element (fire red, water blue, grass green, light gold, dark purple; neutral keeps the classic red) — try the buttons, the −9 follows now. Wiring: one elemBurst(div, elem) helper in fx.js, called from animateMove right where FX.slash fires today. Particle counts are small enough to stay DOM-cheap (≤12 spans); on the three.js board (§8 D) these become GPU particles.
Today a respawn is a text line. Proposal: the kill plays normally, then time reverses for the shards — 12 gold fragments fly inward, flash, and the body reforges on its origin square. The most-owned aura in the game should look like the chase it is.
| Beat | Timing |
|---|---|
| 1 · kill resolves (existing shatter) | ~340ms |
| 2 · 12 gold shards spawn on a 1.5× ring, converge inward | 260ms, ease-in |
| 3 · white-gold flash | 60ms |
| 4 · body pops scale 0.6→1.0 + ankh ring expands 1.5× + light pillar | 220ms |
| 5 · halo fades | 800ms |
Sealed-by-check stays the anti-ceremony: gray puff + the existing “✦ AEGIS shattered” line — the contrast teaches the rule. Trigger stays exactly as shipped (origin empty / not attacker path / not check-illegal); this is presentation only. Sfx: descending shimmer → knock → bright ring.
Check is the scariest moment in chess and today it's silent. Two additions, both quantified.
| Beat — DELIVER CHECK | Timing |
|---|---|
| king tile rim pulses red | 2 × 400ms |
| screen-edge vignette (25% red) in/out | 900ms |
| heartbeat — two 55Hz thumps | at 0 / 260ms |
| king glyph ember glow | 1.2s decay |
| Beat — FINAL-BLOW SLOW-MO | Timing |
|---|---|
| reserved for the capture that delivers checkmate (and Throne Duel's last hit) | — |
| capture plays at 0.35× time, zoom 1.04 toward the victim, slight desaturation | 700ms real |
| snap back to 1× → existing Triumph Stage dialog opens | — |
The demo board has a king on e2 (center). Slow-mo runs a full SAVAGE capture at 0.35× so you can read every shard.
Sieges are a core conquest outcome (they even hold stalemates at bay) but today the bounce is a soft slide. Proposal: the attacker still fails — but the defender blocks: spark, chips, braced stance, metallic clang.
| Beat | Timing |
|---|---|
| attacker slides to 80% and strikes | 150ms |
| 2 white spark crosses + 4 shield chips fly off the defender | 280ms |
| defender braces — pushed 2px away, springs back | 240ms |
| attacker recoil-arcs home (slight hop, not a slide) | 200ms |
Sfx swaps the soft knock for a high-Q metallic clang (3.2kHz bandpass ring + knock body). Applies to failed attacks only; check-holds-cancelled attacks stay silent-and-illegal as shipped.
For the one watching: a board that never fully sleeps. Everything here is ≤2px, phase-staggered so pieces never move in lockstep, and none of it touches hit-testing, plates, or the constant-height rule.
| System | Spec |
|---|---|
| breathing | translateY −1..0px sine, 1.5s alternate, random phase per piece; shadow syncs scaleX 1→0.9 |
| camera drift | stage translate ±3px, 8s ease-in-out loop — the “alive stream” feel |
| glide trails | 3 afterimage ghosts spawned 40ms apart along the path, opacity 0.25→0, 180ms life |
| landing dust | 6 motes (quiet move) / 10 (attack impact), 300ms |
A “reduce motion” master switch (OS-aware) kills breathing + drift together; trails/dust stay because they're event-driven, not ambient.
You said “probably three.js” — here's the honest cost/benefit ladder. The board is 32 meshes at most (trivial for WebGL), but the DOM board is also what every shipped screen already hangs off. The research file's Hearthstone split applies: 3D for the battlefield's juice, DOM for everything else.
| Rung | What you get | Cost / risk | Verdict |
|---|---|---|---|
| A · DOM + CSS polish everything in §1–§7 as demoed | SUPER cut-ins, full shake ladder, element bursts, reforge, slow-mo, idle life | zero deps · mobile-safe · ~2–3 sessions | ship first — every pick above works on this rung |
| B · CSS 3D tilt | board perspective + parallax layers; attacks tilt the table ~6° toward the action | small add-on · no WebGL · demo below | cheap wow, stacks with A |
| C · three.js FX overlay | transparent ortho canvas over the DOM board: GPU particles (hundreds), real god-rays, shockwave shaders, shake synced with the DOM stage | one dep ~150KB gz · falls back to A on WebGL fail · medium scope | the “stunning” backbone — adopt when §3/§4 particles feel too small |
| D · full three.js board | GLB pieces (existing render_pieces.py pipeline), lighting/shadows, true kill-cam camera orbits for SUPER, depth-of-field finishers | board render + interaction rewrite · old-phone testing · phase-3 per asset-research.md | the endgame — the SUPER direction C cut-in is its natural home |
Pieces sit at translateZ(22px), the banner decor at 46px — rotation exposes the parallax. In the app the tilt would bias toward the mover's side on attacks and settle back at 0° within 600ms.