Description
The Three.js adapter dispatches CustomEvent("hf-seek") on window during its seek() method, but user script window.addEventListener("hf-seek", ...) listeners may not receive these events during Puppeteer frame capture (render mode).
Expected behavior
Compositions using the /three skill pattern should receive hf-seek events and render their Three.js scene at each frame time.
Actual behavior
The hf-seek listener never fires. The Three.js scene either remains static (if preserveDrawingBuffer: true is set) or renders blank (if not).
window.__hfThreeTime IS set correctly — the adapter's seek() executes, but the event dispatch (wrapped in try/catch at packages/core/src/runtime/adapters/three.ts) may silently fail.
Reproduction
- Create a composition using only the
hf-seek event pattern from skills/three/SKILL.md
- Add
preserveDrawingBuffer: true to the renderer (otherwise output is blank regardless)
- Render with
npx hyperframes render
- Observe: Three.js canvas is static in the output
Workaround
Hook into the GSAP timeline's onUpdate callback instead:
tl.eventCallback("onUpdate", () => {
renderAt(tl.time());
});
This works because HyperFrames seeks GSAP timelines directly via timeline.totalTime(), which fires onUpdate on every frame. Documented in #583.
Root cause hypothesis
The adapter's seek() is called during onDeterministicSeek (init.ts) which runs during renderSeek. The user script's event listener may be registered in a different execution context, or the event dispatch may be swallowed by the try/catch that wraps window.dispatchEvent().
Environment
- HyperFrames: latest (via npx)
- macOS, Node 25, Chrome (Puppeteer bundled)
- Three.js r128 (global build)
Discovered by 0xHoneyJar using the Loa framework.
Description
The Three.js adapter dispatches
CustomEvent("hf-seek")onwindowduring itsseek()method, but user scriptwindow.addEventListener("hf-seek", ...)listeners may not receive these events during Puppeteer frame capture (render mode).Expected behavior
Compositions using the
/threeskill pattern should receivehf-seekevents and render their Three.js scene at each frame time.Actual behavior
The
hf-seeklistener never fires. The Three.js scene either remains static (ifpreserveDrawingBuffer: trueis set) or renders blank (if not).window.__hfThreeTimeIS set correctly — the adapter'sseek()executes, but the event dispatch (wrapped in try/catch atpackages/core/src/runtime/adapters/three.ts) may silently fail.Reproduction
hf-seekevent pattern fromskills/three/SKILL.mdpreserveDrawingBuffer: trueto the renderer (otherwise output is blank regardless)npx hyperframes renderWorkaround
Hook into the GSAP timeline's
onUpdatecallback instead:This works because HyperFrames seeks GSAP timelines directly via
timeline.totalTime(), which firesonUpdateon every frame. Documented in #583.Root cause hypothesis
The adapter's
seek()is called duringonDeterministicSeek(init.ts) which runs duringrenderSeek. The user script's event listener may be registered in a different execution context, or the event dispatch may be swallowed by the try/catch that wrapswindow.dispatchEvent().Environment
Discovered by 0xHoneyJar using the Loa framework.