Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
148 commits
Select commit Hold shift + click to select a range
1c019ed
docs(hands): add realism overhaul plan and baseline findings
user1303836 Sep 29, 2026
20d2fe7
feat(hands): facing-relative combat, buffered combos, continuous rend…
user1303836 Sep 29, 2026
e277e96
feat(hands): runtime pose system on the Texel skeleton
user1303836 Sep 29, 2026
2e52086
feat(hands): object-space injuries, instanced blood, baked gore, broa…
user1303836 Sep 29, 2026
895dcbf
feat(hands): layered impact audio, hit flash, knockout slow motion, H…
user1303836 Sep 29, 2026
aa84a9c
feat(hands): touch controls, JPEG fighter textures, off-white canvas
user1303836 Sep 29, 2026
f2173cb
fix(hands): tighten touch pad cluster and mount it in the dev fixture
user1303836 Sep 29, 2026
ec544a9
feat(hands): adaptive render resolution and crowd reactions
user1303836 Sep 29, 2026
619614a
feat(hands): walk to the corners and sit on stools between rounds
user1303836 Sep 29, 2026
57d9365
feat(hands): let the knockout play out before the result panel, winne…
user1303836 Sep 29, 2026
d1683b6
feat(hands): portrait framing, compact scoreboard, round punch stats
user1303836 Sep 29, 2026
acd497c
feat(hands): ropes flex under a pinned fighter, referee wave-off, nar…
user1303836 Sep 29, 2026
1fbec96
feat(hands): face-down knockdowns after hooks
user1303836 Sep 29, 2026
7dfc8a3
docs(hands): record delivery status in the realism plan
user1303836 Sep 29, 2026
25c5f0a
chore: clear dependency audit findings
user1303836 Sep 29, 2026
f607921
fix(hands): survive slow clients; local two-player harness without Di…
user1303836 Sep 29, 2026
0a32862
test(hands): scripted two-player scenarios; celebration outlasts the …
user1303836 Sep 29, 2026
32f918b
test(hands): mash and latency scenarios for the two-player driver
user1303836 Sep 29, 2026
6e3ba7d
feat(hands): slow-motion knockout replay before the result
user1303836 Sep 29, 2026
65c22da
fix(hands): keep the result on screen behind real latency; measure in…
user1303836 Sep 29, 2026
b620af4
test(hands): renderer mock gains setInputLatency
user1303836 Sep 29, 2026
4204093
feat(hands): impact dent at contact; soak scenario
user1303836 Sep 29, 2026
b510b87
test(hands): measured-pace soak and backgrounded-tab scenarios
user1303836 Sep 29, 2026
20ff9c9
feat(hands): referee clinch-break gesture and the ten-second clapper
user1303836 Sep 29, 2026
e9434ca
feat(hands): rematch from the result screen
user1303836 Sep 29, 2026
3675de6
feat(hands): unanimous, split and majority decisions on the result panel
user1303836 Sep 29, 2026
11ce862
feat(hands): cornermen work the corners between rounds
user1303836 Sep 29, 2026
1161fd1
fix(hands): idle scene before the first snapshot; low quality tier
user1303836 Sep 29, 2026
36a3e80
fix(hands): punches retract straight to the guard during recovery
user1303836 Sep 29, 2026
484bf0d
docs(hands): frame-strip motion review script
user1303836 Sep 29, 2026
5a8e0a4
feat(hands): heavier body-shot fold and a guard that gives on head hits
user1303836 Sep 29, 2026
f9e9c0e
feat(hands): shadow boxing and a controls hint while waiting for an o…
user1303836 Sep 29, 2026
aeceaf3
feat(hands): glove touch before the bell; bout punch totals on the re…
user1303836 Sep 29, 2026
afc03ed
feat(hands): round callout at each round start
user1303836 Sep 29, 2026
80bcfa3
feat(hands): broken nose under heavy head trauma; close-up on the bea…
user1303836 Sep 29, 2026
522beba
feat(hands): plain-language failure messages
user1303836 Sep 29, 2026
f239066
test(hands): rematch loop scenario with GPU object counts
user1303836 Sep 29, 2026
0160ef5
docs(hands): describe the rematch loop scenario
user1303836 Sep 29, 2026
e66a4fb
feat(hands): copyable diagnostics in the settings panel
user1303836 Sep 29, 2026
8c62a16
fix(hands): diagnostics copy falls back to selecting the text
user1303836 Sep 29, 2026
1925493
feat(hands): glove trails on fast punches
user1303836 Sep 29, 2026
cfc9412
feat(hands): real clinch tie-up
user1303836 Sep 29, 2026
1d0b967
test(hands): real-stack clinch scenario
user1303836 Sep 29, 2026
53deda2
fix(hands): rendered root follows at a fixed speed per second
user1303836 Sep 29, 2026
92ace92
feat(hands): legible showboat taunt
user1303836 Sep 29, 2026
1ac20f6
feat(hands): cutman works the corner between rounds
user1303836 Sep 29, 2026
0f2bbcf
docs(hands): bring the realism plan status up to date
user1303836 Sep 29, 2026
caec08b
fix(hands): scoreboard names shrink before they truncate
user1303836 Sep 29, 2026
1b9d96e
feat(hands): crowd reacts to a taunt
user1303836 Sep 29, 2026
79738e5
refactor(hands): dispose corner shirts independently of the figures
user1303836 Sep 29, 2026
0ba01e5
test(hands): masher also hammers clinch and taunt
user1303836 Sep 29, 2026
af0f813
feat(hands): referee steps in for the clinch
user1303836 Sep 29, 2026
52479ab
feat(hands): clinched fighters keep working for position
user1303836 Sep 29, 2026
91287f0
feat(hands): failed clinch attempts get a miss cue
user1303836 Sep 29, 2026
2ca5053
chore(hands): three 0.186.1 and matching types
user1303836 Sep 29, 2026
7386700
test(hands): scenarios can render on the real GPU
user1303836 Sep 29, 2026
6e8cdc3
fix(hands): finish presentation and HUD overlaps found on real frames
user1303836 Sep 29, 2026
eca347e
fix(hands): countdown and foul panels no longer cover the fighters
user1303836 Sep 29, 2026
2b89253
feat(hands): broadcast corner shots between rounds
user1303836 Sep 29, 2026
318f6d3
fix(hands): corner posts stop a cornered fighter
user1303836 Sep 29, 2026
2d5b831
fix(hands): tight cameras on portrait phones and a replay that follow…
user1303836 Sep 29, 2026
62a2f3e
chore(hands): fixture takes a blood level
user1303836 Sep 29, 2026
58062d3
fix(hands): round clock holds through counts and timeouts
user1303836 Sep 29, 2026
b7e8d37
fix(hands): result panel text fits the panel
user1303836 Sep 29, 2026
aa09710
fix(hands): own punches never rewind and stepping out of a punch is i…
user1303836 Sep 29, 2026
e25ad9c
fix(hands): fast mashing can no longer get a player thrown out
user1303836 Sep 29, 2026
3d3e968
fix(hands): address the client review of prediction, replay and overlay
user1303836 Sep 29, 2026
c76e781
fix(hands): address the engine and server review of the corner limit …
user1303836 Sep 29, 2026
f7a989f
fix(hands): address the server, engine and netcode review
user1303836 Oct 7, 2026
37a3df6
chore: clear new dependency audit findings
user1303836 Oct 7, 2026
f7acf79
fix(hands): toes stay flat on the canvas when the heel lifts
user1303836 Oct 7, 2026
216bb38
fix(hands): knockdowns fall the way the punch sends them, live and in…
user1303836 Oct 7, 2026
0232ec7
fix(hands): hooks end their sweep on the contact point
user1303836 Oct 7, 2026
1681e9d
fix(hands): body hooks bend the fighter toward the ribs and body uppe…
user1303836 Oct 7, 2026
a549ccc
fix(hands): close range jabs and straights stop at the face instead o…
user1303836 Oct 7, 2026
60a8c31
fix(hands): a punch allocates nothing per frame
user1303836 Oct 7, 2026
36cdb2c
fix(hands): the pose solver stops building vectors every frame
user1303836 Oct 7, 2026
f8d102b
fix(hands): punches the server refuses come back as soon as it says so
user1303836 Oct 7, 2026
0a55d5b
fix(hands): a fighter dropped for not reading still pauses the bout a…
user1303836 Oct 7, 2026
60cd1f0
fix(hands): reconnecting over a stalled socket no longer blocks every…
user1303836 Oct 7, 2026
a914ea1
fix(hands): a punch held back by the input budget no longer fires aft…
user1303836 Oct 7, 2026
a97a1dc
test(hands): the rematch-while-closing test no longer races the bout …
user1303836 Oct 7, 2026
79f231b
fix(hands): the newer of two presses inside one server tick is the pu…
user1303836 Oct 7, 2026
f4ee0c1
test(hands): the first-frame timeout test no longer hangs on windows
user1303836 Oct 7, 2026
32a70f5
fix(hands): a window left open across a protocol bump is told to reload
user1303836 Oct 7, 2026
a60a19c
fix(hands): the local two-player server starts on windows
user1303836 Oct 7, 2026
652adab
fix(hands): an engine failure ends the bout as a no contest instead o…
user1303836 Oct 7, 2026
5c4230f
fix(hands): a punch pressed during another starts when the engine wou…
user1303836 Oct 7, 2026
d7f6ffc
fix(hands): back-to-back bouts between the same two fighters move rat…
user1303836 Oct 7, 2026
5397e9d
fix(hands): slips and weaves that work show the glove go where the he…
user1303836 Oct 7, 2026
64aa97c
fix(hands): a pause returns both fighters' held walk and guard to neu…
user1303836 Oct 7, 2026
2e05cb7
fix(hands): knees bend the right way and feet, knees and gloves meet …
user1303836 Oct 7, 2026
5ffce95
fix(hands): a press is matched to the input frame that actually carri…
user1303836 Oct 7, 2026
89126c5
fix(hands): seated fighters' feet rest on the canvas
user1303836 Oct 7, 2026
f77cd44
fix(hands): a chain of combinations no longer holds a stunned fighter
user1303836 Oct 7, 2026
3ea073c
fix(hands): the body weave and slip test boxes with a settled facing
user1303836 Oct 7, 2026
fff3bd3
fix(hands): clinched fighters stop instead of keeping the speed they …
user1303836 Oct 7, 2026
d45696f
fix(hands): the manifest refuses a facing scale or stool spot the eng…
user1303836 Oct 7, 2026
6bc8693
fix(hands): a walk or guard held at the bell no longer carries into t…
user1303836 Oct 7, 2026
38a6dad
fix(hands): discord rpc and connection failures explain themselves
user1303836 Oct 7, 2026
d48c856
fix(hands): rematch, retry and failures keep the discord activity open
user1303836 Oct 7, 2026
942a45b
fix(hands): a protocol bump asks open windows to reload
user1303836 Oct 7, 2026
f513ee6
fix(hands): interpolation delay recovers within seconds of a latency …
user1303836 Oct 7, 2026
69308ab
fix(hands): presses right after a periodic flush leave at once
user1303836 Oct 7, 2026
d99735f
fix(hands): the bell sends one neutral frame so held input stops
user1303836 Oct 7, 2026
cb980ed
fix(hands): touch input reaches the server without document focus
user1303836 Oct 7, 2026
37524af
fix(hands): keys and touches held through the bell count at once
user1303836 Oct 7, 2026
97de308
fix(hands): phone players can slip, weave, pull and clinch
user1303836 Oct 7, 2026
7f1ef42
fix(hands): audio unlocks on the first touch
user1303836 Oct 7, 2026
876b265
fix(hands): the network remembers which input frame carried each press
user1303836 Oct 7, 2026
4313555
fix(hands): a rematch signs in without authenticating the sdk again
user1303836 Oct 7, 2026
8f9806f
fix(hands): the resolution scaler settles one step down instead of pu…
user1303836 Oct 7, 2026
911f108
fix(hands): the low quality tier no longer recompiles every shader
user1303836 Oct 7, 2026
f1cbd23
fix(hands): the first bloody hit no longer compiles a shader mid-fight
user1303836 Oct 7, 2026
f6c4e9e
fix(hands): rematches free the blob shadows, gib buffers and bloom fi…
user1303836 Oct 7, 2026
781c362
fix(hands): the 3D view follows a zoom or a move to another monitor
user1303836 Oct 7, 2026
1f9a066
fix(hands): the severed glove's wrist cap sits on the glove
user1303836 Oct 7, 2026
844d23a
fix(hands): a dislocated jaw no longer tears open the throat
user1303836 Oct 7, 2026
0a2bd3e
fix(hands): a stoppage without a replay waits for the finishing punch
user1303836 Oct 7, 2026
fdf077f
fix(hands): the knockdown punch keeps its full dent in the face
user1303836 Oct 7, 2026
c611da8
fix(hands): compact scoreboard mini-bars fit small phones
user1303836 Oct 7, 2026
a54349f
fix(hands): blood, teeth and severed parts fly along the punch
user1303836 Oct 7, 2026
c940c23
fix(hands): blood droplets cost nothing when none are in flight
user1303836 Oct 7, 2026
3c204fa
fix(hands): the corner stool stays until the fighter has risen off it
user1303836 Oct 7, 2026
9d5c651
fix(hands): the taunt beckon moves every frame
user1303836 Oct 7, 2026
1d6cd95
fix(hands): fighters walk in to touch gloves before the opening bell
user1303836 Oct 7, 2026
ea285ec
fix(hands): the referee steps in and pushes on both fighters to break…
user1303836 Oct 7, 2026
d1fdce2
fix(hands): the referee's counting arm chops down on each number
user1303836 Oct 7, 2026
f926bdf
fix(hands): officials use plain materials and cast no shadows
user1303836 Oct 7, 2026
ca2118b
fix(hands): a fighter first seen mid-walk to the glove touch steps, n…
user1303836 Oct 7, 2026
8776d74
fix(hands): every punch eases out of the guard instead of jumping on …
user1303836 Oct 7, 2026
86fdb70
fix(hands): snapshots are deflated and coalesced so slow viewers stay…
user1303836 Oct 7, 2026
d164964
test(hands): a spectator on a 40 KB/s link keeps up with the fight
user1303836 Oct 7, 2026
a3d4071
fix(hands): knockdowns near the ropes keep the body inside the ring
user1303836 Oct 7, 2026
b98a52c
fix(hands): the get-up follows the meter and finishes inside the serv…
user1303836 Oct 7, 2026
8cb31d8
fix(hands): the player's fighter sets off on the press and stops on t…
user1303836 Oct 7, 2026
daa9d4c
fix(hands): the player's guard and slips show on the key press
user1303836 Oct 7, 2026
6cb8525
test(hands): the stalled-reconnect test waits for a lasting stall again
user1303836 Oct 7, 2026
a749c6c
fix(hands): a knockdown during the get-up falls from where he was
user1303836 Oct 7, 2026
6dc5098
fix(hands): the feet are planted under a rising fighter instead of sn…
user1303836 Oct 7, 2026
91671d9
test(hands): a transition sweep guards continuity, the canvas and kne…
user1303836 Oct 7, 2026
186c286
Merge fix293-srv into the #293 review-fix integration
user1303836 Oct 7, 2026
173892a
Merge fix293-pred into the #293 review-fix integration
user1303836 Oct 7, 2026
7e70b17
Merge fix293-anim into the #293 review-fix integration
user1303836 Oct 7, 2026
01d5a4c
fix(hands): the renderer learns which input frame carried each press
user1303836 Oct 7, 2026
c137b4d
test(hands): the transition sweep covers hooks, uppercuts and a per-t…
user1303836 Oct 7, 2026
e9bfa3e
test(hands): the knockdown dent test uses a hit the engine can deal
user1303836 Oct 7, 2026
7987a51
build(hands): rebuild the Activity bundle
user1303836 Oct 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ The right stick also performs one punch after a center→peak→center gesture.

