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

# Player exporter

> API reference for ClientSideExporter - browser-based video export

The `ClientSideExporter` class provides browser-based export functionality for Helios compositions. It captures frames and audio, encodes them using WebCodecs, and outputs video files or images.

## Installation

The exporter is included in the `@helios-project/player` package:

```bash theme={null}
npm install @helios-project/player
```

## Basic usage

```typescript theme={null}
import { ClientSideExporter } from '@helios-project/player';

const controller = player.controller; // Or create directly
const exporter = new ClientSideExporter(controller);

await exporter.export({
  format: 'mp4',
  onProgress: (progress) => {
    console.log(`Export: ${Math.round(progress * 100)}%`);
  }
});
```

## Constructor

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

**Parameters:**

<ParamField path="controller" type="HeliosController" required>
  Controller instance connected to the composition.
</ParamField>

## Methods

### export(options)

Exports the composition to a video file or image.

```typescript theme={null}
await exporter.export({
  format: 'mp4',
  width: 1920,
  height: 1080,
  bitrate: 8_000_000,
  filename: 'my-video',
  onProgress: (progress) => {
    progressBar.value = progress;
  },
  signal: abortController.signal
});
```

**Parameters:**

<ParamField path="format" type="'mp4' | 'webm' | 'png' | 'jpeg'" default="mp4">
  Output format. Use `mp4` or `webm` for video, `png` or `jpeg` for single-frame images.
</ParamField>

<ParamField path="width" type="number">
  Output video width in pixels. If omitted, uses composition width. Scales the canvas/DOM during capture.
</ParamField>

<ParamField path="height" type="number">
  Output video height in pixels. If omitted, uses composition height.
</ParamField>

<ParamField path="bitrate" type="number" default="5000000">
  Video bitrate in bits per second. Higher values = better quality, larger file size.
</ParamField>

<ParamField path="filename" type="string" default="video">
  Filename without extension. Extension is added automatically based on format.
</ParamField>

<ParamField path="mode" type="'auto' | 'canvas' | 'dom'" default="auto">
  Capture mode. `auto` attempts canvas first, falls back to DOM. `canvas` captures the canvas element directly. `dom` renders the entire document.
</ParamField>

<ParamField path="canvasSelector" type="string" default="canvas">
  CSS selector for the canvas element when using canvas mode.
</ParamField>

<ParamField path="includeCaptions" type="boolean" default="true">
  Whether to burn captions into the video frames.
</ParamField>

<ParamField path="captionStyle" type="object">
  Caption appearance customization:

  * `color` (string) - Text color (default: `'white'`)
  * `backgroundColor` (string) - Background color (default: `'rgba(0, 0, 0, 0.7)'`)
  * `fontFamily` (string) - Font family (default: `'sans-serif'`)
  * `scale` (number) - Font size as fraction of video height (default: `0.05`)
</ParamField>

<ParamField path="onProgress" type="(progress: number) => void" required>
  Progress callback. Called with values from 0.0 to 1.0 as export progresses.
</ParamField>

<ParamField path="signal" type="AbortSignal">
  Abort signal to cancel the export. Connect to an AbortController.
</ParamField>

**Returns:** `Promise<void>` - Resolves when export completes and download begins.

**Throws:** Error if export fails or is aborted.

### saveCaptionsAsSRT(cues, filename)

Saves captions as an SRT file.

```typescript theme={null}
const cues = [
  { index: 1, startTime: 0, endTime: 5, text: 'Hello world' },
  { index: 2, startTime: 5, endTime: 10, text: 'Second caption' }
];

exporter.saveCaptionsAsSRT(cues, 'captions.srt');
```

**Parameters:**

<ParamField path="cues" type="SubtitleCue[]" required>
  Array of subtitle cues with `index`, `startTime`, `endTime`, and `text` properties.
</ParamField>

<ParamField path="filename" type="string" required>
  Filename for the SRT file (include `.srt` extension).
</ParamField>

## Export formats

### MP4 video

```typescript theme={null}
await exporter.export({
  format: 'mp4',
  bitrate: 8_000_000,
  onProgress: (p) => console.log(`${Math.round(p * 100)}%`)
});
```

* Codec: H.264 (AVC)
* Audio codec: AAC
* Best browser support
* Recommended for web delivery

### WebM video

```typescript theme={null}
await exporter.export({
  format: 'webm',
  bitrate: 8_000_000,
  onProgress: (p) => console.log(`${Math.round(p * 100)}%`)
});
```

* Codec: VP9
* Audio codec: Opus
* Better compression than MP4
* May have limited browser support on some platforms

### PNG image

```typescript theme={null}
await exporter.export({
  format: 'png',
  onProgress: (p) => console.log(`${Math.round(p * 100)}%`)
});
```

* Captures the current frame as a lossless PNG
* Useful for thumbnails or poster images

### JPEG image

```typescript theme={null}
await exporter.export({
  format: 'jpeg',
  onProgress: (p) => console.log(`${Math.round(p * 100)}%`)
});
```

* Captures the current frame as a JPEG
* Smaller file size than PNG
* Lossy compression

## Capture modes

### Auto mode (recommended)

Attempts canvas capture first, falls back to DOM if canvas is not accessible:

```typescript theme={null}
await exporter.export({
  mode: 'auto',
  onProgress: (p) => console.log(p)
});
```

### Canvas mode

Captures directly from a canvas element (fast, efficient):

