diff --git a/three.js/README.md b/three.js/README.md
new file mode 100644
index 00000000..893d51da
--- /dev/null
+++ b/three.js/README.md
@@ -0,0 +1,77 @@
+# three.js demos
+
+Five progressively richer browser demos of the ADR-097 sensing-helpers scene,
+ending with a live MediaPipe-Pose → Mixamo X Bot retargeting pipeline driven
+by a real ESP32 CSI feed.
+
+## Run them
+
+```bash
+python examples/three.js/server/serve-demo.py
+# then open one of the URLs the script prints
+```
+
+`server/serve-demo.py` is a tiny `ThreadingHTTPServer` with aggressive
+no-cache headers — the stdlib `http.server` is single-threaded and times out
+on the parallel script + FBX fetches the demos make.
+
+## Demos
+
+| # | File | What it shows |
+|---|------|---------------|
+| 01 | [`demos/01-helpers.html`](demos/01-helpers.html) | Plain ADR-097 helpers in the point-cloud viewer |
+| 02 | [`demos/02-cinematic.html`](demos/02-cinematic.html) | Cinematic camera + pseudo-CSI visualization on top of #01 |
+| 03 | [`demos/03-skinned.html`](demos/03-skinned.html) | GLTF skinned mesh + additive animation blending |
+| 04 | [`demos/04-skinned-fbx.html`](demos/04-skinned-fbx.html) | Mixamo X Bot loaded from FBX in the ADR-097 scene |
+| 05 | [`demos/05-skinned-realtime.html`](demos/05-skinned-realtime.html) | Webcam → MediaPipe Pose Heavy → Mixamo IK retarget, live ESP32 CSI overlay |
+
+| Screenshot | |
+|---|---|
+|  |  |
+|  |  |
+|  | |
+
+## Layout
+
+```
+examples/three.js/
+├── README.md
+├── .gitignore
+├── demos/ # 5 self-contained HTML demos
+│ ├── 01-helpers.html
+│ ├── 02-cinematic.html
+│ ├── 03-skinned.html
+│ ├── 04-skinned-fbx.html
+│ └── 05-skinned-realtime.html
+├── screenshots/ # one PNG per demo
+│ └── 0N-*.png
+├── server/
+│ ├── serve-demo.py # local HTTP server with no-cache headers
+│ └── ruvultra-csi-bridge.py # ESP32 CSI WebSocket bridge (ruvultra:8766)
+└── assets/
+ └── X Bot.fbx # gitignored — get your own from mixamo.com
+ # (FBX Binary, T-Pose, Without Skin)
+ # used by demos 04 and 05
+```
+
+## Mixamo X Bot
+
+Demos 04 and 05 expect `assets/X Bot.fbx`. It's gitignored (size + license
+boundary). Download yours from [mixamo.com](https://mixamo.com): pick the
+"X Bot" character, export as **FBX Binary**, **T-Pose**, **Without Skin**,
+and drop it into `assets/`.
+
+## Live ESP32 CSI overlay (demo 05 only)
+
+`server/ruvultra-csi-bridge.py` is the systemd-deployable bridge that runs on
+the `ruvultra` host (over Tailscale). It listens for ESP32-S3 CSI on UDP and
+re-broadcasts it as WebSocket frames at `ws://ruvultra:8766/csi`. Demo 05
+auto-connects; if the socket is down, it falls back to the bundled idle clip
+plus a synthetic CSI driver.
+
+## Open issues
+
+- [#583](https://github.com/ruvnet/RuView/issues/583) — head/face tracking
+ fidelity in `05-skinned-realtime.html`. Recommended fix: swap MediaPipe
+ Pose Heavy for MediaPipe Holistic (same API, adds 468-point face mesh +
+ hand landmarks for proper PnP head pose and finger curl tracking).
diff --git a/three.js/assets/README.txt b/three.js/assets/README.txt
new file mode 100644
index 00000000..4d3c1d83
--- /dev/null
+++ b/three.js/assets/README.txt
@@ -0,0 +1,7 @@
+The Mixamo "X Bot.fbx" required by demos 04-skinned-fbx.html and
+05-skinned-realtime.html is intentionally not redistributed here.
+
+Download your own from https://mixamo.com (FBX Binary, T-Pose,
+Without Skin) and place it here as "X Bot.fbx" if you want to
+run those demos locally. See examples/three.js/README.md in the
+repo for context.
diff --git a/three.js/demos/01-helpers.html b/three.js/demos/01-helpers.html
new file mode 100644
index 00000000..e915fd13
--- /dev/null
+++ b/three.js/demos/01-helpers.html
@@ -0,0 +1,587 @@
+
+
+
+
+
+ RuView · ADR-097 · three.js helpers in the point cloud viewer
+
+
+
+
+
+
+
+
RuView · Helpers Demo
+
ADR-097 · three.js helpers for the point cloud viewer
+
+
+
+
diff --git a/three.js/demos/04-skinned-fbx.html b/three.js/demos/04-skinned-fbx.html
new file mode 100644
index 00000000..8353c015
--- /dev/null
+++ b/three.js/demos/04-skinned-fbx.html
@@ -0,0 +1,961 @@
+
+
+
+
+
+ RuView · Skinned (FBX) · Mixamo X Bot in the ADR-097 helpers scene
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
▸ Loading skinned subject · X Bot.fbx
+
+
+
RuView · Skinned (FBX)
+
ADR-097 · Mixamo X Bot · loaded via FBXLoader
+
Subject● Tracked
+
SourceX Bot.fbx
+
FormatFBX 7700 · 1.75 MB
+
Bones—
+
Animation—
+
Mesh nodes4 · multistatic
+
Coherence— %
+
Heart rate— bpm
+
Bbox vol— m³
+
Render— fps
+
+
+
+
AnimationMixer
+
+ clips
+
+
+
+ time scale
+
+ 1.00
+
+
+ No animations in this FBX.
+ Mixamo's "T-Pose / Without Skin" export rigs the model but has no clips.
+ Re-download with "Original Pose" + an animation selected
+ (e.g. Walking) to get a clip, or drop another FBX with anim and reload.
+
+ Five progressively richer browser demos of the ADR-097
+ sensing-helpers scene, ending with a live MediaPipe-Pose → Mixamo X Bot retargeting pipeline driven
+ by a real ESP32 CSI feed.
+
+ Demos 04 and 05 need a Mixamo asset. The Mixamo
+ X Bot.fbx file is intentionally not redistributed in
+ this deployment — it's licensed for end-users to download from
+ mixamo.com directly.
+ To run these locally: clone the repo, download X Bot.fbx
+ (FBX Binary, T-Pose, Without Skin) into
+ examples/three.js/assets/, then run
+ python examples/three.js/server/serve-demo.py.
+