> ## 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 controllers

> API reference for DirectController and BridgeController classes

Controllers provide the interface between the `<helios-player>` element and the Helios composition. They handle communication, state synchronization, and frame capture.

## Controller types

There are two controller implementations:

* **DirectController** - Used when the composition is same-origin and accessible via direct JavaScript reference
* **BridgeController** - Used when the composition is cross-origin and requires message-based communication

The player automatically selects the appropriate controller based on iframe access permissions.

## HeliosController interface

Both controllers implement the `HeliosController` interface:

### Playback control

#### play()

Starts or resumes playback.

```typescript theme={null}
controller.play();
```

#### pause()

Pauses playback.

```typescript theme={null}
controller.pause();
```

#### seek(frame)

Seeks to a specific frame.

```typescript theme={null}
await controller.seek(120); // Seek to frame 120
```

**Parameters:**

* `frame` (number) - Target frame number

**Returns:** `Promise<void>` - Resolves when seek completes

### Audio control

#### setAudioVolume(volume)

Sets the master audio volume.

```typescript theme={null}
controller.setAudioVolume(0.8); // 80% volume
```

**Parameters:**

* `volume` (number) - Volume level from 0.0 to 1.0

#### setAudioMuted(muted)

Mutes or unmutes all audio.

```typescript theme={null}
controller.setAudioMuted(true);
```

**Parameters:**

* `muted` (boolean) - Whether audio should be muted

#### setAudioTrackVolume(trackId, volume)

Sets the volume for a specific audio track.

```typescript theme={null}
controller.setAudioTrackVolume('narration', 0.5);
```

**Parameters:**

* `trackId` (string) - Audio track identifier
* `volume` (number) - Volume level from 0.0 to 1.0

#### setAudioTrackMuted(trackId, muted)

Mutes or unmutes a specific audio track.

```typescript theme={null}
controller.setAudioTrackMuted('music', true);
```

**Parameters:**

* `trackId` (string) - Audio track identifier
* `muted` (boolean) - Whether track should be muted

### Playback configuration

#### setLoop(loop)

Enables or disables looping playback.

```typescript theme={null}
controller.setLoop(true);
```

**Parameters:**

* `loop` (boolean) - Whether playback should loop

#### setPlaybackRate(rate)

Sets the playback speed.

```typescript theme={null}
controller.setPlaybackRate(2.0); // 2x speed
```

**Parameters:**

* `rate` (number) - Speed multiplier (1.0 = normal)

#### setPlaybackRange(startFrame, endFrame)

Sets a custom playback range.

```typescript theme={null}
controller.setPlaybackRange(60, 240); // Play frames 60-240
```

**Parameters:**

* `startFrame` (number) - First frame of range
* `endFrame` (number) - Last frame of range (exclusive)

#### clearPlaybackRange()

Clears the playback range, restoring full duration playback.

```typescript theme={null}
controller.clearPlaybackRange();
```

### Captions

#### setCaptions(captions)

Sets captions from VTT string or cue array.

```typescript theme={null}
// From VTT string
controller.setCaptions('WEBVTT\n\n00:00.000 --> 00:05.000\nHello');

// From cue array
controller.setCaptions([
  { startTime: 0, endTime: 5, text: 'Hello' }
]);
```

**Parameters:**

* `captions` (string | CaptionCue\[]) - VTT string or caption cue array

### Composition properties

#### setInputProps(props)

Sets input properties for the composition schema.

```typescript theme={null}
controller.setInputProps({
  title: 'My Video',
  theme: 'dark'
});
```

**Parameters:**

* `props` (Record) - Property key-value pairs

#### setDuration(seconds)

Overrides the composition duration.

```typescript theme={null}
controller.setDuration(30);
```

**Parameters:**

* `seconds` (number) - Duration in seconds

#### setFps(fps)

Overrides the composition frame rate.

```typescript theme={null}
controller.setFps(60);
```

**Parameters:**

* `fps` (number) - Frames per second

#### setSize(width, height)

Sets the composition dimensions.

```typescript theme={null}
controller.setSize(1920, 1080);
```

**Parameters:**

* `width` (number) - Width in pixels
* `height` (number) - Height in pixels

#### setMarkers(markers)

Sets timeline markers.

```typescript theme={null}
controller.setMarkers([
  { time: 5, label: 'Intro ends' },
  { time: 25, label: 'Call to action' }
]);
```

**Parameters:**

* `markers` (Marker\[]) - Array of marker objects

### State and events

#### subscribe(callback)

Subscribes to state changes.

```typescript theme={null}
const unsubscribe = controller.subscribe((state) => {
  console.log('Current frame:', state.currentFrame);
  console.log('Is playing:', state.isPlaying);
});

// Later: unsubscribe()
```

**Parameters:**

* `callback` ((state: any) => void) - Called on each state update

**Returns:** `() => void` - Unsubscribe function

**State object properties:**

* `currentFrame` (number) - Current frame
* `isPlaying` (boolean) - Playing state
* `duration` (number) - Duration in seconds
* `fps` (number) - Frame rate
* `volume` (number) - Master volume
* `muted` (boolean) - Muted state
* `playbackRate` (number) - Playback speed
* `width` (number) - Composition width
* `height` (number) - Composition height
* `availableAudioTracks` (array) - Available audio tracks
* `audioTracks` (array) - Active audio tracks
* `activeCaptions` (CaptionCue\[]) - Currently visible captions
* `playbackRange` (\[number, number] | null) - Active playback range

#### onError(callback)

Subscribes to error events.

```typescript theme={null}
const unsubscribe = controller.onError((error) => {
  console.error('Composition error:', error.message);
  console.error('Stack:', error.stack);
});
```