### Presentation and settings

The committed client renders a real-time 3D broadcast presentation with WebGL (pinned three.js): a procedurally modeled arena, canvas-textured ring, a referee, dynamic lighting and shadows, and GPU particle effects—including deliberately over-the-top, full-mode arcade gore—plus generated Web Audio. The fighters use the realistic “Boxer” rig by Texel, Inc. (CC BY 4.0; source, attribution, and modification notice in `web/hands/assets-src/Boxer.LICENSE.txt`) driven by authored clips retarget-baked from the project's own boxing motion (idle, movement, guards, all punches both hands, reactions, knockdown, get-up), phase-locked to the authoritative simulation via combat-manifest.json. All arena meshes, textures, and sounds remain generated in code. **Blood defaults to graphic `full` mode**; Settings can select `full`, `reduced`, or `off` without changing authoritative trauma or HUD information. Settings also provide master audio volume, haptics on/off, and reduced motion. Audio unlocks only after user interaction and suspends while hidden; haptics are feature-detected and limited to bounded authoritative impact events. Reduced motion removes particles/shake while preserving match state and private get-up information.
The committed client renders a real-time 3D broadcast presentation with WebGL (pinned three.js): a procedurally modeled arena, canvas-textured ring, a referee, dynamic lighting and shadows, and GPU particle effects—including deliberately over-the-top, full-mode arcade gore—plus generated Web Audio. The fighters and the referee use the realistic “Boxer” rig by Texel, Inc. (CC BY 4.0; source, attribution, and modification notice in `web/hands/assets-src/Boxer.LICENSE.txt`) posed at runtime by a two-bone IK solver: planted, stepping feet, punches that extend from the rotated shoulder to the opponent's live head phase-locked to combat-manifest.json timing, spring-damper hit reactions along the incoming punch line, and authored falls and get-ups. Injuries are shaded in object space on the skin materials (bruising, swelling, cuts, flowing blood) and the finishers detach the fighter's actual head or glove mesh. All arena meshes, textures, and sounds remain generated in code. **Blood defaults to graphic `full` mode**; Settings can select `full`, `reduced`, or `off` without changing authoritative trauma or HUD information. Settings also provide master audio volume, haptics on/off, and reduced motion. Coarse-pointer clients (Discord mobile) get on-screen controls: a floating joystick for footwork, punch pads whose left or right half picks the hand, and hold buttons for body, power, and guards. Audio unlocks only after user interaction and suspends while hidden; haptics are feature-detected and limited to bounded authoritative impact events. Reduced motion removes particles/shake while preserving match state and private get-up information.

