Describe the bug
Re-opening the #2477 symptom on 0.8.72: parallel capture emits a fully transparent frame at each worker shard's first frame. The fix PR #2489 was closed unmerged, and discardWarmupCapture still has zero call sites on current main (a3d7736) — only its definition in frameCapture.ts and the re-export in engine/src/index.ts.
Observed with a transparent ProRes 4444 render of a canvas/DOM overlay composition (120 frames @ 24fps, --workers auto → 4 workers, screenshot capture mode): frames 30, 60, 90 decode fully transparent — exactly shardStart = w * (totalFrames / workers) for workers 1–3. Worker 0's first frame is naturally identical to t=0 so it's invisible in the alpha scan. --workers 1 produces zero blank frames; the anomaly is deterministic in position across parallel runs but intermittent (some runs clean), matching the paint-settle race described in #2477.
Notable difference from the original report's setup: our composition registers a non-GSAP duck-typed RuntimeTimelineLike on window.__timelines ({duration, time, seek, totalTime, pause, paused, play, add, set} — pure seek→redraw, no GSAP). If the post-#2477 warm-up logic engages only for GSAP timelines or on a code path duck timelines don't reach, that would explain why the regression survives for this comp shape.
Steps to reproduce
The race is timing-sensitive — a heavier page (custom fonts, canvas setup, larger script payload) widens the window. Composition shape that fails for us ~half the time at 4 workers:
<div id="root" data-composition-id="repro" data-duration="4"
data-width="1920" data-height="1080">
<div id="box"></div>
</div>
<script>
var t = 0;
window.__timelines = window.__timelines || {};
window.__timelines["repro"] = {
duration: function(){ return 4; },
time: function(){ return t; },
seek: function(s){ // pure seek->redraw contract
t = Math.max(0, Number(s) || 0);
// visible whenever t>0; only t=0 state is blank
document.getElementById("box").style.opacity = t > 0.02 ? "1" : "0";
},
totalTime: function(s){ this.seek(s); },
pause: function(){}, paused: function(){ return true; },
play: function(){}, add: function(){}, set: function(){}
};
</script>
Detection (alpha scan, same method as #2477):
ffmpeg -i out.mov -vf "alphaextract,signalstats,metadata=mode=print" -f null -
# frames at w * ceil(totalFrames/workers) show alpha mean ~0 between normal neighbors
Expected behavior
Every worker's first captured frame contains the seeked state — e.g. wire discardWarmupCapture into the parallel worker path (it already exists and is exported), or gate the first captureFrame on a post-seek paint commit rather than the seek alone.
Actual behavior
First frame of each non-zero worker shard captures the pre-seek page state (t=0). For fade-in/transparent overlays that state is fully transparent, producing visible flicker in the baked output.
Environment
hyperframes@0.8.72 (managed npm install)
- macOS arm64, chrome-headless-shell 152.0.7977.30 (CLI-managed cache)
screenshot capture mode (drawelement not engaged for this comp)
- Output:
--format mov (ProRes 4444, yuva444p12le), --fps 24
Additional context
Workaround confirmed on our side: --workers 1 removes the anomaly entirely (verified across a 37-template regression suite with per-frame RGBA diffing). Filing mainly so the #2477 fix can land — the reporter there already wrote it.
Describe the bug
Re-opening the #2477 symptom on 0.8.72: parallel capture emits a fully transparent frame at each worker shard's first frame. The fix PR #2489 was closed unmerged, and
discardWarmupCapturestill has zero call sites on currentmain(a3d7736) — only its definition inframeCapture.tsand the re-export inengine/src/index.ts.Observed with a transparent ProRes 4444 render of a canvas/DOM overlay composition (120 frames @ 24fps,
--workers auto→ 4 workers, screenshot capture mode): frames 30, 60, 90 decode fully transparent — exactlyshardStart = w * (totalFrames / workers)for workers 1–3. Worker 0's first frame is naturally identical to t=0 so it's invisible in the alpha scan.--workers 1produces zero blank frames; the anomaly is deterministic in position across parallel runs but intermittent (some runs clean), matching the paint-settle race described in #2477.Notable difference from the original report's setup: our composition registers a non-GSAP duck-typed
RuntimeTimelineLikeonwindow.__timelines({duration, time, seek, totalTime, pause, paused, play, add, set}— pure seek→redraw, no GSAP). If the post-#2477 warm-up logic engages only for GSAP timelines or on a code path duck timelines don't reach, that would explain why the regression survives for this comp shape.Steps to reproduce
The race is timing-sensitive — a heavier page (custom fonts, canvas setup, larger script payload) widens the window. Composition shape that fails for us ~half the time at 4 workers:
Detection (alpha scan, same method as #2477):
Expected behavior
Every worker's first captured frame contains the seeked state — e.g. wire
discardWarmupCaptureinto the parallel worker path (it already exists and is exported), or gate the firstcaptureFrameon a post-seek paint commit rather than the seek alone.Actual behavior
First frame of each non-zero worker shard captures the pre-seek page state (t=0). For fade-in/transparent overlays that state is fully transparent, producing visible flicker in the baked output.
Environment
hyperframes@0.8.72(managed npm install)screenshotcapture mode (drawelement not engaged for this comp)--format mov(ProRes 4444, yuva444p12le),--fps 24Additional context
Workaround confirmed on our side:
--workers 1removes the anomaly entirely (verified across a 37-template regression suite with per-frame RGBA diffing). Filing mainly so the #2477 fix can land — the reporter there already wrote it.