**Parameters:**

* `callback` ((error: any) => void) - Called when errors occur

**Returns:** `() => void` - Unsubscribe function

#### getState()

Returns the current state synchronously.

```typescript theme={null}
const state = controller.getState();
console.log('Current frame:', state.currentFrame);
```

**Returns:** State object (see `subscribe()` for properties)

### Frame capture

#### captureFrame(frame, options)

Captures a single frame as a VideoFrame.

```typescript theme={null}
const result = await controller.captureFrame(120, {
  mode: 'canvas',
  selector: 'canvas',
  width: 1920,
  height: 1080
});

if (result) {
  const { frame, captions } = result;
  // Use VideoFrame and captions
  frame.close(); // Clean up when done
}
```

**Parameters:**

* `frame` (number) - Frame number to capture
* `options` (object, optional)
  * `selector` (string) - CSS selector for canvas (default: `'canvas'`)
  * `mode` ('canvas' | 'dom') - Capture mode
  * `width` (number) - Output width (scales if different)
  * `height` (number) - Output height (scales if different)

**Returns:** `Promise<{ frame: VideoFrame, captions: CaptionCue[] } | null>`

### Audio tracks

#### getAudioTracks()

Retrieves all audio assets from the composition.

```typescript theme={null}
const tracks = await controller.getAudioTracks();
tracks.forEach(track => {
  console.log(track.id, track.buffer.duration);
});
```

**Returns:** `Promise<AudioAsset[]>`

**AudioAsset properties:**

* `id` (string) - Track identifier
* `buffer` (AudioBuffer) - Decoded audio data
* `element` (HTMLAudioElement) - Source audio element

### Schema and diagnostics

#### getSchema()

Retrieves the composition schema definition.

```typescript theme={null}
const schema = await controller.getSchema();
if (schema) {
  console.log('Inputs:', schema.inputs);
}
```

**Returns:** `Promise<HeliosSchema | undefined>`

#### diagnose()

Generates a diagnostic report for debugging.

```typescript theme={null}
const report = await controller.diagnose();
console.log(report);
```

**Returns:** `Promise<DiagnosticReport>`

### Audio metering

#### startAudioMetering()

Begins real-time audio level monitoring.

```typescript theme={null}
controller.startAudioMetering();
```

#### stopAudioMetering()

Stops audio level monitoring.

```typescript theme={null}
controller.stopAudioMetering();
```

#### onAudioMetering(callback)

Subscribes to audio level updates.

```typescript theme={null}
const unsubscribe = controller.onAudioMetering((levels) => {
  console.log('Peak:', levels.peak);
  console.log('RMS:', levels.rms);
});

controller.startAudioMetering();

// Later:
controller.stopAudioMetering();
unsubscribe();
```

**Parameters:**

* `callback` ((levels: AudioLevels) => void) - Called with audio levels

**Returns:** `() => void` - Unsubscribe function

**AudioLevels properties:**

* `peak` (number) - Peak level (0.0 to 1.0)
* `rms` (number) - RMS level (0.0 to 1.0)

### Cleanup

#### dispose()

Cleans up resources and removes listeners.

```typescript theme={null}
controller.dispose();
```

## DirectController

Used for same-origin compositions with direct access.

### Constructor

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

const helios = new Helios({ ... });
const iframe = document.querySelector('iframe');
const controller = new DirectController(helios, iframe);
```

**Parameters:**

* `instance` (Helios) - Helios instance from the composition
* `iframe` (HTMLIFrameElement, optional) - Iframe element containing the composition

### Features

* Direct method calls on Helios instance (no message passing overhead)
* Synchronous state access
* Built-in audio fading for smooth playback transitions
* Real-time audio metering support

## BridgeController

Used for cross-origin compositions using postMessage.

### Constructor

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

const iframeWindow = iframe.contentWindow;
const controller = new BridgeController(iframeWindow, initialState);
```

**Parameters:**

* `iframeWindow` (Window) - Window object of the composition iframe
* `initialState` (object, optional) - Initial state object

### Features

* Message-based communication for cross-origin safety
* Automatic message filtering by source
* Promise-based async operations with timeouts
* State caching to minimize message overhead

### Bridge setup

The composition must call `connectToParent()` to enable bridge communication:

```typescript theme={null}
// In your composition:
import { Helios } from '@helios-project/core';
import { connectToParent } from '@helios-project/player/bridge';

const helios = new Helios({ ... });
connectoParent(helios);
```

## Example: Using controllers directly

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

// Create composition
const helios = new Helios({
  canvas: document.querySelector('canvas'),
  duration: 10,
  fps: 30
});

// Create controller
const controller = new DirectController(helios);

// Subscribe to state
controller.subscribe((state) => {
  console.log(`Frame ${state.currentFrame}/${state.duration * state.fps}`);
});

// Control playback
controller.play();

setTimeout(() => {
  controller.pause();
}, 5000);

// Export a frame
const result = await controller.captureFrame(90);
if (result) {
  // Use result.frame (VideoFrame)
  result.frame.close();
}
```

## Example: Bridge controller with error handling

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

const iframe = document.querySelector('iframe');
const controller = new BridgeController(iframe.contentWindow);

// Handle errors
controller.onError((error) => {
  console.error('Composition error:', error);
  // Show error UI
});

// Subscribe to state
controller.subscribe((state) => {
  updateUI(state);
});

// Seek with timeout handling
try {
  await controller.seek(100);
  console.log('Seek completed');
} catch (err) {
  console.error('Seek failed or timed out:', err);
}
```