Before a public release, review Discord's current violent-content rules, age rating/restriction controls, store/application disclosures, and regional requirements. Do not add external/borrowed assets, commercial boxing-game UI, audio, animation traces, real-fighter or celebrity likenesses, sanctioning-body/brand marks, logos, or recognizable trade dress. All fighter art, interface, motion, terminology, and sound must remain original.

Expand Down Expand Up @@ -600,7 +600,7 @@ git diff --exit-code -- src/intelstream/hands/static

`npm run build` typechecks, writes exactly `index.html`, `assets/hands.js`, and `assets/hands.css` into `src/intelstream/hands/static`, and runs the scanner. Those generated files are committed so production Python installations do not require Node. Change TypeScript/CSS in `web/hands`, rebuild, inspect the output, commit source and generated files together, and require the final `git diff --exit-code` after a clean rebuild. Never hand-edit the generated bundle.

The fixed three-file deployment contract embeds the Boxer rig and its five source textures in `hands.js`; the resulting production script is intentionally about 5.6 MB (about 2.9 MB gzip). This startup-cost tradeoff keeps the Activity self-contained and avoids runtime asset requests.
The fixed three-file deployment contract embeds the mesh-only Boxer rig and its five source textures (re-encoded as JPEG by the generator) in `hands.js`; the resulting production script is about 2.36 MB (about 1.28 MB gzip). This startup-cost tradeoff keeps the Activity self-contained and avoids runtime asset requests. `scripts/hands_pose_strip.js` renders a pose frozen at a list of times into one frame strip for motion review. `npm run dev` also serves `?model-lab=1&pose=<name>&cam=<angle>&trauma=<light|heavy|cut>` as a pose lab for reviewing the runtime animation and injury shading without a match (poses include punches, guards, slips, knockdown, seated, celebrate and wave_off), and `?fixture=1` with `&finisher=head|hand`, `&phase=rest`, `&pin=1|corner`, `&blood=reduced|off` or `&platform=mobile` for presentation review.

