Files
hyperframes/packages/producer
Miguel ÁngelandClaude Opus 4.6 2bde83d5e3 fix(producer): externalize postcss to fix ESM dynamic require
postcss is a CJS module that uses `require("path")` internally.
When esbuild bundles it into the ESM producer output, the dynamic
require call fails at runtime with "Dynamic require of path is
not supported". This broke `npx tsx cli render` and `npx tsx cli
preview` from the local dev build.

Fix: add postcss to the external list alongside puppeteer and
esbuild — it stays as a runtime dependency resolved from
node_modules instead of being bundled.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:18:25 +00:00
..
2026-04-03 04:36:19 +00:00

@hyperframes/producer

Full HTML-to-video rendering pipeline: capture frames with Chrome's BeginFrame API, encode with FFmpeg, mix audio — all in one call.

Install

npm install @hyperframes/producer

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

Usage

Render a video

import { createRenderJob, executeRenderJob } from "@hyperframes/producer";

const job = createRenderJob({
  inputPath: "./my-composition.html",
  outputPath: "./output.mp4",
  width: 1920,
  height: 1080,
  fps: 30,
});

const result = await executeRenderJob(job, (progress) => {
  console.log(`${Math.round(progress.percent * 100)}%`);
});

console.log(result.outputPath); // ./output.mp4

Run as an HTTP server

The producer can also run as a render server, accepting render requests over HTTP:

import { startServer } from "@hyperframes/producer";

await startServer({ port: 8080 });
// POST /render with a RenderConfig body

Configuration

RenderConfig controls the render pipeline:

Option Default Description
inputPath Path to the HTML composition
outputPath Output video file path
width 1920 Frame width in pixels
height 1080 Frame height in pixels
fps 30 Frames per second (24, 30, or 60)
quality "standard" Encoder preset ("draft", "standard", "high")

How it works

  1. Serve — spins up a local file server for the HTML composition
  2. Capture — opens the page in headless Chrome, seeks frame-by-frame via HeadlessExperimental.beginFrame, captures screenshots
  3. Encode — pipes frames through FFmpeg (with GPU encoder detection and chunked concat)
  4. Mix — extracts <audio> elements and mixes them into the final video
  5. Finalize — applies faststart for streaming-friendly MP4

Documentation

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