Skip to content

Function API

Individual, tree-shakeable functions grouped by module. Each function takes an AudioContext as its first argument (BYO Context pattern).

import { createContext } from "waa-play/context";
import { loadBuffer } from "waa-play/buffer";
import { play } from "waa-play/play";
const ctx = createContext();
const buffer = await loadBuffer(ctx, "/audio/track.mp3");
const playback = play(ctx, buffer);

play

Core playback engine. Wraps AudioBufferSourceNode with a state machine, position tracking, and event system.

import { play } from "waa-play/play";

play()

play(ctx: AudioContext, buffer: AudioBuffer, options?: PlayOptions): Playback;

Play an AudioBuffer. Returns a controllable Playback handle.

const playback = play(ctx, buffer, {
offset: 10,
loop: true,
playbackRate: 1.5,
through: [gain, filter],
});

PlayOptions

OptionTypeDefaultDescription
offsetnumber0Start position in seconds
loopbooleanfalseEnable looping
loopStartnumber0Loop start point in seconds
loopEndnumberdurationLoop end point in seconds
playbackRatenumber1Playback speed multiplier
throughAudioNode[][]Audio nodes to route through (effects chain)
destinationAudioNodectx.destinationOutput destination node
timeupdateIntervalnumber250Interval for timeupdate events in ms
preservePitchbooleanfalsePreserve pitch when changing playback rate (uses stretcher engine)

Playback Methods

// State
getState(): PlaybackState; // "playing" | "paused" | "stopped"
getCurrentTime(): number;
getDuration(): number;
getProgress(): number; // [0, 1]
// Control
pause(): void;
resume(): void;
togglePlayPause(): void;
seek(position: number): void;
stop(): void;
// Configuration
setPlaybackRate(rate: number): void;
setLoop(loop: boolean): void;
// Events
on(event: string, handler: Function): () => void; // Returns unsubscribe
off(event: string, handler: Function): void;
// Cleanup
dispose(): void;

Playback Events

EventPayloadDescription
play-Playback started
pause-Playback paused
resume-Resumed from pause
seeknumberSeeked to position (seconds)
stop-Playback stopped
ended-Reached end naturally
loop-Looped back to start
statechangePlaybackStateState changed
timeupdatenumberPosition updated (fires at timeupdateInterval)
buffering-Stretcher engine is buffering
buffered-Stretcher engine buffering complete

PlaybackSnapshot

interface PlaybackSnapshot {
state: PlaybackState;
position: number;
duration: number;
progress: number;
stretcher?: StretcherSnapshotExtension;
}

context

AudioContext lifecycle utilities.

import { createContext, resumeContext, ensureRunning, now } from "waa-play/context";

createContext()

createContext(options?: { sampleRate?: number; latencyHint?: AudioContextLatencyCategory | number }): AudioContext;

resumeContext()

resumeContext(ctx: AudioContext): Promise<void>;

Resume a suspended AudioContext. Call from a user gesture handler.

ensureRunning()

ensureRunning(ctx: AudioContext): Promise<void>;

Ensure the AudioContext is in the "running" state. Safe to call multiple times.

now()

now(ctx: AudioContext): number;

Shorthand for ctx.currentTime.


buffer

Audio file loading and decoding.

import { loadBuffer, loadBufferFromBlob, loadBuffers, getBufferInfo } from "waa-play/buffer";

loadBuffer()

loadBuffer(ctx: AudioContext, url: string, options?: { onProgress?: (progress: number) => void }): Promise<AudioBuffer>;

Fetch and decode an audio file. Supports progress tracking via onProgress (0–1).

loadBufferFromBlob()

loadBufferFromBlob(ctx: AudioContext, blob: Blob): Promise<AudioBuffer>;

Decode an AudioBuffer from a Blob or File.

loadBuffers()

loadBuffers(ctx: AudioContext, map: Record<string, string>): Promise<Map<string, AudioBuffer>>;

Load multiple audio files in parallel from a key-URL map.

getBufferInfo()

getBufferInfo(buffer: AudioBuffer): { duration: number; numberOfChannels: number; sampleRate: number; length: number };

nodes

Audio node factories and routing utilities.

import { createGain, rampGain, createAnalyser, createFilter, createPanner, createCompressor, chain, disconnectChain } from "waa-play/nodes";

Node Factories

createGain(ctx: AudioContext, initialValue?: number): GainNode;
createAnalyser(ctx: AudioContext, options?: { fftSize?: number; smoothingTimeConstant?: number }): AnalyserNode;
createFilter(ctx: AudioContext, options?: { type?: BiquadFilterType; frequency?: number; Q?: number; gain?: number }): BiquadFilterNode;
createPanner(ctx: AudioContext, pan?: number): StereoPannerNode;
createCompressor(ctx: AudioContext, options?: { threshold?: number; knee?: number; ratio?: number; attack?: number; release?: number }): DynamicsCompressorNode;