For a standalone presentation fixture, run `npm --prefix web/hands run dev` and open the printed URL with `?fixture=1`. For the real two-process localhost flow, terminal one runs the Python bot/Hands server with valid Discord credentials:

Expand All @@ -614,6 +614,19 @@ Terminal two runs Vite and proxies HTTP plus WebSocket `/api/hands` requests to
HANDS_DEV_BACKEND=http://127.0.0.1:8080 npm --prefix web/hands run dev
```

### Playing a match locally without Discord

`scripts/hands_e2e_server.py` runs the Hands server with a development auth backend that skips the Discord OAuth steps but keeps the production ticket, room, websocket, engine and persistence code paths:

```bash
uv run python scripts/hands_e2e_server.py --port 8091 --rounds 1 --round-seconds 45
HANDS_DEV_BACKEND=http://127.0.0.1:8091 npm --prefix web/hands run dev -- --port 5174
```

Open `http://localhost:5174/?e2e=1&instance_id=<any id>&player=<name>` in two browser windows (different names) to fight. The `e2e` switch only exists in Vite development builds; the development server authenticates anyone who can reach its port, so never expose it.

`scripts/hands_e2e_scenarios.js` automates this with Playwright: `ko` (power punches until a knockdown, private get-up rhythm, stoppage), `reconnect` (one client drops offline mid-round and resumes), `rest` (two rounds with the corner rest), `spectator` (a third client joins read-only), `touch` (one player on an emulated phone), `mash` (hundreds of key presses per second) `latency` (a TCP proxy delays every frame 110 ms each way, and the clients report their measured input acknowledgement latency, which the fighter HUD also shows) `soak` (a full three-round bout sampling heap size, latency and render scale every twenty seconds) `background` (one player's page is frozen through the DevTools lifecycle state for eight seconds, as a backgrounded phone app would be, then resumed) `rematch` (both players use the Rematch button after the result and fight a second bout in the same instance) `rematchloop` (four bouts back to back while sampling heap size and GPU object counts for leaks) `clinch` (both players close in and one ties up; the harness reads the fighters' separation from the client state through the engine's draw-in, the hold and the referee break) and `response` (key press to first rendered frame for the player's own punch and step, when the opponent sees them, and whether the predicted punch ever jumps back, behind a delay proxy set with `E2E_DELAY_MS`, 60 ms each way by default). It spawns the development server itself and prints the clients' status text plus any client or server errors. The default software renderer keeps runs reproducible on machines without a GPU; set `E2E_GPU=1` to render on the machine's GPU for real frame pacing, input latency and screenshots.

