Skip to content

snapshot can capture a <video> inside a vfx capture canvas before its seeked frame decodes (shows frame 0; rate rises with load) #4762

Closed

Description

@vanceingalls

Summary

hyperframes snapshot can capture a <video> that sits inside a vfx capture canvas (canvas[layoutsubtree].hf-vfx-src) before its seeked frame has decoded, so the snapshot shows the video's first frame instead of the frame at --at. It is a race whose rate rises with machine load: frozen in 10 of 10 snapshots at load 80+, and 4 of 10 at load 40–50. With the capture canvas replaced by a plain div, 0 of 12 snapshots froze at any load. hyperframes render shows the correct frame.

Observed on hyperframes 0.8.83, macOS (arm64), hyperframes snapshot . --no-browser-gpu --at 5 --no-end.

Minimal reproduction

Footage (synthetic): a white bar moving across black at 160 px/s. At t = 5 s the bar spans columns 800–1099, and at frame 0 it spans 0–299.

mkdir -p assets
ffmpeg -f lavfi -i "color=c=black:s=1920x1080:r=24:d=10" -f lavfi -i "color=c=white:s=300x1080:r=24:d=10" \
  -filter_complex "[0][1]overlay=x='t*160':y=0:shortest=1,format=yuv420p" -c:v libx264 -profile:v main -g 30 assets/clip.mp4

index.html (video inside a capture canvas feeding a one-node Noise data-vfx-chain):

<!doctype html>
<html lang="en"><head><meta charset="utf-8"><title>h264 in vfx capture canvas</title>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>html,body{margin:0;padding:0;background:#000}</style></head>
<body>
<div id="root" data-composition-id="main" data-width="1920" data-height="1080" data-duration="10" data-fps="24" style="position:relative;overflow:hidden;width:1920px;height:1080px;background:#000">
<canvas id="bg-vfxbd" layoutsubtree="" class="hf-vfx-src" data-vfx-for="bg" width="1920" height="1080" style="position:absolute;left:0;top:0;width:1920px;height:1080px"><div id="bg-vfxin" class="hf-vfx-in" style="position:absolute;left:0;top:0;width:1920px;height:1080px"><video id="vid" data-start="0" data-duration="10" data-track-index="16" src="assets/clip.mp4" muted="" style="position:absolute;left:0;top:0;width:1920px;height:1080px"></video></div></canvas>
<div id="bg" class="clip" data-start="0" data-duration="10" data-track-index="15" data-vfx-chain="{&quot;version&quot;:1,&quot;nodes&quot;:[{&quot;type&quot;:&quot;noise&quot;,&quot;id&quot;:&quot;n2&quot;,&quot;params&quot;:{&quot;useColorNoise&quot;:true,&quot;clipping&quot;:true}}]}" style="position:absolute;left:0;top:0;width:1920px;height:1080px;--vfx-n2-amount:0.2"></div>
</div>
<script>window.__timelines = window.__timelines || {}; window.__timelines["main"] = gsap.timeline({ paused: true });</script>
</body></html>

Control: replace the <canvas …>…</canvas> element with a plain wrapper, keeping the video:

<div style="position:absolute;inset:0"><div id="bg-vfxin" class="hf-vfx-in" style="position:absolute;left:0;top:0;width:1920px;height:1080px"><video id="vid" data-start="0" data-duration="10" data-track-index="16" src="assets/clip.mp4" muted="" style="position:absolute;left:0;top:0;width:1920px;height:1080px"></video></div></div>

Steps: run hyperframes snapshot . --no-browser-gpu --at 5 --no-end 10 times while the machine is busy (e.g. two other renders running in parallel). For each frame, check where the bright columns are: 800–1099 is correct (t = 5 s), 0–299 is frozen at frame 0.

Results

page frozen at frame 0 machine load
video inside capture canvas 8 of 8 110–140
canvas replaced by a div 0 of 6 80–115

On a larger page with the same structure: H.264 inside the canvas froze 4 of 10 at load 40–50, and VP9 (-g 12) inside the canvas froze 0 of 9.

On an idle machine it may not reproduce in a handful of runs, so please run it under load.

Expected

The captured video frame is the one at --at, as in hyperframes render and as with the canvas removed.

Activity

  1. Dante-dan commented on Sep 30, 2026

    @Dante-dan
    Contributor

    I would like to investigate and fix the snapshot-specific video/VFX race. My intended approach is to trace the ordering between the snapshot seek, video-frame readiness, and refreshing the VFX capture canvas, then make capture wait for the requested frame before that canvas is sampled. I would keep the change scoped to this ordering and avoid adding a fixed sleep.

    For verification I would use the moving-bar reproduction in this issue, including the capture-canvas and plain-div control under load, and check the expected columns at --at 5. I would also check that the render path still produces the requested frame. I have read the reproduction but have not yet reproduced it locally, so the exact fix still needs confirmation from that investigation.

    Please assign this to me if this scope fits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions