> ## Documentation Index
> Fetch the complete documentation index at: /llms.txt
> Use this file to discover all available pages before exploring further.

# @hyperframes/producer

> Render a HyperFrames project from Node.js.

`@hyperframes/producer` owns the complete render pipeline: compile the project,
capture its frames, encode the video, and mix its audio.

Use it when rendering is part of your own Node.js service or job runner. For a
local script or terminal workflow, use [`npx hyperframes render`](/developers/cli)
instead.

```bash
npm install @hyperframes/producer
```

## Render one project

`createRenderJob()` describes the render. `executeRenderJob()` receives that
job, the project directory, and the output path.

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

const job = createRenderJob({
  fps: 30,
  quality: "standard",
  format: "mp4",
});

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

The entry file defaults to `index.html`. Set `entryFile` when the project has a
different entry composition.

```ts
const job = createRenderJob({
  fps: { num: 30000, den: 1001 },
  quality: "high",
  entryFile: "compositions/launch.html",
});
```

## Choose an output

| Format | Best for | Audio |
| - | - | - |
| `mp4` | Default delivery and web playback | AAC |
| `webm` | Transparent video for the web | Opus |
| `mov` | Transparent ProRes 4444 for an editor | AAC |
| `gif` | Small silent previews | None |
| `png-sequence` | Lossless frames for another pipeline | AAC sidecar when needed |
| `hls` | VOD streaming from a playlist directory | AAC rendition |

HDR output is available for MP4 through `hdrMode`. Transparent formats render
in SDR because HDR and alpha are not one supported output path.

`chromaSubsampling: "444"` renders an SDR MP4 master with full-resolution
colour (H.264 yuv444p, lossless PNG capture). It needs the software encoder,
is rejected with `useGpu`, HDR output, other formats and distributed renders,
and many players cannot decode it. See
[4:4:4 masters](/guides/rendering.md#444-masters).

```ts
const job = createRenderJob({
  fps: 30,
  quality: "high",
  format: "mp4",
  hdrMode: "auto",
});
```

See [HDR rendering](/guides/hdr.md) for the source and delivery constraints.

### HLS

`format: "hls"` writes an HLS VOD stream into `outputPath`, which is a
directory: `master.m3u8`, `video.m3u8` and `video_NNNNN.ts`, plus `audio.m3u8`
and `audio_NNNNN.ts` when the composition has audio. The encode is the same
H.264 + AAC as MP4 — the packaging pass only stream-copies it into segments.

```ts
const job = createRenderJob({
  fps: 30,
  format: "hls",
  hlsSegmentSeconds: 4,
});
```

Segments are exactly `hlsSegmentSeconds` long except the last, because the
encoder GOP is locked to `round(fps × hlsSegmentSeconds)` frames so every
segment starts on a keyframe. HLS is SDR only (`hdrMode: "force-hdr"` is
rejected), refuses GPU encoding (GPU encoders ignore the keyframe lock), and is
not available in distributed rendering, Lambda, or Cloud Run.

## Inspect output validation

A matching output duration does not prove that every expected frame is present.
WebM and HLS often omit the frame count from their metadata. When no usable count
is available, the producer skips the frame-count check and continues checking
duration and requested dimensions.

Set `debug: true` in `createRenderJob()` or use `npx hyperframes render --debug`
to retain `render.log` in the render's working directory. Its
`artifact_frame_count_unavailable` debug event records the output path and
expected frame count whenever this check is skipped. Normal output stays quiet;
a custom `logger` can also collect the event through its `debug` method.

## Reference background

Completed jobs expose `reference_background`. In-process local render results, blocking and
streaming producer responses, and distributed plan and assembly results carry
this same additive field:

```json
{"reference_background":{"kind":"solid","rgb":[10,46,138]}}
```

The value describes the composition root and its surrounding page background at
the canonical initial film state. It is read from Chromium's computed styles
before transparent capture changes those styles, not inferred from rendered
frames or an inner panel. Distributed chunks preserve the plan's reference;
they do not choose a different reference at each chunk boundary.

| Kind | Meaning |
| - | - |
| `solid` | An opaque computed background, with three integer sRGB components from 0 to 255. |
| `none` | A positively established `background: none` declaration with no effective paint. |
| `unsupported_non_solid` | An effective background image or gradient. |
| `unknown` | Missing, unresolved, changing, translucent, or otherwise indeterminate background. |

A transparent computed color alone does **not** establish `none`. An undeclared
background, an authored transparent color, or an inaccessible stylesheet that
prevents proving absence remains `unknown`. Gradients and images are never
replaced with their underlying color. Consumers must preserve indeterminate
results instead of treating them as a default color or proof of a blank frame.

In-process local rendering reads from a session it already opens. The Docker CLI
wrapper does not currently return the child process's result metadata. Distributed planning
opens an inspection-only page even for a composition whose duration was
resolved statically. It does not require WebGPU, run BeginFrame liveness checks,
or reconcile media on that page. An unavailable browser or failed inspection
produces `unknown` without rejecting an otherwise valid static plan. The value
is stored in the execution-plan artifacts, not added to the hashed v2 transport
manifest, so older readers and materializers remain compatible.

## Cancel a render

Pass an abort signal to the final argument:

```ts
const controller = new AbortController();

await executeRenderJob(
  job,
  "./my-hyperframes-project",
  "./renders/video.mp4",
  undefined,
  controller.signal,
);

// Call controller.abort() from your cancellation path.
```

An aborted render throws `RenderCancelledError`. Correctness warnings can also
block a render when `strictness: "strict"` is enabled.

## Build a render service

The package exports a Hono application and server helpers:

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

await startServer({ port: 8080 });
```

For larger workloads, the `@hyperframes/producer/distributed` entry exposes the
three rendering activities—`plan`, `renderChunk`, and `assemble`. Your
orchestrator remains responsible for dispatch, retries, storage, and networking.

## Related topics

> [card] [Choose rendering infrastructure](/deploy/overview.md)
>
> Decide between local, server, Temporal, AWS, or another adapter.

> [card] [@hyperframes/engine](/packages/engine.md)
>
> Use the lower-level capture and encoding primitives.