`HANDS_DEV_BACKEND` is a Node/Vite development-process setting, not a browser `VITE_*` value and not a production origin. Real OAuth still needs a valid Activity instance; the fixture is only recorded presentation data. Keep `HANDS_DEV_MODE=false` in production.

### Wheel/package verification
Expand Down
10 changes: 10 additions & 0 deletions docs/hands-lab-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,3 +347,13 @@ the palette toward pastel; trauma overlays are scaled for the larger head but
their positions are approximate; the knockdown head-fall velocity makes the
0.10 m joint metric read ~0.22 m one tick after contact (filmstrip shows flush
contact).

---

# Superseded (2026-09-28)

The clip-based animation graph, the retarget-baked GLB, the marker export, and
the sphere trauma overlays described above were replaced by the runtime pose
system, object-space injury shading, and mesh-baked gore documented in
`docs/hands-realism-plan.md`. The lab route, the golden replay, and the
acceptance metrics still apply; the protocol is now v3 and the ring is square.
87 changes: 87 additions & 0 deletions docs/hands-realism-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Hands realism overhaul

Branch `feature/hands-realism`. Goal: make the Hands Discord Activity a realistic,
responsive boxing game with injuries and gore, on the existing authoritative
Python engine, websocket rooms, persistence, and ratings.

