@hyperframes/producer

中文文档
0.8.226发布于 2026/11/01TypeScriptES Module构建来源已验证

HTML-to-video rendering engine using Chrome's BeginFrame API

原始使用文档
文档目录16 节
  1. Install
  2. Usage
  3. Render a video
  4. Run as an HTTP server
  5. Configuration
  6. Transparent Video Output
  7. Example
  8. What "transparent background" means here
  9. Caveats
  10. Authoring transparent compositions
  11. HLS Output
  12. Distributed rendering
  13. How it works
  14. Where each step lives
  15. Documentation
  16. Related packages

@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({
  fps: 30,
  quality: "standard",
});

await executeRenderJob(job, "./my-project", "./output.mp4", (currentJob) => {
  console.log(`${Math.round(currentJob.progress)}%`);
});

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 or /render/stream with projectDir, html, or previewUrl (public HTTPS only) and render options

Both HTTP routes accept optional pageMotionBlur: "off" to disable authored page motion blur. Omit it to preserve the composition's behavior. Other explicit values are rejected before a render starts. This is separate from engine sub-frame motionBlur sampling.

Configuration

RenderConfig controls the render pipeline:

OptionDefaultDescription
entryFileindex.htmlEntry composition inside the project directory. Its authored dimensions set the canvas size.
fpsrequiredFrame rate as an integer or exact { num, den } rational.
qualityrequiredEncoder preset ("draft", "standard", "high").
pageMotionBlurauthored settingSet "off" to disable page motion blur; omit to preserve authored behavior.
format"mp4"Output container — "mp4", "webm", "mov", "gif", "png-sequence", or "hls". See Transparent Video Output and HLS Output below.
hlsSegmentSeconds4HLS target segment length in whole seconds. Ignored for every other format.
videoFrameFormat"auto"Source video frame extraction format — "auto", "jpg", or "png". Use "png" for UI recordings, screen captures, and color-sensitive source videos.

Transparent Video Output

The producer can render HTML compositions to formats that carry a true alpha channel — not chroma key. The same composition that renders an opaque MP4 renders a layerable overlay when you set format.

formatCodec / pixel formatAlphaAudioUse case
"mp4" (default)H.264 (yuv420p) or H.265 + HDR10NoAACStreaming, social, default deliverable
"webm"VP9 + yuva420pTrue alphaOpusWeb playback as overlay (<video> over background); supported in Chrome, Edge, Firefox
"mov"ProRes 4444 + yuva444p10leTrue alphaAACEditor ingest (Premiere, Final Cut Pro, DaVinci Resolve)
"png-sequence"Numbered RGBA PNGs in a directoryLossless alphaSidecar audio.m4aAfter Effects / Nuke / Fusion, or pipelines that post-process frames before encoding
"hls"H.264 (yuv420p) in MPEG-TSNoAAC renditionVOD streaming — see HLS Output

Example

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

const job = createRenderJob({
  fps: 30,
  quality: "standard",
  format: "webm", // "mp4" | "webm" | "mov" | "png-sequence"
});

await executeRenderJob(job, "./my-project", "./output.webm");

What "transparent background" means here

The producer captures Chrome screenshots with the page background forced transparent (html, body, [data-composition-id] { background: transparent !important }) and the CDP default background override set to RGBA 0,0,0,0. The captured PNGs carry a real alpha channel and that channel is preserved end-to-end:

  • VP9 (webm) is encoded with -pix_fmt yuva420p, -auto-alt-ref 0, -cpu-used 4 by default, and alpha_mode=1 metadata. Tune the speed/quality tradeoff with PRODUCER_VP9_CPU_USED (-8 to 8) or local CLI --vp9-cpu-used.
  • ProRes 4444 (mov) uses -pix_fmt yuva444p10le and carries a separate alpha plane. Chrome's RGBA capture, including alpha, is 8 bits per channel; the 10-bit pixel format adds no capture precision.
  • PNG sequences are written without re-encoding (zero-padded frame_NNNNNN.png).

This is not chroma keying. There is no green/blue background to remove and no "key" tolerance to tune — pixels that were transparent in the browser are transparent in the output.

Caveats

  • Linux + alpha forces screenshot capture. Chrome's BeginFrame compositor (the default deterministic capture path on Linux headless-shell) does not preserve alpha; the orchestrator falls back to Page.captureScreenshot, which is slower per frame. macOS and Windows already use screenshot mode by default, so they are unaffected.
  • HDR + alpha is not supported. Setting hdr: true together with an alpha-capable format logs a warning and falls back to SDR. Use format: "mp4" for HDR10 output.
  • png-sequence does not produce a single muxed file. When the composition contains audio elements, an audio.m4a sidecar is written alongside the PNGs in outputPath.
  • Safari + WebM alpha is incomplete. For broad browser playback of an alpha video, ship format: "mov" to your editor and re-encode for the codec your distribution target supports.

Authoring transparent compositions

Don't paint a fullscreen background in your HTML. The default body background is overridden to transparent automatically — any body { background: ... }, #root { background: ... }, or [data-composition-id] { background: ... } rule is force-overridden during alpha rendering. Backgrounds on inner elements (cards, scenes, components) are kept.

HLS Output

format: "hls" packages the same H.264 + AAC encode as mp4 as an HLS VOD stream. outputPath is a directory:

master.m3u8      # references both renditions
video.m3u8       # video media playlist
video_00000.ts   # MPEG-TS segments, one IDR frame at the start of each
audio.m3u8       # written only when the composition has audio
audio_00000.ts
const job = createRenderJob({
  fps: 30,
  quality: "standard",
  format: "hls",
  hlsSegmentSeconds: 4,
});
await executeRenderJob(job, "./my-project", "./renders/stream");

Segments are exactly hlsSegmentSeconds long except the last: the encoder's GOP is locked to round(fps × hlsSegmentSeconds) frames so the packaging pass can stream-copy (-c copy) and still cut on keyframes. There is no second encode.

Constraints:

  • SDR only. hdrMode: "force-hdr" is rejected; HDR10 would need fMP4 segments and HEVC. Use format: "mp4" for HDR.
  • Software encoder only. useGpu: true is rejected — GPU encoders ignore the forced-keyframe lock that fixed-length segments depend on.
  • Not available in distributed rendering, including Lambda and Cloud Run.
  • MPEG-TS carries no mov-family metadata, so the render provenance tags written into MP4 output are absent.

Distributed rendering

For renders too large for a single machine, the producer ships a public set of distributed-render primitives. They are pure functions over local file paths — networking and orchestration live in adapter packages (Temporal, AWS Lambda + Step Functions, Cloud Run Jobs, K8s Jobs).

Plan v2 is recommended for new integrations. It publishes an immutable manifest plus content-addressed artifacts and materializes only each worker's declared dependencies:

import { planV2, renderChunkV2, assembleV2 } from "@hyperframes/producer/distributed";

// Controller-side: produce a v2 manifest + local content-addressed store.
const planResult = await planV2(
  projectDir,
  { fps: 30, width: 1920, height: 1080, format: "mp4" },
  "/tmp/plan-v2",
);

const chunk = await renderChunkV2("/tmp/plan-v2", 0, "/tmp/chunks/0.mp4");

// Controller-side: stitch chunks into the final deliverable.
await assembleV2("/tmp/plan-v2", ["/tmp/chunks/0.mp4", "/tmp/chunks/1.mp4"], "/tmp/output.mp4");

Cloud adapters should use planV2WithPublisher() so artifacts publish directly to object storage. The legacy plan() / renderChunk() / assemble() v1 layout remains supported, and cloud SDKs still interpret an omitted protocol as v1 for backwards compatibility.

The activity functions plus their result types are also re-exported from @hyperframes/producer so callers that pin the main package don't need a separate subpath import. Supported formats: mp4 SDR, mov ProRes 4444, and png-sequence. webm and HDR mp4 trip a typed FormatNotSupportedInDistributedError — use the in-process renderer (executeRenderJob) for those.

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 (or Page.captureScreenshot for transparent / non-Linux renders), captures screenshots
  3. Encode — pipes frames through FFmpeg (with GPU encoder detection and chunked concat). Skipped for format: "png-sequence".
  4. Mix — extracts <audio> elements and mixes them into the final video. For png-sequence, audio is written as an audio.m4a sidecar.
  5. Finalize — applies faststart for streaming-friendly MP4 (no-op for WebM, MOV, and png-sequence). For hls, stream-copies the encode into playlists and MPEG-TS segments instead.

Where each step lives

executeRenderJob in src/services/renderOrchestrator.ts runs the stages in src/services/render/stages/, in this order:

  • compileStage.ts, probeStage.ts, extractVideosStage.ts, audioStage.ts
  • src/services/render/captureCost.ts sizes the worker count (runCaptureCalibration, then resolveRenderWorkerCount)
  • createCapturePlan in src/services/render/capturePlan.ts picks the capture kind: streaming, disk, segmented or HDR layered
  • capture: captureStreamingStage.ts, captureSegmentedStage.ts and captureHdrStage.ts encode as they capture; disk capture (captureStage.ts) encodes afterwards in encodeStage.ts
  • assembleStage.ts, except for PNG sequences and GIF, which are written directly

The job's producerConfig is used when present; otherwise resolveConfig in packages/engine/src/config.ts builds it. Capture, encoding and browser primitives are engine services (see packages/engine/README.md).

Documentation

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

版本与发布记录

@hyperframes/producer 共发布 581 个版本,首次发布于 2026/03/23;过去 12 个月发布了 581 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.8.226latest2026/11/012 天前
0.8.2252026/11/012 天前
0.8.2242026/11/012 天前
0.8.2232026/10/313 天前
0.8.2222026/10/313 天前
0.8.2212026/10/304 天前

其他发布通道:alpha → 0.7.67-alpha.0,desktop → 0.8.53-desktop.0,hdr → 0.8.190-hdr.1。

关于 @hyperframes/producer 的常见问题

@hyperframes/producer 是什么?

使用Chrome的BeginFrame API的html到视频渲染引擎

如何安装 @hyperframes/producer?

在项目目录运行 npm install @hyperframes/producer,或使用 pnpm add / yarn add / bun add。

@hyperframes/producer 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@hyperframes/producer 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@hyperframes/producer 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@hyperframes/producer 可以商用吗?

@hyperframes/producer 使用 未声明 许可。package.json 没有声明许可证。没有许可证的代码默认保留全部权利,商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括,不构成法律意见,请以许可原文为准。

@hyperframes/producer 还在维护吗?

最新版本 0.8.226 发布于 2026/11/01(2 天前);至今共发布 581 个版本,其中过去 12 个月发布了 581 个;首次发布于 2026/03/23。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。