Summary
hyperframes render with the default parallel worker pool (-w auto) consistently deadlocks when a composition combines a HyperShader.init() transition with at least one <audio> clip. All workers eventually time out with Runtime.callFunctionOn timed out. Falling back to -w 1 renders the same composition cleanly.
Repro
Minimal composition with one shader transition between two anchor scenes plus an audio clip.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@hyperframes/core/dist/hyperframe.runtime.iife.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@hyperframes/shader-transitions/dist/index.global.js"></script>
<style>
html,body{margin:0;width:1080px;height:1920px;background:#0a0a0c;color:#fff;overflow:hidden}
.scene{position:absolute;inset:0;width:1080px;height:1920px}
#s2{background:#f5efde;color:#0a0a0c}
</style>
</head>
<body>
<div id="root" data-composition-id="main" data-start="0" data-duration="6" data-width="1080" data-height="1920">
<audio class="clip" data-start="0" data-duration="3" data-track-index="9" src="narration.wav"></audio>
<div class="scene clip" id="s1" data-start="0" data-duration="3" data-track-index="0" style="opacity:0;">
<h1 style="font-size:200px;text-align:center;margin-top:800px">SCENE 1</h1>
</div>
<div class="scene clip" id="s2" data-start="3" data-duration="3" data-track-index="0" style="opacity:0;">
<h1 style="font-size:200px;text-align:center;margin-top:800px">SCENE 2</h1>
</div>
</div>
<script>
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
tl.set("#s1", { opacity: 1 }, 0);
window.HyperShader.init({
bgColor: "#0a0a0c",
scenes: ["s1", "s2"],
timeline: tl,
transitions: [{ time: 2.75, shader: "cinematic-zoom", duration: 0.5 }]
});
window.__timelines["main"] = tl;
</script>
</body>
</html>
Any short WAV/MP3 (~3s) at narration.wav is enough — the bug is independent of audio content.
# Fails (default — auto picks 8 workers on a 16-core M4 Max):
npx hyperframes render --quality high
# Succeeds (~2 min):
npx hyperframes render --quality high -w 1
Observed behavior
After ~12 minutes wall-clock, all workers fail with the same error:
[Parallel] Capture failed:
Worker 0: Runtime.callFunctionOn timed out. Increase the 'protocolTimeout' setting in launch/connect calls for a higher timeout if needed.
Worker 1: Runtime.callFunctionOn timed out. ...
Worker 2: Runtime.callFunctionOn timed out. ...
Worker 3: Runtime.callFunctionOn timed out. ...
Worker 4: Runtime.callFunctionOn timed out. ...
Worker 5: Runtime.callFunctionOn timed out. ...
✗ Render failed
Try --docker for containerized rendering
Progress hangs at 25% Starting frame capture with [non-blocking] Failed to load resource: the server responded with a status of 404 (Not Found) lines (preconnect to fonts.gstatic.com failing in headless — those are benign, the deterministic @font-face injection takes over for actual rendering).
Expected
Same render pipeline that succeeds with -w 1 should succeed with -w auto (workers are stateless seekers — there's no obvious reason a worker count should change correctness).
Hypothesis
Race condition between:
- HyperShader transition windows that require
html2canvas snapshots of both anchor scenes during seek
- The
<audio> element's canplaythrough readiness gate that the runtime waits on before emitting a frame
When two workers happen to seek into the same shader window simultaneously, one ends up blocked waiting on a canvas snapshot while another is blocked waiting on the same audio element's readiness — both time out before either resolves.
This would explain why:
- Pure-visual compositions (no audio) render fine in parallel
- Audio-only compositions (no shader) render fine in parallel
- Only the combination deadlocks
System
- HyperFrames CLI:
0.4.31 (latest at time of report)
- Node:
v24.13.0 (darwin arm64)
- macOS: 24.6.0
- CPU: Apple M4 Max, 16 cores
- RAM: 128 GB (>20 GB free during render)
- Chrome: system, version 147.0.7727.117
- FFmpeg: 8.1
- Workers chosen by
-w auto: 8 (per 16 cores detected)
Workaround
-w 1 reliably succeeds in ~2 min for a 12s composition with one shader and one audio clip. Trade-off is wall-clock time vs the parallel render's potential 5-8x speedup.
Suggested fixes (low confidence — open to discussion)
- Bump Puppeteer's
protocolTimeout for shader-transition windows specifically — let workers wait longer when they're inside a shader range.
- Serialize seeks within each shader window even when using parallel workers (one worker at a time inside the
[time, time+duration] interval).
- Detect the shader+audio combo at compile time and fall back to
-w 1 automatically with a warning, rather than letting the user discover this via a 12-minute timeout.
Happy to share more logs, tests, or a public repo with a minimal repro if useful. Thanks for the framework — hyperframes has been a great experience overall, just hit this one sharp edge.
Summary
hyperframes renderwith the default parallel worker pool (-w auto) consistently deadlocks when a composition combines aHyperShader.init()transition with at least one<audio>clip. All workers eventually time out withRuntime.callFunctionOn timed out. Falling back to-w 1renders the same composition cleanly.Repro
Minimal composition with one shader transition between two anchor scenes plus an audio clip.
Any short WAV/MP3 (~3s) at
narration.wavis enough — the bug is independent of audio content.Observed behavior
After ~12 minutes wall-clock, all workers fail with the same error:
Progress hangs at
25% Starting frame capturewith[non-blocking] Failed to load resource: the server responded with a status of 404 (Not Found)lines (preconnect tofonts.gstatic.comfailing in headless — those are benign, the deterministic@font-faceinjection takes over for actual rendering).Expected
Same render pipeline that succeeds with
-w 1should succeed with-w auto(workers are stateless seekers — there's no obvious reason a worker count should change correctness).Hypothesis
Race condition between:
html2canvassnapshots of both anchor scenes during seek<audio>element'scanplaythroughreadiness gate that the runtime waits on before emitting a frameWhen two workers happen to seek into the same shader window simultaneously, one ends up blocked waiting on a canvas snapshot while another is blocked waiting on the same audio element's readiness — both time out before either resolves.
This would explain why:
System
0.4.31(latest at time of report)v24.13.0(darwin arm64)-w auto: 8 (per16 cores detected)Workaround
-w 1reliably succeeds in ~2 min for a 12s composition with one shader and one audio clip. Trade-off is wall-clock time vs the parallel render's potential 5-8x speedup.Suggested fixes (low confidence — open to discussion)
protocolTimeoutfor shader-transition windows specifically — let workers wait longer when they're inside a shader range.[time, time+duration]interval).-w 1automatically with a warning, rather than letting the user discover this via a 12-minute timeout.Happy to share more logs, tests, or a public repo with a minimal repro if useful. Thanks for the framework —
hyperframeshas been a great experience overall, just hit this one sharp edge.