## Baseline findings (2026-09-28)

Measured against `origin/main` at 992a4f5 with the dev fixture, the model lab,
and the engine source.

Simulation and protocol

- Punches only hit along the ring x axis. `_resolve_punch` projects onto the
binary `facing` sign, so fighters offset along ring depth whiff while the
presentation shows them squared up.
- A second punch pressed during the first is dropped. `ACTION_BUFFER_TICKS`
(6) expires before a 13-tick jab clears, so `pending_actions` is cleared
before it can dispatch. There is no recovery cancel, so combos cannot flow.
- Get-up teleports the downed fighter to `x = +-140`, which the client hides
with a 0.85 m slide.

Presentation and network

- Movement stutters at 15 Hz. The renderer samples a fixed `latest.tick - 1`
and snapshots arrive every second tick, so the pose holds for about four
frames and jumps.
- Input is polled every 40 ms instead of sent on the key edge, and there is no
local movement prediction, so footwork lags by round trip plus polling.
- Punch, guard, movement, reaction, and knockdown clips are retarget-baked
from a capsule rig through direction-only child aim. Elbow and wrist twist
are uncontrolled and the knockdown never reaches the canvas.
- Injuries are colored spheres and boxes parented to the head bone. They
float beside the textured face. Gibs are red icosahedra; the severed head is
a scaled sphere; the referee is a capsule mannequin.
- Five 105 cd fill spotlights plus bloom wash the image to pastel and blow out
the white rope. The broadcast camera sits 7 m back, so fighters occupy
roughly a third of the frame height.

