Skip to content

(bug) Render freezes & Chrome crash at 25% frame capture on low-memory Linux (video element) #1072

@O1Anas

Description

@O1Anas

Describe the bug

HyperFrames render process consistently freezes at 25% "Starting frame capture" phase when rendering a simple video element composition on a low-resource system (Intel i5-7200U, 8GB RAM, Intel HD 620 GPU, Lubuntu 24.04). Headless Chrome crashes, leading to "Protocol error (Runtime.callFunctionOn): Target closed".

Steps to reproduce

  1. System Configuration:
    • OS: Lubuntu 24.04 (Linux x64)
    • CPU: Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz (4 cores)
    • RAM: 8 GB (1.8 GB free reported by hyperframes doctor)
    • GPU: Intel HD Graphics 620
    • hyperframes CLI version: 0.6.43
  2. Composition: An HTML file with:
    • A single <video> element (8s, 1080x1920 MP4 or WEBM, muted, playsinline).
    • Minimal GSAP timeline (registered as window.__timelines["main"]).
    • No complex CSS, animations, or overlays.

Exact composition content:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8"><title>Mockup Video</title>
  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15/dist/gsap.min.js"></script>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { background: #000; overflow: hidden; }
    #stage { width: 1080px; height: 1920px; margin: 0 auto; background: #000; }
    video { width: 100%; height: 100%; object-fit: contain; }
  </style>
</head>
<body>
  <div id="stage" data-composition-id="mockup" data-width="1080" data-height="1920" data-start="0">
    <video id="mockup-video" muted playsinline data-track-index="0" data-start="0" data-duration="8" src="assets/mockup.mp4"></video>
  </div>
  <script>
    window.__timelines = window.__timelines || {};
    window.__timelines['mockup'] = gsap.timeline({ paused: true });
  </script>
</body>
</html>
  1. Command to reproduce:
    npx hyperframes render index.html -o output.mp4 --fps 30 --quality standard --workers 1 --browser-args="--disable-dev-shm-usage"
    (Also tried without browser-args and with/without --workers 1.)
  2. Observe:
    • Render progresses to 25% ("Starting frame capture") then freezes.
    • After ~30s, terminates: "Protocol error (Runtime.callFunctionOn): Target closed".
    • Earlier: "Auto-worker calibration failed; using conservative worker budget".

Expected behavior

HyperFrames should render the HTML composition into a video, or gracefully fail with a clear error if memory is insufficient, instead of crashing silently.

Actual behavior

Crash at 25% progress ("Starting frame capture"). Headless Chrome exits with: Protocol error (Runtime.callFunctionOn): Target closed. No clear explanation about memory exhaustion is given. See debug output and clues below.

Environment

✓ Version          0.6.43 (latest)
  ✓ Node.js          v22.22.2 (linux x64)
  ✓ CPU              4 cores · Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz @ 2342MHz
  ✗ Memory           7.6 GB total · 1.8 GB free
                     Low memory — renders may fail. Close other apps or increase RAM.
  ✓ Disk             41.8 GB free
  ✓ /dev/shm         3907 MB
  ✓ Environment      Native terminal
  ✓ FFmpeg           ffmpeg version 6.1.1-3ubuntu5 Copyright (c) 2000-2023 the FFmpeg developers
  ✓ FFprobe          ffprobe version 6.1.1-3ubuntu5 Copyright (c) 2007-2023 the FFmpeg developers
  ✓ Chrome           cache: /home/anas/.cache/hyperframes/chrome/chrome-headless-shell/linux-131.0.6778.85/chrome-headless-shell-linux64/chrome-headless-shell
  ✓ Docker           Docker version 29.1.3, build 29.1.3-0ubuntu3~24.04.2
  ✗ Docker running   Not running
                     Start Docker Desktop or run: sudo systemctl start docker

  ◇  Some checks failed — see hints above

Additional context

Auto-worker calibration failed, conservative worker budget used. Rendering a simple video composition on a low-memory machine triggers Chrome crash (see error above). Likely cause: memory exhaustion from managing a large number of high-res frames as <img> elements (see injectVideoFramesBatch, syncVideoFrameVisibility in /packages/engine/src/services/screenshotService.ts).

Possible solutions:

  1. Lazy Loading/Windowing: Only a small buffer of frames (e.g., current +/- 2–5) should be active in the DOM at once, substantially reducing memory and layout overhead compared to the current approach that accumulates hundreds of <img> elements per video.

  2. Dynamic src Updates: Instead of new <img> elements per frame, maintain a single persistent <img> element (or a tiny pool). Changing its src attribute each frame minimizes DOM bloat and allows Chrome to efficiently garbage collect previous image data.

  3. Off-screen Canvas Pre-loading: Preload upcoming frames into off-screen canvases or ImageBitmaps. This avoids oversized base64 data URIs, enables more efficient memory usage (raw pixel data is smaller), and can make frame display smoother on low-resource systems.

  4. Aggressive Memory Monitoring & Adaptive Quality: Check free system memory before each render. If low, automatically reduce rendered frame resolution, switch to JPEG from PNG, or drop frame cache size – gracefully degrading quality so renders complete instead of crashing.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions