Spinoff from the cinematic three.js skinned-realtime demo branch (feat/three-skinned-realtime-pose-demo / PR pending). Limbs + torso + hips all tracking well now; head movement is the next fidelity ceiling.
What's already shipped
examples/three.js/helpers-skinned-realtime.html — webcam → MediaPipe Pose Heavy → poseWorldLandmarks → direct quaternion retargeting onto a Mixamo X Bot. Live ESP32-S3 CSI over WebSocket from ruvultra (Tailscale).
| Pipeline stage |
Working today |
| Camera |
1280×720 @ 30 fps |
| Pose model |
MediaPipe Pose Heavy (modelComplexity=2), poseWorldLandmarks real meters |
| Retargeting |
12 bones (arms × 2, legs × 2, spine × 3, neck) + Hips root rotation from shoulder/hip basis |
| Smoothing |
One Euro Filter per landmark per axis (minCutoff=1.7, beta=0.007) |
| Visibility gate |
per-bone slerp gain ramps vis 0.4 → 0.7 |
| CSI overlay |
live ESP32-S3 (D0:CF:13:44:01:84) via ruvultra-csi-bridge.py systemd unit on ruvultra:8766 |
Why head movement specifically falls short
MediaPipe Pose's 33 landmarks only give:
0 nose
1–10 face/eyes/ears/mouth (low-fidelity, single point each)
The current Neck bone retarget uses shoulder_mid → ear_mid (kp 11/12 → 7/8). That's enough to drive the cervical-spine tilt, but:
- The
Head bone itself is not retargeted — head rotation around the neck pivot (look left/right, nod) is not captured
- Ears have higher visibility than the nose but aren't reliable enough for full yaw/pitch/roll
- No facial expression (mouth open, eyebrows, etc.)
- No gaze direction
Candidate fixes
Option A — Add Head bone retarget from current landmarks
Head aimed at nose (kp 0), with the existing forward-bias-removal trick (project out the rest forward component)
- Tracks yaw / pitch from nose displacement vs ear-midpoint
- Cheap: no new model, no extra inference cost
- Limited: no roll, no face expression
Option B — Upgrade to MediaPipe Holistic
- Replaces Pose with Holistic: 33 pose landmarks + 468 face mesh + 21 left-hand + 21 right-hand
- Drop-in API change:
new Holistic() instead of new Pose(), results expose poseLandmarks + faceLandmarks + leftHandLandmarks + rightHandLandmarks
- Face mesh gives proper head pose via PnP solver (3 axes of rotation, not just 1)
- Hands give finger curl tracking — Mixamo X Bot has 20 finger bones per side already in
mixamorigLeftHandIndex1 etc.
- Cost: ~25 MB extra model download, ~30% more CPU per inference
- Biggest fidelity jump available without a custom training pipeline
Option C — MediaPipe Face Mesh as a sidecar pipeline
- Keep Pose Heavy for body, add
@mediapipe/face_mesh separately
- Run both in parallel, solve head pose from face mesh
- More wiring than Holistic but lets us tune them independently
Option D — Solve head pose with cv2.solvePnP equivalent using just the kp 0..10 anchors
- Use OpenCV.js or a pure-JS PnP solver
- Inputs: nose, eyes, ears, mouth corners (6 stable face anchors)
- Output: 3D head rotation matrix
- Drives
Head bone directly
- Middle ground — better than just ear-midpoint, simpler than Holistic
Recommended next step
Option B (Holistic) for the biggest user-visible jump. Hands tracking is the second-most-asked-for feature after head, and Holistic gives both for free. The current helpers-skinned-realtime.html retargeting layer is shape-compatible — RETARGETS would just gain entries for LeftHandIndex1 … RightHandPinky3 (already valid Mixamo bone names, confirmed in the rig).
If Holistic feels too heavy on first-load (25 MB), gate it behind ?holistic=1 and keep Pose Heavy as the default.
Acceptance criteria
Refs
🤖 Generated with claude-flow
Spinoff from the cinematic three.js skinned-realtime demo branch (
feat/three-skinned-realtime-pose-demo/ PR pending). Limbs + torso + hips all tracking well now; head movement is the next fidelity ceiling.What's already shipped
examples/three.js/helpers-skinned-realtime.html— webcam → MediaPipe Pose Heavy → poseWorldLandmarks → direct quaternion retargeting onto a Mixamo X Bot. Live ESP32-S3 CSI over WebSocket from ruvultra (Tailscale).poseWorldLandmarksreal metersvis 0.4 → 0.7ruvultra-csi-bridge.pysystemd unit on ruvultra:8766Why head movement specifically falls short
MediaPipe Pose's 33 landmarks only give:
0nose1–10face/eyes/ears/mouth (low-fidelity, single point each)The current
Neckbone retarget usesshoulder_mid → ear_mid(kp 11/12 → 7/8). That's enough to drive the cervical-spine tilt, but:Headbone itself is not retargeted — head rotation around the neck pivot (look left/right, nod) is not capturedCandidate fixes
Option A — Add Head bone retarget from current landmarks
Headaimed atnose(kp 0), with the existing forward-bias-removal trick (project out the rest forward component)Option B — Upgrade to MediaPipe Holistic
new Holistic()instead ofnew Pose(), results exposeposeLandmarks+faceLandmarks+leftHandLandmarks+rightHandLandmarksmixamorigLeftHandIndex1etc.Option C — MediaPipe Face Mesh as a sidecar pipeline
@mediapipe/face_meshseparatelyOption D — Solve head pose with
cv2.solvePnPequivalent using just the kp 0..10 anchorsHeadbone directlyRecommended next step
Option B (Holistic) for the biggest user-visible jump. Hands tracking is the second-most-asked-for feature after head, and Holistic gives both for free. The current
helpers-skinned-realtime.htmlretargeting layer is shape-compatible —RETARGETSwould just gain entries forLeftHandIndex1…RightHandPinky3(already valid Mixamo bone names, confirmed in the rig).If Holistic feels too heavy on first-load (25 MB), gate it behind
?holistic=1and keep Pose Heavy as the default.Acceptance criteria
Headbone visibly through its full ~80° rangeRefs
feat/three-skinned-realtime-pose-demo2026-05-15-oneeuro-hips-visexamples/three.js/helpers-skinned-realtime.html🤖 Generated with claude-flow