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

# Render orchestrator

> Distributed rendering engine for parallel video generation

The `RenderOrchestrator` class enables distributed rendering by splitting a composition into multiple chunks that can be rendered in parallel. This dramatically reduces rendering time for long videos on multi-core systems.

## How it works

1. **Plan**: Divides the composition into frame ranges based on concurrency
2. **Render**: Executes chunks in parallel using worker renderers
3. **Concatenate**: Stitches chunk videos into a single file
4. **Mix**: Applies final audio mixing and encoding

## Static methods

### plan

```typescript theme={null}
static plan(
  compositionUrl: string,
  outputPath: string,
  options: DistributedRenderOptions
): RenderPlan
```

Creates a render plan without executing it. Useful for previewing the distribution strategy.

**compositionUrl** `string` *required*

URL of the HTML composition to render.

**outputPath** `string` *required*

Final output file path for the rendered video.

**options** `DistributedRenderOptions` *required*

Configuration object extending RendererOptions with distributed rendering settings.

#### Returns

A `RenderPlan` object containing:

**totalFrames** `number`

Total number of frames in the composition.

**chunks** `RenderChunk[]`

Array of chunk specifications, each containing:

* `id`: Chunk identifier
* `startFrame`: First frame in this chunk
* `frameCount`: Number of frames to render
* `outputFile`: Temporary file path for chunk output
* `options`: RendererOptions for this specific chunk

**concatManifest** `string[]`

List of chunk file paths to concatenate.

**concatOutputFile** `string`

Temporary file path for the concatenated video (before final audio mix).

**finalOutputFile** `string`

Final output path (same as the outputPath parameter).

**mixOptions** `RendererOptions`

Options for the final audio mixing pass.

**cleanupFiles** `string[]`

List of temporary files to delete after successful rendering.

#### Example

```typescript theme={null}
import { RenderOrchestrator } from '@helios/renderer';

const plan = RenderOrchestrator.plan(
  'file:///composition.html',
  './output.mp4',
  {
    width: 1920,
    height: 1080,
    fps: 30,
    durationInSeconds: 60,
    concurrency: 4
  }
);

console.log(`Rendering ${plan.totalFrames} frames across ${plan.chunks.length} chunks`);
plan.chunks.forEach(chunk => {
  console.log(`Chunk ${chunk.id}: frames ${chunk.startFrame}-${chunk.startFrame + chunk.frameCount}`);
});
```

### render

```typescript theme={null}
static async render(
  compositionUrl: string,
  outputPath: string,
  options: DistributedRenderOptions,
  jobOptions?: RenderJobOptions
): Promise<void>
```

Executes a distributed render job.

**compositionUrl** `string` *required*

URL of the HTML composition to render.

**outputPath** `string` *required*

Final output file path for the rendered video.

**options** `DistributedRenderOptions` *required*

Configuration object extending RendererOptions with distributed rendering settings.

**jobOptions** `RenderJobOptions`

Optional job configuration including progress callbacks and abort signals.

#### Example

```typescript theme={null}
await RenderOrchestrator.render(
  'file:///composition.html',
  './output.mp4',
  {
    width: 1920,
    height: 1080,
    fps: 30,
    durationInSeconds: 120,
    concurrency: 8,
    audioFilePath: './music.mp3'
  },
  {
    onProgress: (progress) => {
      console.log(`Overall progress: ${(progress * 100).toFixed(1)}%`);
    }
  }
);
```

## Distributed render options

```typescript theme={null}
interface DistributedRenderOptions extends RendererOptions {
  concurrency?: number;
  executor?: RenderExecutor;
}
```

**concurrency** `number`

Number of parallel render workers. Defaults to `os.cpus().length - 1`.

**executor** `RenderExecutor`

Custom executor for running render chunks. Defaults to `LocalExecutor` which runs chunks as parallel processes on the local machine. Implement custom executors for cloud-based or distributed execution.

## Concurrency behavior

### Automatic concurrency

```typescript theme={null}
// Uses CPU count - 1 workers
await RenderOrchestrator.render(
  'file:///composition.html',
  './output.mp4',
  {
    width: 1920,
    height: 1080,
    fps: 30,
    durationInSeconds: 60
    // concurrency not specified
  }
);
```

### Single worker fallback

When `concurrency: 1`, the orchestrator skips chunking and uses a standard `Renderer` directly:

```typescript theme={null}
await RenderOrchestrator.render(
  'file:///composition.html',
  './output.mp4',
  {
    width: 1920,
    height: 1080,
    fps: 30,
    durationInSeconds: 60,
    concurrency: 1 // No parallelization
  }
);
```

### Custom concurrency

```typescript theme={null}
await RenderOrchestrator.render(
  'file:///composition.html',
  './output.mp4',
  {
    width: 1920,
    height: 1080,
    fps: 30,
    durationInSeconds: 60,
    concurrency: 16 // Use 16 workers
  }
);
```

## Audio handling in distributed mode

The orchestrator uses a multi-stage audio pipeline:

### Chunk rendering

1. Each chunk renders with **PCM audio** (`pcm_s16le` codec)
2. Audio tracks are **not** mixed during chunk rendering
3. Chunks output to `.mov` container format to preserve PCM audio

### Concatenation

1. Chunk videos are concatenated without re-encoding
2. PCM audio streams are preserved in the concatenated file