```typescript theme={null}
await exporter.export({
  mode: 'canvas',
  canvasSelector: '#my-canvas',
  onProgress: (p) => console.log(p)
});
```

**Requirements:**

* Composition must render to a canvas
* Canvas must be accessible (same-origin)

### DOM mode

Captures the entire document using DOM-to-bitmap rendering (slower, more flexible):

```typescript theme={null}
await exporter.export({
  mode: 'dom',
  width: 1920,
  height: 1080,
  onProgress: (p) => console.log(p)
});
```

**Use cases:**

* Compositions without canvas
* HTML/CSS-based animations
* SVG content

**Performance:** Slower than canvas mode. Export time increases with DOM complexity.

## Caption rendering

### Burn captions into video

```typescript theme={null}
await exporter.export({
  format: 'mp4',
  includeCaptions: true,
  captionStyle: {
    color: 'yellow',
    backgroundColor: 'rgba(0, 0, 0, 0.8)',
    fontFamily: 'Arial',
    scale: 0.06 // 6% of video height
  },
  onProgress: (p) => console.log(p)
});
```

### Export captions as separate SRT file

```typescript theme={null}
// Export video
await exporter.export({ format: 'mp4', includeCaptions: false });

// Export captions separately
const state = controller.getState();
const cues = state.activeCaptions.map((c, i) => ({
  index: i + 1,
  startTime: c.startTime,
  endTime: c.endTime,
  text: c.text
}));

exporter.saveCaptionsAsSRT(cues, 'captions.srt');
```

## Progress tracking

```typescript theme={null}
const progressBar = document.querySelector('progress');
const statusText = document.querySelector('.status');

await exporter.export({
  format: 'mp4',
  onProgress: (progress) => {
    progressBar.value = progress;
    statusText.textContent = `Exporting: ${Math.round(progress * 100)}%`;
  }
});

statusText.textContent = 'Export complete!';
```

## Abort handling

```typescript theme={null}
const abortController = new AbortController();
const cancelButton = document.querySelector('.cancel-btn');

cancelButton.addEventListener('click', () => {
  abortController.abort();
});

try {
  await exporter.export({
    format: 'mp4',
    signal: abortController.signal,
    onProgress: (p) => console.log(`Progress: ${p}`)
  });
  console.log('Export completed');
} catch (err) {
  if (err.message === 'Export aborted') {
    console.log('Export was cancelled');
  } else {
    console.error('Export failed:', err);
  }
}
```

## Playback range export

Export only a portion of the composition:

```typescript theme={null}
// Set playback range
controller.setPlaybackRange(60, 240); // Frames 60-240

// Export the range
await exporter.export({
  format: 'mp4',
  onProgress: (p) => console.log(p)
});

// Clear range
controller.clearPlaybackRange();
```

## Full example: Export UI

```typescript theme={null}
import { ClientSideExporter } from '@helios-project/player';

const player = document.querySelector('helios-player');
const exporter = new ClientSideExporter(player.controller);

const exportButton = document.querySelector('.export-btn');
const progressBar = document.querySelector('progress');
const statusText = document.querySelector('.status');
const formatSelect = document.querySelector('.format-select');
const abortController = new AbortController();

exportButton.addEventListener('click', async () => {
  // Disable button during export
  exportButton.disabled = true;
  statusText.textContent = 'Exporting...';
  progressBar.value = 0;
  
  try {
    await exporter.export({
      format: formatSelect.value,
      width: 1920,
      height: 1080,
      bitrate: 8_000_000,
      filename: 'my-composition',
      includeCaptions: true,
      signal: abortController.signal,
      onProgress: (progress) => {
        progressBar.value = progress;
        statusText.textContent = `Exporting: ${Math.round(progress * 100)}%`;
      }
    });
    
    statusText.textContent = 'Export complete! Download started.';
  } catch (err) {
    if (err.message === 'Export aborted') {
      statusText.textContent = 'Export cancelled';
    } else {
      statusText.textContent = 'Export failed: ' + err.message;
      console.error(err);
    }
  } finally {
    exportButton.disabled = false;
  }
});

// Cancel button
document.querySelector('.cancel-btn').addEventListener('click', () => {
  abortController.abort();
});
```

## Performance tips

1. **Use canvas mode when possible** - Much faster than DOM mode
2. **Lower resolution for previews** - Export at lower resolution for quick previews
3. **Adjust bitrate** - Higher bitrate = better quality but larger files and longer encoding
4. **Minimize DOM complexity** - Simpler DOMs render faster in DOM mode
5. **Close VideoFrames** - Always close captured frames to avoid memory leaks

## Browser compatibility

Requires:

* WebCodecs API (Chrome 94+, Edge 94+)
* OffscreenCanvas (Chrome 69+, Firefox 105+)
* MediaRecorder API for encoding

Not supported in:

* Safari (WebCodecs not available)
* Older browsers

For unsupported browsers, use server-side rendering instead.

## Error handling

Common errors and solutions:

```typescript theme={null}
try {
  await exporter.export({ ... });
} catch (err) {
  if (err.message.includes('Canvas not found')) {
    // Use DOM mode instead
    await exporter.export({ mode: 'dom', ... });
  } else if (err.message.includes('WebCodecs')) {
    // Browser doesn't support WebCodecs
    alert('Your browser does not support video export');
  } else {
    console.error('Export failed:', err);
  }
}
```
