Skip to Content
raavl

Player API

Control playback and subscribe separately to semantic state and time.

createPlayer() returns an independent controller with no constructor options. Import it from raavl. Playback requires a finite audio source.

Commands

MethodBehavior
setSource(source, { signal? }?)Load a source; resolves after metadata becomes available
clearSource()Cancel work, clear audio, return to idle
play()Start or resume; returns a promise that can reject
pause()Pause playback
seek(seconds)Seek to nonnegative seconds, capped at duration
seekFraction(fraction)Seek using a value in [0, 1]
setVolume(volume)Set volume in [0, 1]
setMuted(muted)Toggle mute
setPlaybackRate(rate)Set speed from 0.25 to 4
setLoop(loop)Loop the whole recording
playRange({ start, end, loop? })Play a bounded time range; returns a promise
dispose()Cancel work and release owned resources; idempotent

Range endpoints use seconds, must lie within duration, and must satisfy start < end. Range playback disables full-recording looping and uses best-effort HTML media timing. Pause, seek, source replacement, or disposal clears range scheduling.

Read and observe

MethodResult
getSnapshot()Cached immutable PlayerSnapshot
subscribe(listener)Unsubscribe function for semantic state changes
getCurrentTime()Cached playback time in seconds
subscribeTime(listener)Unsubscribe function for the independent clock
getMediaElement()Active media element or null

The clock updates at roughly 10 Hz during playback plus media and seek events. Background scheduling may reduce that rate. Read the media element when you need the browser's immediate playback time. The Canvas playhead animates outside React.

const off = player.subscribe(() => {
  const state = player.getSnapshot();
  playButton.disabled = state.source !== 'ready';
  if (state.error) showError(state.error);
  if (state.waveformError) showWaveformError(state.waveformError);
});
const offTime = player.subscribeTime(() => {
  clock.textContent = player.getCurrentTime().toFixed(1);
});
// On teardown: off(); offTime(); player.dispose();

Ownership

The player owns audio it creates for URL/file sources and revokes its own Blob URLs. Borrowed media remains application-owned. A view always borrows its player; disposing a view never disposes that controller.

After disposal, reads expose final state and mutating commands throw or reject with DISPOSED. See state and errors for the full snapshot.

On this page