### Final mix

1. Concatenated video is processed with final audio options
2. Audio tracks from `audioFilePath` and `audioTracks` are mixed
3. Output is encoded with specified `audioCodec` (default: `aac`)
4. Final video uses the container format from `outputPath` extension

#### Example with audio

```typescript theme={null}
await RenderOrchestrator.render(
  'file:///composition.html',
  './output.mp4',
  {
    width: 1920,
    height: 1080,
    fps: 30,
    durationInSeconds: 60,
    concurrency: 4,
    audioTracks: [
      {
        path: './music.mp3',
        volume: 0.7,
        fadeInDuration: 2,
        fadeOutDuration: 3
      },
      {
        path: './sfx.wav',
        offset: 10,
        volume: 1.0
      }
    ],
    audioCodec: 'aac',
    audioBitrate: '192k'
  }
);
```

## Progress tracking

The orchestrator aggregates progress from all workers:

```typescript theme={null}
const progressBar = new Map<number, number>();

await RenderOrchestrator.render(
  'file:///composition.html',
  './output.mp4',
  {
    width: 1920,
    height: 1080,
    fps: 30,
    durationInSeconds: 60,
    concurrency: 4
  },
  {
    onProgress: (globalProgress) => {
      // globalProgress is weighted by chunk size
      console.log(`Overall: ${(globalProgress * 100).toFixed(1)}%`);
    }
  }
);
```

Each worker's progress is weighted by its frame count, ensuring accurate overall progress reporting.

## Error handling and cancellation

### Abort signal

```typescript theme={null}
const controller = new AbortController();

const renderPromise = RenderOrchestrator.render(
  'file:///composition.html',
  './output.mp4',
  {
    width: 1920,
    height: 1080,
    fps: 30,
    durationInSeconds: 60,
    concurrency: 4
  },
  {
    signal: controller.signal
  }
);

// Cancel after 10 seconds
setTimeout(() => controller.abort(), 10000);

try {
  await renderPromise;
} catch (error) {
  if (error.message === 'Aborted') {
    console.log('Render was cancelled');
  }
}
```

### Worker failure propagation

If any worker fails, all other workers are immediately aborted:

```typescript theme={null}
try {
  await RenderOrchestrator.render(
    'file:///composition.html',
    './output.mp4',
    {
      width: 1920,
      height: 1080,
      fps: 30,
      durationInSeconds: 60,
      concurrency: 4
    }
  );
} catch (error) {
  // If worker 2 fails, workers 0, 1, and 3 are aborted
  console.error('Distributed render failed:', error);
}
```

## Temporary file cleanup

The orchestrator automatically cleans up temporary files:

* Chunk video files (e.g., `temp_123_part_0.mov`)
* Concatenated intermediate file (e.g., `temp_concat_123.mov`)

Cleanup occurs in the `finally` block, ensuring files are removed even if rendering fails.

## Custom executors

Implement the `RenderExecutor` interface to run chunks on cloud infrastructure:

```typescript theme={null}
import { RenderExecutor } from '@helios/renderer';

class CloudExecutor implements RenderExecutor {
  async render(
    compositionUrl: string,
    outputPath: string,
    options: RendererOptions,
    jobOptions?: RenderJobOptions
  ): Promise<void> {
    // Upload composition to cloud storage
    // Trigger cloud render job
    // Download rendered chunk
  }
}

await RenderOrchestrator.render(
  'file:///composition.html',
  './output.mp4',
  {
    width: 1920,
    height: 1080,
    fps: 30,
    durationInSeconds: 60,
    concurrency: 100, // Scale to cloud capacity
    executor: new CloudExecutor()
  }
);
```

## Performance considerations

### Optimal concurrency

* **CPU-bound**: Set concurrency to CPU core count minus 1
* **Memory-bound**: Reduce concurrency if renders are memory-intensive
* **Cloud**: Scale concurrency based on available cloud workers

### Chunk size

Chunk size is automatically calculated as `totalFrames / concurrency`. For very short videos, some workers may receive no frames:

```typescript theme={null}
// 90 frames, 4 workers = 22-23 frames per chunk
// 90 frames, 100 workers = 0-1 frames per chunk (inefficient)
```

### When to use distributed rendering

**Use distributed rendering when:**

* Videos are longer than 30 seconds
* You have multiple CPU cores available
* Rendering time is critical

**Use standard Renderer when:**

* Videos are very short (\< 10 seconds)
* Single-core environment
* Simplicity is preferred over speed

## Console output

Distributed rendering produces detailed logs:

```
Starting distributed render with concurrency: 4
[Worker 0] Rendering frames 0 to 450 (450 frames) to /tmp/temp_123_part_0.mov
[Worker 1] Rendering frames 450 to 900 (450 frames) to /tmp/temp_123_part_1.mov
[Worker 2] Rendering frames 900 to 1350 (450 frames) to /tmp/temp_123_part_2.mov
[Worker 3] Rendering frames 1350 to 1800 (450 frames) to /tmp/temp_123_part_3.mov
All chunks rendered. Concatenating...
Mixing audio into concatenated video...
Spawning FFmpeg for audio mixing: /usr/local/bin/ffmpeg -i /tmp/concat.mov ...
Cleaning up temporary files...
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.