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

# Driver interfaces

> Time driver and ticker interfaces for Helios synchronization

## TimeDriver

The `TimeDriver` interface defines the contract for time synchronization drivers. Drivers manage media elements, animations, and ensure frame-accurate playback.

```typescript theme={null}
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

<ResponseField name="init" type="(scope: unknown) => void">
  Initialize the driver with an animation scope. For browser drivers, this is typically the `document` object.
</ResponseField>

<ResponseField name="update" type="(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
</ResponseField>

<ResponseField name="waitUntilStable" type="() => Promise<void>">
  Returns a promise that resolves when all asynchronous operations (media seeking, image loading, etc.) are complete. Critical for deterministic rendering.
</ResponseField>

### Optional methods

<ResponseField name="dispose" type="() => void">
  Clean up resources when the driver is no longer needed.
</ResponseField>

<ResponseField name="subscribeToMetadata" type="(callback: (meta: DriverMetadata) => void) => () => void">
  Subscribe to metadata updates (e.g., discovered audio tracks). Returns an unsubscribe function.
</ResponseField>

<ResponseField name="getAudioContext" type="() => Promise<unknown>">
  Get the Web Audio API AudioContext for custom audio processing.
</ResponseField>

<ResponseField name="getAudioSourceNode" type="(trackId: string) => Promise<unknown>">
  Get a MediaElementAudioSourceNode for a specific audio track, useful for visualization.
</ResponseField>

## DriverMetadata

Metadata reported by drivers about available resources.

```typescript theme={null}
interface DriverMetadata {
  audioTracks?: AudioTrackMetadata[];
}
```

<ResponseField name="audioTracks" type="AudioTrackMetadata[]">
  Audio tracks discovered by the driver from the DOM.
</ResponseField>

## AudioTrackMetadata

Detailed metadata for an audio track.

```typescript theme={null}
interface AudioTrackMetadata {
  id: string;
  src: string;
  startTime: number;
  duration: number;
  fadeInDuration?: number;
  fadeOutDuration?: number;
  fadeEasing?: string;
}
```

<ResponseField name="id" type="string">
  Unique track identifier.
</ResponseField>

<ResponseField name="src" type="string">
  Audio source URL or path.
</ResponseField>

<ResponseField name="startTime" type="number">
  Track start time in the composition timeline.
</ResponseField>

<ResponseField name="duration" type="number">
  Track duration in seconds.
</ResponseField>

<ResponseField name="fadeInDuration" type="number">
  Optional fade-in duration in seconds.
</ResponseField>

<ResponseField name="fadeOutDuration" type="number">
  Optional fade-out duration in seconds.
</ResponseField>

<ResponseField name="fadeEasing" type="string">
  Optional easing function name for fade transitions.
</ResponseField>

## Ticker

The `Ticker` interface defines the contract for playback loop implementations.

```typescript theme={null}
interface Ticker {
  start(callback: TickCallback): void;
  stop(): void;
}
```

<ResponseField name="start" type="(callback: TickCallback) => void">
  Start the ticker loop, calling the callback on each tick with the delta time since the last tick.
</ResponseField>

<ResponseField name="stop" type="() => void">
  Stop the ticker loop.
</ResponseField>

## TickCallback

Callback type for ticker implementations.

```typescript theme={null}
type TickCallback = (deltaTime: number) => void;
```

<ResponseField name="deltaTime" type="number">
  Time elapsed since the last tick in milliseconds.
</ResponseField>

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

```typescript theme={null}
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

```typescript theme={null}
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

```typescript theme={null}
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

```typescript theme={null}
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();
```

## Related types

* [HeliosConfig](/api/types/helios-config) - Configuration interface
* [HeliosState](/api/types/helios-state) - Runtime state interface
* [RenderOptions](/api/types/render-options) - Rendering configuration