Utilities

rampGain(gain: GainNode, target: number, duration: number): void;
getFrequencyData(analyser: AnalyserNode): Float32Array;
getFrequencyDataByte(analyser: AnalyserNode): Uint8Array;

Routing

chain(...nodes: AudioNode[]): void; // Connect nodes in series
disconnectChain(...nodes: AudioNode[]): void; // Disconnect chained nodes

emitter

Minimal, type-safe event emitter.

import { createEmitter } from "waa-play/emitter";

createEmitter()

createEmitter<Events extends Record<string, unknown>>(): Emitter<Events>;
type MyEvents = { progress: number; complete: void };
const emitter = createEmitter<MyEvents>();
emitter.on("progress", (v) => console.log(v)); // Returns unsubscribe fn
emitter.emit("progress", 0.5);
emitter.clear(); // Remove all handlers

Emitter Methods

on<K>(event: K, handler: (data: Events[K]) => void): () => void;
off<K>(event: K, handler: (data: Events[K]) => void): void;
emit<K>(event: K, data: Events[K]): void;
clear(event?: keyof Events): void;

waveform

Extract visual waveform data from AudioBuffer.

import { extractPeaks, extractPeakPairs, extractRMS } from "waa-play/waveform";

Functions

extractPeaks(buffer: AudioBuffer, options?: ExtractPeaksOptions): number[];
extractPeakPairs(buffer: AudioBuffer, options?: ExtractPeaksOptions): PeakPair[];
extractRMS(buffer: AudioBuffer, options?: ExtractPeaksOptions): number[];

Options

OptionTypeDefaultDescription
resolutionnumber200Number of data points to extract
channelnumber0Channel index (-1 for all channels)

PeakPair is { min: number; max: number }.


fade

Fade in/out and crossfade utilities using GainNode automation.

import { fadeIn, fadeOut, crossfade, autoFade } from "waa-play/fade";

Functions

fadeIn(gain: GainNode, target: number, options?: FadeOptions): void;
fadeOut(gain: GainNode, options?: FadeOptions): void;
crossfade(gainA: GainNode, gainB: GainNode, options?: CrossfadeOptions): void;
autoFade(playback: Playback, gain: GainNode, options?: AutoFadeOptions): () => void;

autoFade applies fade-in on start and fade-out before end. Returns a cleanup function.

Options

OptionTypeDefaultDescription
durationnumber1Fade duration in seconds
curveFadeCurve"linear""linear" | "exponential" | "equal-power"

AutoFadeOptions uses fadeIn / fadeOut (seconds) instead of duration.


scheduler

Lookahead-based event scheduler and BPM clock.

import { createScheduler, createClock } from "waa-play/scheduler";

createScheduler()

createScheduler(ctx: AudioContext, options?: { lookahead?: number; interval?: number }): Scheduler;
OptionTypeDefaultDescription
lookaheadnumber0.1How far ahead to schedule (seconds)
intervalnumber25Timer interval (ms)

Scheduler methods: schedule(id, time, callback), cancel(id), start(), stop(), dispose().

createClock()

createClock(ctx: AudioContext, options?: { bpm?: number }): Clock;

BPM-based clock. Default 120 BPM.

Clock methods: beatToTime(beat), getCurrentBeat(), getNextBeatTime(), setBpm(bpm), getBpm().


synth

Generate synthetic audio buffers.

import { createSineBuffer, createNoiseBuffer, createClickBuffer } from "waa-play/synth";
createSineBuffer(ctx: AudioContext, frequency: number, duration: number): AudioBuffer;
createNoiseBuffer(ctx: AudioContext, duration: number): AudioBuffer;
createClickBuffer(ctx: AudioContext, frequency: number, duration: number): AudioBuffer;

adapters

Framework integration utilities. Compatible with React’s useSyncExternalStore.

import { getSnapshot, subscribeSnapshot, onFrame, whenEnded, whenPosition } from "waa-play/adapters";

Functions

getSnapshot(playback: Playback): PlaybackSnapshot;
subscribeSnapshot(playback: Playback, callback: (snap: PlaybackSnapshot) => void): () => void;
onFrame(playback: Playback, callback: (snap: PlaybackSnapshot) => void): () => void;
whenEnded(playback: Playback): Promise<void>;
whenPosition(playback: Playback, position: number): Promise<void>;

React Example

import { useSyncExternalStore, useCallback } from "react";
import { getSnapshot, subscribeSnapshot } from "waa-play/adapters";
function usePlayback(playback: Playback) {
const subscribe = useCallback(
(cb: () => void) => subscribeSnapshot(playback, cb),
[playback],
);
const snap = useCallback(
() => getSnapshot(playback),
[playback],
);
return useSyncExternalStore(subscribe, snap, snap);
}