Skip to main content
The engine package provides the low-level video capture pipeline: it loads an HTML page in headless Chrome, seeks to each frame independently, and captures pixel buffers using Chrome’s HeadlessExperimental.beginFrame API. This is the layer that makes Hyperframes rendering deterministic.
npm install @hyperframes/engine

When to Use

Most users should NOT use the engine directly. Use the CLI (npx hyperframes render) or the producer package instead — they handle runtime injection, audio mixing, and encoding for you.
Use @hyperframes/engine when you need to:
  • Build a custom rendering pipeline with full control over frame capture
  • Integrate Hyperframes capture into an existing video processing system
  • Capture individual frames (e.g., for thumbnails or sprite sheets) without encoding to video
  • Implement a custom encoding backend (not FFmpeg)
Use a different package if you want to:
  • Render an HTML composition to a finished MP4 — use the producer or CLI
  • Preview compositions in the browser — use the CLI or studio
  • Lint or parse composition HTML — use core

How It Works

The engine implements a seek-and-capture loop that is fundamentally different from screen recording:
1

Launch headless Chrome

The engine starts chrome-headless-shell, a minimal headless Chrome binary optimized for programmatic control via the Chrome DevTools Protocol (CDP).
2

Load the composition

Your HTML composition is loaded into a browser page. The Hyperframes runtime is injected to manage timeline seeking.
3

Seek to each frame

For every frame in the video (e.g., 900 frames for a 30-second video at 30fps), the engine calls renderSeek(time) to advance the composition to the exact timestamp. No wall clock is involved — each frame is independently positioned.
4

Capture via BeginFrame

Chrome’s HeadlessExperimental.beginFrame API captures the compositor output as a pixel buffer. This produces pixel-perfect frames without any screen recording artifacts.
5

Hand off frames

Captured frame buffers are passed to a consumer — typically FFmpeg (via the producer) for encoding into MP4, but you can provide your own consumer.
This approach guarantees deterministic rendering: the same HTML always produces the identical video, regardless of system load or timing.

Configuration

import type { EngineConfig } from '@hyperframes/engine';

const config: EngineConfig = {
  fps: 30,              // Frames per second: 24, 30, or 60
  width: 1920,          // Output width in pixels
  height: 1080,         // Output height in pixels
  quality: 'standard',  // Encoding preset: 'draft', 'standard', or 'high'
};

Quality Presets

PresetUse CaseSpeed
draftFast iteration during developmentFastest
standardProduction renders with good quality/speed balanceModerate
highFinal delivery, maximum qualitySlowest

FPS Options

FPSUse Case
24Cinematic look, smaller file size
30Standard web video, good balance
60Smooth motion, UI animations, screen recordings

Programmatic Usage

import { createEngine } from '@hyperframes/engine';

const engine = createEngine({
  fps: 30,
  width: 1920,
  height: 1080,
});

// Capture all frames from a composition
const frames = await engine.capture('./my-video/index.html');

// Each frame is a pixel buffer (PNG/raw)
for (const frame of frames) {
  // Process frames however you need:
  // - pipe to FFmpeg
  // - save as individual PNGs
  // - generate a thumbnail
  // - feed into a custom encoder
}

await engine.close();

Key Concepts

BeginFrame Rendering

Traditional screen capture records at wall-clock speed — if your system is under load, frames get dropped. The engine uses Chrome’s HeadlessExperimental.beginFrame to explicitly advance the compositor, producing each frame on demand. This means:
  • No dropped frames — every frame is captured
  • No timing dependency — a 60-second video does not take 60 seconds to capture
  • Pixel-perfect output — the compositor produces the exact pixels it would display
For more on how this enables deterministic output, see Deterministic Rendering.

Seek Contract

The engine relies on the Hyperframes runtime’s renderSeek(time) function. When called, renderSeek:
  1. Pauses all GSAP timelines
  2. Seeks every timeline to the exact timestamp
  3. Updates all media elements (video, audio) to match
  4. Mounts/unmounts clips based on their data-start and data-duration
This contract is what makes frame-by-frame capture possible — each frame is a complete, independent snapshot of the composition at that point in time.

Chrome Requirements

The engine requires chrome-headless-shell, which is included when you install the package. It uses a pinned Chrome version to ensure consistent rendering across environments. For fully deterministic output (including fonts), use Docker mode via the producer.

Producer

Wraps the engine with runtime injection, FFmpeg encoding, and audio mixing for complete MP4 output.

Core

Provides the types, runtime, and linter that the engine depends on.

CLI

The easiest way to render — calls the producer (and engine) under the hood.

Studio

Visual editor for building compositions before rendering them with the engine.