Skip to main content
Audio tracks work like video tracks. Declare them on your Output, emit samples alongside frames, and the runtime handles encoding, transport, and synchronization.

Declaring an audio track

Emit both together from the run loop:
Every track declared on the class must be supplied on every emit(). When a tick produces no audio, send an empty array rather than omitting the field:

Audio format

The runtime encodes to Opus for transport. 48 kHz is the Opus native rate, so audio already at 48 kHz needs no resampling.

Custom sample rates

If your model generates at a different rate, subclass Audio and declare it:
The runtime resamples to 48 kHz for transport. Declaring the true rate matters — it is what makes the resampling correct and keeps playback at the right pitch and duration.

Keeping audio and video in sync

When you emit an output carrying both tracks, the runtime keeps them aligned. If the video payload is a batch, the audio is split proportionally across those frames. The rule that follows: each emit should carry the audio that belongs to its video. At 30 fps, one video frame is 48000 / 30 = 1600 samples, so a batch of 3 frames should carry about 4,800 samples (100 ms). Emitting a frame’s worth of video with a second’s worth of audio will drift, however well the transport behaves.

Rate-matching two streams

Models whose audio and video come from separate producers need to pair them before emitting. Buffer whichever stream runs ahead, and emit only when both have material for the same span:
Pairing by block index rather than arrival order is what keeps the two tracks locked together when one producer is briefly slower than the other.

Reading audio from the client

Inbound audio arrives as (1, samples) int16 at 48 kHz, read the same way as video but in FIFO order so no samples are dropped:
See Media Input for why audio wants FIFO and video wants the newest frame.

Next

Media Input

Read the client’s camera and microphone.

Recording

Capture the model’s output into downloadable clips.