## Plan

1. Simulation feel: continuous facing vector with limited turn rate and a
facing-relative hit test; input buffer that survives the current punch;
late-recovery combo cancels; knockdown separation by the referee instead of
a teleport; protocol v3 carrying `facing_x`, `facing_y`, and the last
processed input sequence.
2. Netcode: continuous render clock with adaptive interpolation delay,
30 Hz broadcast, edge-triggered action sends, and local movement prediction
for the viewer's fighter.
3. Animation: a runtime pose system on the Texel skeleton (stance, guards,
punches that reach the live target, footwork with planted feet and pivots,
spring-damper hit reactions, keyframed falls and get-ups) replacing the
retarget-baked clips.
4. Injuries and gore: face and body damage painted into the skin textures
(bruising, cuts, swelling displacement, flowing blood), blood on gloves and
canvas, stretched droplet sprites, and mesh-based decapitation and
dismemberment for the arcade finishers.
5. Presentation: closer broadcast framing with impact and knockdown cameras,
corrected lighting and exposure, layered impact audio, and a skinned
referee.

Every phase lands as its own commits with Python and Vitest coverage, a
rebuilt committed bundle, and screenshots captured through headless Chromium.

## Status (2026-09-29)

All five phases landed on `feature/hands-realism` (PR #293). Beyond the plan, the branch also
carries: adaptive render resolution with a no-gain revert, crowd excitement, rest-phase corner
walk with stools and a cutman who climbs in to work the worse eye (engine + client), anti-parallel
facing fix, knockout presentation (slow-motion replay, close-up, held result panel, winner
celebration, referee wave-off), portrait framing with a compact scoreboard, per-round and per-bout
punch stats, rope flex under a pinned fighter, face-down knockdowns after hooks, a real clinch
tie-up (the engine draws the fighters to a hold distance; one hooks over, the other under), a
legible showboat taunt, glove trails on fast punches, a rendered root that follows the engine at a
fixed speed per second, a broken nose under heavy head trauma, a glove touch before the opening
bell, a shadow-boxing waiting screen, rematches, plain-language failure text and a copyable
diagnostics panel.

Review harnesses: `?model-lab=1&pose=...` (pose lab), `?fixture=1` with `&finisher=head|hand`,
`&phase=rest`, `&pin=1|corner`, `&blood=reduced|off`, `&platform=mobile`, `?lab=1` (golden replay), and the Discord-free
real-stack harness (`scripts/hands_e2e_server.py` plus `scripts/hands_e2e_scenarios.js` with the
ko, reconnect, rest, spectator, touch, mash, latency, soak, background, rematch, rematchloop and
clinch scenarios; see the README).

Considered and not done: starting round 1 from the corners (the engine tests and the golden
replay schedule assume the centre start). A live Discord Activity launch with the server's players
still has to happen outside this repository's tooling.
Loading
Loading