# Wave 0 spike verdict (AVTR-01) **Date:** 2026-09-05 **Space revision:** `487b8cf` (code; `82717b7` docs on top) **Transport:** inline The question this spike existed to answer: can three.js + `@pixiv/three-vrm` render a VRM with idle life inside a Gradio 6 `gr.HTML` component on a real, public, ZeroGPU Hugging Face Space, and survive the host's re-render paths — or does the avatar have to live in an iframe? `01-RESEARCH.md` § "The honest gap" found no prior art either way: every three.js Space on the Hub is `sdk: static`, and none of Gradio's own `gr.HTML` examples load a third-party ES module. ## Automated evidence All values are read as numbers out of `window.Avatar.getDebug()` on the public URL by `tests/e2e/test_avatar_loop.py --space-url https://wolfdavid-japanese-learning-avatar.hf.space` (5 passed, run twice on 2026-09-05 against `487b8cf`). Never a screenshot diff. | Check | Value | Pass | |---|---|---| | threeInstanceCount (deployed) | 1 | ✅ | | mountCount after 20 interactions | 1 (send ×7, fill+send ×7, checkbox ×6; `ready` still true, threeInstanceCount still 1) | ✅ | | vrmMetaTitle | `VRM1_Constraint_Twist_Sample` (vrmSpecVersion `1`) | ✅ | | blink max over 12 s | 1.0 — 3 completed blinks (`blinkCount`), full-closure frame observed | ✅ | | distinct breath values over 12 s | 169 (170 samples at animation-frame rate) | ✅ | | console: "Multiple instances of Three.js" | absent | ✅ | | console: "import map" | absent | ✅ | | armDown left / right (rest pose, raw skeleton) | −0.932 / −0.932 (threshold < −0.7; 0 would be the T-pose) | ✅ | | Space-page embed (`huggingface.co/spaces/…` cross-origin iframe) | `ready: true`, threeInstanceCount 1, mountCount 1, armDown −0.932, transport `inline` — read inside the iframe | ✅ | | tutor.vrm served from the Space | 10,776,332 bytes transferred (`model/vrml`), not an LFS pointer | ✅ | | Cold wake (Space already warm) | 0.2 s to first HTTP 200 — true sleep→wake is a 01-10 measurement | recorded | | Page load → `ready` | 4.09 s / 5.61 s / 5.66 s / 8.30 s across four loads; tutor.vrm 2.96–6.79 s of that | recorded | | Space runtime | `RUNNING`, hardware `zero-a10g`, `DISABLE_GPU=1`; rebuilt to RUNNING in 106 s after push | ✅ | **Console noise on the deployed page, none of it a spike token:** one `404` from `https://huggingface.co/api/organizations/WolfDavid/avatar` (Gradio's own Hub-integration lookup, not a project resource); deprecation warnings for `VRMUtils.removeUnnecessaryJoints` (three-vrm says use `combineSkeletons`, which we already call) and `THREE.Clock` (use `THREE.Timer`); headless-GPU `ReadPixels` stall warnings that do not occur on a real GPU. The two deprecations are cheap to retire when plan 01-10 vendors the modules. ## Human observation **Desktop:** Chromium 151.0.7922.34 (Playwright, headless, 1100×900 and 1280×900) — `docs/evidence/2026-09-05-deployed-rest-pose.png` (direct URL) and `docs/evidence/2026-09-05-hub-embed.png` (the Space page on huggingface.co, "Running on ZERO" badge visible). Character renders upper-body, face readable, not clipped; hair, skin and T-shirt in correct MToon toon shading; arms hang in a relaxed A-pose; the pixiv credit line is visible under the controls. The earlier build `41ee90e` was confirmed **T-posed** by the project owner and by `docs/evidence/2026-09-05-deployed-tpose.png` — that was a missing rest pose (nothing ever moved the arms), *not* the two-instances symptom, and it is fixed in `7a26764`. The project owner gave the verdict on this evidence on 2026-09-05. **Mobile:** ⚠ **NOT YET TESTED.** No real device has loaded the Space. Fill in: ` / / ` — renders y/n — smooth or slideshow. This row is a plan-01-05 acceptance criterion and an AVTR-01 manual-only verification; until it is recorded, **AVTR-01 stays Pending** even though every automated check passes. **Space-page embed (huggingface.co/spaces/WolfDavid/japanese-learning-avatar):** the app loads inside the Hub's cross-origin iframe (`?__theme=system`), the avatar reaches `ready`, and the same debug numbers read identically from inside the frame. No mixed-content, no third-party-cookie, no `allow=` issue surfaced for rendering. (Microphone permission inside this embed is a plan 01-08/01-09 question, not this spike's.) ## Verdict **CONFIRMED: `gr.HTML` hosts three.js/VRM on a deployed Gradio 6 Space.** This is a publicly novel result — no prior art existed. Keeping `AVATAR_TRANSPORT=inline`. The iframe transport (`avatar/avatar-iframe.js` + `avatar/stage.html`) stays in the tree as the tested fallback it was built to be; `tests/e2e/test_facade_parity.py` keeps proving both transports expose byte-identical method and debug surfaces, so flipping the env var remains a config change rather than a re-plan. ## Consequences for later plans - **None for the architecture.** The `window.Avatar` facade, `turn-loop.js` and the six-method `stagePort` seam are exactly as designed; 01-08 wires `dispatchTurn` / `requestSlower` against them with no transport awareness. - **AVTR-01 is not closed by this verdict.** Its last open item is the mobile row above. Record it, then mark the requirement. - **A green suite is not a correct picture.** The deployed avatar passed `test_vrm_ready`, `test_idle_life` and `test_no_remount` while standing in a T-pose. The fix added `armDown` and a `< −0.7` assertion at three layers (standalone stage, both transports, deployed). The standing rule for 01-08/01-09: **any visual property a human would notice must be published as a number and asserted**, before the phase is called done. - **`ready` fires before the first frame renders,** and the first frame compiles every MToon shader (>1.5 s measured headless). Post-render measurements must wait for `breathValue !== 0`, never a fixed sleep. 01-09's deployed lip-sync tests inherit this. - **`window.Avatar.__debug` is a stale snapshot** refreshed only by `getDebug()`. Always `await window.Avatar.getDebug()` per sample; `IDLE_SAMPLER` in `test_avatar_loop.py` is the reference pattern. - **01-10 latency harness inputs recorded here:** page→ready 4.1–8.3 s warm; tutor.vrm 3.0–6.8 s of that over the public internet; Space rebuild-to-RUNNING 106 s; warm wake 0.2 s. True cold-from-sleep wake (48 h `gcTimeout`) is still unmeasured. - **01-10 vendoring:** replace `THREE.Clock` with `THREE.Timer` and drop the deprecated `removeUnnecessaryJoints` call while pinning the vendored modules.