Skip to main content

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.

TimeDriver

The TimeDriver interface defines the contract for time synchronization drivers. Drivers manage media elements, animations, and ensure frame-accurate playback.
interface TimeDriver {
  init(scope: unknown): void;
  update(timeInMs: number, options?: {
    isPlaying: boolean;
    playbackRate: number;
    volume?: number;
    muted?: boolean;
    audioTracks?: Record<string, { volume: number; muted: boolean }>;
  }): void;
  waitUntilStable(): Promise<void>;
  dispose?(): void;
  subscribeToMetadata?(callback: (meta: DriverMetadata) => void): () => void;
  getAudioContext?(): Promise<unknown>;
  getAudioSourceNode?(trackId: string): Promise<unknown>;
}

Required methods

init
(scope: unknown) => void
Initialize the driver with an animation scope. For browser drivers, this is typically the document object.
update
(timeInMs: number, options?) => void
Update the driver to a specific time in milliseconds with playback options.Parameters:
  • timeInMs: Target time in milliseconds
  • options.isPlaying: Whether playback is active
  • options.playbackRate: Speed multiplier
  • options.volume: Master volume (0.0 to 1.0)
  • options.muted: Master mute state
  • options.audioTracks: Per-track audio state
waitUntilStable
() => Promise<void>
Returns a promise that resolves when all asynchronous operations (media seeking, image loading, etc.) are complete. Critical for deterministic rendering.

Optional methods

dispose
() => void
Clean up resources when the driver is no longer needed.
subscribeToMetadata
(callback: (meta: DriverMetadata) => void) => () => void
Subscribe to metadata updates (e.g., discovered audio tracks). Returns an unsubscribe function.
getAudioContext
() => Promise<unknown>
Get the Web Audio API AudioContext for custom audio processing.
getAudioSourceNode
(trackId: string) => Promise<unknown>
Get a MediaElementAudioSourceNode for a specific audio track, useful for visualization.

DriverMetadata

Metadata reported by drivers about available resources.
interface DriverMetadata {
  audioTracks?: AudioTrackMetadata[];
}
audioTracks
AudioTrackMetadata[]
Audio tracks discovered by the driver from the DOM.

AudioTrackMetadata

Detailed metadata for an audio track.
interface AudioTrackMetadata {
  id: string;
  src: string;
  startTime: number;
  duration: number;
  fadeInDuration?: number;
  fadeOutDuration?: number;
  fadeEasing?: string;
}
id
string
Unique track identifier.
src
string
Audio source URL or path.
startTime
number
Track start time in the composition timeline.
duration
number
Track duration in seconds.
fadeInDuration
number
Optional fade-in duration in seconds.
fadeOutDuration
number
Optional fade-out duration in seconds.
fadeEasing
string
Optional easing function name for fade transitions.

Ticker

The Ticker interface defines the contract for playback loop implementations.
interface Ticker {
  start(callback: TickCallback): void;
  stop(): void;
}
start
(callback: TickCallback) => void
Start the ticker loop, calling the callback on each tick with the delta time since the last tick.
stop
() => void
Stop the ticker loop.

TickCallback

Callback type for ticker implementations.
type TickCallback = (deltaTime: number) => void;
deltaTime
number
Time elapsed since the last tick in milliseconds.

Built-in implementations

Helios provides several built-in driver and ticker implementations:

Drivers

  • DomDriver: Synchronizes WAAPI animations and media elements with Helios playback
  • NoopDriver: Minimal driver with no synchronization (default when autoSyncAnimations is false)

Tickers

  • RafTicker: Uses requestAnimationFrame for smooth browser-based playback (default in browsers)
  • TimeoutTicker: Uses setTimeout for Node.js environments (default in Node.js)
  • ManualTicker: Manual tick control for testing or custom playback loops

Usage examples

Custom time driver

import { TimeDriver, Helios } from '@helios/core';

class CustomDriver implements TimeDriver {
  private mediaElements: HTMLMediaElement[] = [];

  init(scope: unknown): void {
    if (scope instanceof Document) {
      this.mediaElements = Array.from(
        scope.querySelectorAll('audio, video')
      );
    }
  }

  update(timeInMs: number, options?: {
    isPlaying: boolean;
    playbackRate: number;
    volume?: number;
    muted?: boolean;
  }): void {
    const timeSec = timeInMs / 1000;

    this.mediaElements.forEach(el => {
      el.currentTime = timeSec;
      el.playbackRate = options?.playbackRate ?? 1;
      
      if (options?.volume !== undefined) {
        el.volume = options.volume;
      }
      
      if (options?.muted !== undefined) {
        el.muted = options.muted;
      }

      if (options?.isPlaying) {
        el.play();
      } else {
        el.pause();
      }
    });
  }

  async waitUntilStable(): Promise<void> {
    // Wait for all media to finish seeking
    await Promise.all(
      this.mediaElements.map(el => new Promise<void>(resolve => {
        if (el.seeking) {
          el.addEventListener('seeked', () => resolve(), { once: true });
        } else {
          resolve();
        }
      }))
    );
  }

  dispose(): void {
    this.mediaElements = [];
  }
}

// Use the custom driver
const helios = new Helios({
  duration: 10,
  fps: 30,
  driver: new CustomDriver()
});

Custom ticker

import { Ticker, TickCallback, Helios } from '@helios/core';

class CustomTicker implements Ticker {
  private intervalId: number | null = null;
  private lastTime = 0;

  start(callback: TickCallback): void {
    this.lastTime = Date.now();
    
    this.intervalId = setInterval(() => {
      const now = Date.now();
      const delta = now - this.lastTime;
      this.lastTime = now;
      
      callback(delta);
    }, 1000 / 60) as unknown as number; // 60 FPS
  }

  stop(): void {
    if (this.intervalId !== null) {
      clearInterval(this.intervalId);
      this.intervalId = null;
    }
  }
}

// Use the custom ticker
const helios = new Helios({
  duration: 10,
  fps: 30,
  ticker: new CustomTicker()
});

Accessing audio context

import { Helios } from '@helios/core';

const helios = new Helios({
  duration: 10,
  fps: 30,
  autoSyncAnimations: true // Uses DomDriver
});

// Get the audio context for custom processing
const audioContext = await helios.getAudioContext();

if (audioContext instanceof AudioContext) {
  // Create an analyser for visualization
  const analyser = audioContext.createAnalyser();
  
  // Get source node for a specific track
  const sourceNode = await helios.getAudioSourceNode('background-music');
  
  if (sourceNode instanceof MediaElementAudioSourceNode) {
    sourceNode.connect(analyser);
    analyser.connect(audioContext.destination);
    
    // Now you can visualize audio
    const dataArray = new Uint8Array(analyser.frequencyBinCount);
    analyser.getByteFrequencyData(dataArray);
  }
}

Subscribing to metadata

import { Helios } from '@helios/core';

const helios = new Helios({
  duration: 10,
  fps: 30,
  autoSyncAnimations: true
});

// Subscribe to available audio tracks
const unsubscribe = helios.availableAudioTracks.subscribe(tracks => {
  console.log('Available audio tracks:', tracks);
  
  tracks.forEach(track => {
    console.log(`Track ${track.id}:`, track.src);
    console.log(`  Duration: ${track.duration}s`);
    console.log(`  Start: ${track.startTime}s`);
  });
});

// Clean up when done
unsubscribe();

Build docs developers (and LLMs) love