atmoky Engage Server SDK is now available! Check out the documentation to learn more.
Real-time Communication
Publishing Tracks

Publishing and Subscribing Tracks

This guide explains how to publish audio and video tracks to a room, and how to subscribe to tracks published by other participants.
Whenever a user wants to share their microphone input, webcam feed, or screen, a track is created and published to a room. Already connected participants will then be notified of a new track publication. If they want to receive that track, they simply subscribe to it and will start receiving the track's data.
The Engage SDK has some handy wrapper methods taking care of creating and publishing tracks for you, like LocalParticipant.setMicrophoneEnabled(true) and LocalParticipant.setCameraEnabled(true).
For a more fine-grained control, you can also create tracks manually and publish them to a room.


A Track is a representation of an audio or video stream, e.g. from a microphone or webcam, which can be sent to a room and received by other participants. A Track with a local media source is called a LocalTrack, while a Track received from a remote participant is called a RemoteTrack.

Kinds and Sources

A Track.Kind can be either audio or video. Tracks from an audio source are represented by the classes LocalAudioTrack and RemoteAudioTrack. Their kind member is set to "audio" or Track.Kind.Audio. Similar, tracks from video sources are represented by LocalVideoTrack and RemoteVideoTrack. Their kind member is set to "video" or Track.Kind.Video.
A Track.Source can be either camera, microphone, or screen_share. The source member of a Track is set to one of these values.

Creating a LocalTrack

LocalTracks can either be created using their constructors (see LocalAudioTrack and LocalVideoTrack), or by using the create<...>Track(s) utility functions:
Using createLocalTracks to capture both audio and video at the same time will prompt the user for permission to access their microphone and webcam only once.
import {
} from "@atmokyaudio/engage-client";
async function captureMicrophone(deviceID?: string) {
    const audioTrack = await createLocalAudioTrack({ deviceID });
    return audioTrack;
async function captureWebcam() {
    const videoTrack = await createLocalVideoTrack();
    return videoTrack;
async function createScreenCapture() {
    const screenTrack = await createLocalScreenTracks()[0];
    return screenTrack;
Checkout the DeviceManager for getting a list of available audio and video devices.
You can create local tracks before joining a room, so the user can preview their audio and video input before joining.


In order to share a LocalTrack with other participants, it needs to be published to a room. This is done by calling LocalParticipant.publishTrack.
captureMicrophone().then((audioTrack) => {
    localParticipant.publishTrack(audioTrack).then((publication) => {
        console.log("Published microphone track");
Participants already connected to the room will be notified of the new track publication by the RoomEvent.TrackPublishedevent emitted by the room, or alternatively by the ParticipantEvent.TrackPublished event emitted by the publishing participant. When a user joins a room after the track has been published, they won't receive these events, but can access all already available publications by traversing RemoteParticipant.tracks, or for specific track kinds RemoteParticipant.audioTracks and RemoteParticipant.videoTracks. These containers map a track's SID (unique server-generated identifier) to the corresponding RemoteTrackPublication. The LocalParticipant also has these containers holding the local tracks published to a room.
Tracks can be unpublished by calling LocalParticipant.unpublishTrack(localTrack):
// Example for unpublishing the local microphone track
// find local audio track publication with microphone as source
const microphoneTrack = localParticipant.getTrack(Track.Source.Microphone);
// unpublish track
if (microphoneTrack) {
        .then((publication: LocalTrackPublication | undefined) => {
            if (publication) {
                console.log("unpublished microphone track");


Important note: Audio tracks from microphones are automatically subscribed to, see AudioConnectionManager.

Subscribing to a RemoteTrack

In order to receive a RemoteTrack, it needs to be subscribed to.

Find the corresponding RemoteTrackPublication

You first need to get ahold of the RemoteTrackPublication you want to subscribe to. This can be done by traversing the RemoteParticipant.tracks container (or the more specific audioTracks or videoTracks subsets).
If the desired track hasn't been published yet, you can set up a ParticipantEvent.TrackPublished / RoomEvent.TrackPublished event listener to get notified of new publications.


Just call RemoteTrackPublication.setSubscribed(true) and the SDK is starting the subscription process.

Handle subscription events

Your subscription is ready and the track is being received when the TrackEvent.Subscribed event is emitted by the RemoteTrackPublication. This event will also be re-emitted by the RemoteParticipant and even the Room itself.
Let's say we want to subscribe to the video tracks of a remote participant:
// 1. create useful event handlers
// handling newly published tracks
const handleRemoteTrackPublished = (publication: RemoteTrackPublication) => {
    if (publication.kind === Track.Kind.Video) {
        // subscribe to the video track
// handling subscribed tracks
const handleVideoTrackSubscribed = (track: RemoteVideoTrack, publication: RemoteTrackPublication) => {
    console.log("Successfully subscribed to video track", track);
    console.log("This track is part of the following publication", publication);
    console.log("Let's display it in a video element");
    const videoElement = /** create or get an HTML video element */;
// 2. attach them to the remote participant
remoteParticipant.on(ParticipantEvent.TrackPublished, handleRemoteTrackPublished);
remoteParticipant.on(ParticipantEvent.TrackSubscribed, handleVideoTrackSubscribed);
// 3. subscribe to all already published video tracks
remoteParticipant.videoTracks.forEach((publication) => {
Note, that we have attached the event handlers to the RemoteParticipant instance. You can also make them more general and attach them to the Room instance, which will re-emit the events with the RemoteParticipant instance the event originated from.
In the example above, the remoteParticipant variable could come from a RoomEvent.ParticipantConnected event handler, or when iterating over Room.participants when joining the room.
room.participants.forEach((remoteParticipant) => {
    // ...


Unsubscribing is fairly easy, simply call RemoteTrackPublication.setSubscribed(false) and the SDK will stop receiving the track's data.

Related Functions, Methods, and Events

All-in-one wrappers

Creating Tracks

Publishing/Unpublishing Tracks

Finding Tracks / TrackPublications

Subscribing to Tracks

atmoky Logo

© 2024 atmoky, GmbH.
All rights reserved.

We use cookies to measure and improve your browsing experience, and serve personalized ads or content.