Skip to main content
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.
controller.play();

pause()

Pauses playback.
controller.pause();

seek(frame)

Seeks to a specific frame.
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.
controller.setAudioVolume(0.8); // 80% volume
Parameters:
  • volume (number) - Volume level from 0.0 to 1.0

setAudioMuted(muted)

Mutes or unmutes all audio.
controller.setAudioMuted(true);
Parameters:
  • muted (boolean) - Whether audio should be muted

setAudioTrackVolume(trackId, volume)

Sets the volume for a specific audio track.
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.
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.
controller.setLoop(true);
Parameters:
  • loop (boolean) - Whether playback should loop

setPlaybackRate(rate)

Sets the playback speed.
controller.setPlaybackRate(2.0); // 2x speed
Parameters:
  • rate (number) - Speed multiplier (1.0 = normal)

setPlaybackRange(startFrame, endFrame)

Sets a custom playback range.
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.
controller.clearPlaybackRange();

Captions

setCaptions(captions)

Sets captions from VTT string or cue array.
// 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.
controller.setInputProps({
  title: 'My Video',
  theme: 'dark'
});
Parameters:
  • props (Record) - Property key-value pairs

setDuration(seconds)

Overrides the composition duration.
controller.setDuration(30);
Parameters:
  • seconds (number) - Duration in seconds

setFps(fps)

Overrides the composition frame rate.
controller.setFps(60);
Parameters:
  • fps (number) - Frames per second

setSize(width, height)

Sets the composition dimensions.
controller.setSize(1920, 1080);
Parameters:
  • width (number) - Width in pixels
  • height (number) - Height in pixels

setMarkers(markers)

Sets timeline markers.
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.
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.
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.
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.
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.
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.
const schema = await controller.getSchema();
if (schema) {
  console.log('Inputs:', schema.inputs);
}
Returns: Promise<HeliosSchema | undefined>

diagnose()

Generates a diagnostic report for debugging.
const report = await controller.diagnose();
console.log(report);
Returns: Promise<DiagnosticReport>

Audio metering

startAudioMetering()

Begins real-time audio level monitoring.
controller.startAudioMetering();

stopAudioMetering()

Stops audio level monitoring.
controller.stopAudioMetering();

onAudioMetering(callback)

Subscribes to audio level updates.
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.
controller.dispose();

DirectController

Used for same-origin compositions with direct access.

Constructor

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

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:
// 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

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

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);
}

Build docs developers (and LLMs) love