Files
hyperframes/packages/engine
Miguel Ángel e25394d38e perf(engine): wait for a missing composition timeline once per render, not once per browser (#5227)
* perf(engine): wait for a missing composition timeline once per render, not once per browser

A composition host that never registers a timeline (CSS, canvas or rAF animation
without data-no-timeline) made every capture session of a render wait the full
player-ready timeout: calibration, then the workers, then each static-frame check
page. The first session that times out now records the host ids in a memo shared
by the render's sessions; later sessions report the same timeout warning without
waiting again.

* perf(engine): the static-frame check page stops on a failed script and chunks share one memo

The verification page now gets the session's failed-script list, so it bails after
the short grace like the main page instead of waiting the full timeout. Distributed
chunks share one timeline-wait memo across their sessions. The poll takes an
options object instead of eight positional parameters.

* fix(engine): replay only a clean timeline timeout so a script failure still fails the render

A session that skipped the wait classified it at once, before a late page error
could arrive, so a render that used to stop on a script failure could ship. The
memo is now written only by a wait that ran its full length with no page errors
and no failed script loads; a replaying session that already sees a failed script
load reports a script failure.
2026-10-09 16:34:12 +00:00
..
2026-10-08 23:28:49 +00:00
2026-03-21 22:43:56 -07:00

@hyperframes/engine

Seekable web-page-to-video rendering engine built on Puppeteer and FFmpeg.

Framework-agnostic: works with GSAP, Lottie, Three.js, CSS animations, or any web content that implements the window.__hf seek protocol.

Install

npm install @hyperframes/engine

Requirements: Node.js >= 22, Chrome/Chromium (auto-downloaded by Puppeteer), FFmpeg

What it does

The engine opens your HTML composition in a headless Chrome instance, seeks frame-by-frame using Chrome's HeadlessExperimental.beginFrame API, captures screenshots, and encodes them into video with FFmpeg.

Key services

Service Description
browserManager Launches and pools headless Chrome instances (chrome-headless-shell)
frameCapture Manages capture sessions — seek, screenshot, buffer lifecycle
screenshotService BeginFrame-based capture with CDP (Chrome DevTools Protocol)
chunkEncoder FFmpeg encoding with chunked concat, GPU detection, faststart
streamingEncoder Pipe frames to FFmpeg in real time (no intermediate PNGs on disk)
audioMixer Parse <audio> elements and mix audio tracks via FFmpeg
videoFrameExtractor Extract frames from <video> elements for compositing
parallelCoordinator Split frame ranges across worker processes
fileServer Serve local HTML files to the browser via Hono

Usage

import {
  acquireBrowser,
  createCaptureSession,
  initializeSession,
  captureFrame,
  closeCaptureSession,
} from "@hyperframes/engine";

// 1. Launch browser
const browserLease = await acquireBrowser({ captureMode: "beginFrame" });

// 2. Open a capture session
const session = createCaptureSession({
  browser: browserLease.browser,
  url: "http://localhost:3000/my-composition.html",
  width: 1920,
  height: 1080,
  fps: 30,
});
await initializeSession(session);

// 3. Capture frames
for (let i = 0; i < totalFrames; i++) {
  await captureFrame(session, i, `/tmp/frames/frame-${i}.png`);
}

// 4. Clean up
await closeCaptureSession(session);
await browserLease.release();

Most users should use @hyperframes/producer or the hyperframes CLI instead of calling the engine directly.

Documentation

Full documentation: hyperframes.heygen.com/packages/engine