Skip to main content
Custom tracks let you send any MediaStreamTrack as an additional stream alongside your camera and microphone. Common use cases include:
  • Streaming additional camera angles (e.g., a document camera)
  • Broadcasting background music in stereo

Starting a custom track

startCustomTrack() takes a StartCustomTrackOptions object and returns a promise that resolves to the track name — which is used to identify the track in the participant’s track list and used for stopping it later.

StartCustomTrackOptions

track
MediaStreamTrack
required
The MediaStreamTrack to send. Can be audio or video.
trackName
string
A name for the track. Must be unique within the call. If omitted, Daily generates a UUID. The resolved promise value is the final track name.
Track names become part of the participant’s tracks object for the lifetime of the call. Choose descriptive, stable names — especially if remote participants need to subscribe to them by name.
mode
'music' | 'speech' | DailyMicAudioModeSettings
For audio tracks: sets the encoding mode. Use 'music' for music or wideband audio, 'speech' for voice. For fine-grained control, pass a DailyMicAudioModeSettings object.
ignoreAudioLevel
boolean
When true, this track’s audio level is excluded from the local audio level observer. Useful for background music that should not trigger speaking indicators.

DailyMicAudioModeSettings

bitrate
number
Target audio bitrate in bits per second.
stereo
boolean
When true, the track is encoded as stereo. Default: false.

Stopping a custom track

Passing the track name used in or returned by startCustomTrack() stops sending that track. The underlying MediaStreamTrack is not automatically stopped — call .stop() on it yourself if needed.

Canvas-based custom video track

A common pattern is to draw content onto an HTML <canvas> element and stream it as a custom track:

Receiving remote custom tracks

When subscribeToTracksAutomatically is enabled (the default), remote custom tracks are subscribed to automatically — no extra setup needed. Listen for track-started with the custom track name as type to render them. If you’ve disabled automatic subscriptions, use updateParticipant() with setSubscribedTracks to opt in. See the startCustomTrack() reference for the full custom subscription options.

Reading custom tracks from a participant

Custom tracks appear in the participant’s tracks object keyed by their track name:
participant.tracks is typed as DailyParticipantTracks, which allows arbitrary string keys:

track-started and track-stopped events

Custom track events arrive on the standard track-started / track-stopped events. The type field holds the custom track name:

Complete example