Skip to main content

Working with Events

Events are a fundamental aspect of working with the SDK. As introduced in the getting started section, events enable your application to respond to various changes and updates. For example, you can be notified when a

RemotePeer

joins or leaves the room, or starts an

AudioOutput

.

The SDK primarily revolves around three core Entities, each associated with a specific set of events:

How Events Propagate

AudioActivity and AudioPowerLevel events are dispatched in a fan-out chain:

  1. The underlying

    AudioInput

    or

    AudioOutput

    decides when activity changes — voice activity detection runs on the decoded audio in the worklet, and the event is emitted on the edge (state change), not on every sample.
  2. That media re-dispatches the event on its owning Peer.
  3. The Peer re-dispatches it on the

    Room

    .

Each level sees the same event — just at a different scope. Listening at the room level gives you every media's activity in one stream; listening on a specific peer narrows it to that peer's media; listening on a media instance narrows it to that one input or output. There is no duplication and no information loss between levels.

Choose the right level for many-user UIs

When rendering many users at once, prefer a single room-level handler plus your own per-user state map. Attaching a peer-level listener inside every per-user component multiplies listener traffic and re-subscribes on every component re-mount or input change — a common source of dropped events under load and momentary gaps when listeners are torn down and re-added.

Activity is independent of volume

AudioActivity events come from voice activity detection on the decoded audio, not from the playback gain. Calling room.setVolume(0), remotePeer.setVolume(0), or output.setVolume(0) silences local playback but does not stop activity events from firing. To actually stop receiving activity for a remote stream, call output.pause() — the server will stop sending packets, and the SDK dispatches a final Activity = false once the stream drains. Local input activity stops only when the

AudioInput

is removed from the room or its volume is set to 'muted'.

Read current state synchronously

You don't need to wait for the next event to know the current state. The following getters are always readable:

  • audioInput.isActive / audioOutput.isActivetrue while the media is currently emitting voice.
  • peer.isActivetrue if any of the peer's audio outputs is active.

This is useful when seeding UI state on mount, or after a reconnect, without waiting for the next edge-triggered event.

Room Events

The room is the main entity to work with. It's important to register its event handlers before joining. This ensures you capture all relevant events, particularly initial ones like the

RoomStatus

Event or

PeerJoined

Event.

info

As for all Events, there are two ways to register EventHandlers (callbacks). The obj.addEventlistener() and obj.onEvent approach. In the following examples, for simplicity, the onEvent property approach is used.

info

To prevent memory leaks when reusing a room instance for multiple connections, ensure that EventListeners are handled correctly. You should either:

  • Remove event listeners when leaving the room.
  • Ensure that event listeners are registered only once.

Alternatively, you can use the room.onPeerJoined syntax. This assigns a callback directly, overwriting any previous assignment, which can simplify event management and help avoid unintended multiple registrations.

Most relevant room events

Example room events
// Called, after the room was successfully joined.
room.onJoined = ({ room, peer }) => {
// Start Audio
// Manage UI state
};

// Called when a Peer joined. Also, gets called for peers who were already present.
// Peers that were already present will emit before the own peer. This can be handy in some cases.
room.onPeerJoined = ({ room, peer }) => {
if (peer.isRemote) {
// Do something for remote peers
} else {
// Do something for the local peer, also after this event occured, all following events will be peers that joined after.
}
};

// Called when a Peer left the room
room.onPeerLeft = ({ room, peer }) => {};

// Called, when the room status changed.
room.onStatusChanged = ({ oldState, newState }) => {};

// Called, when a RemotePeer started to transmit audio.
room.onAudioOutputStarted = ({ room, peer, media }) => {
// media is a AudioOutput, can be used to adjust playback volume or get acitvity
const volume = state.getVolumeFor(peer.id);
// Default is 1
media.setVolume(volume);
// UI Handling, for example display that the peer has audio
};

// Called, when a RemotePeer stopped to transmit audio.
room.onAudioOutputStopped = ({ room, peer, media }) => {
// UI handling, for example that this peer has no audio
};

// Called, when a message was received from a RemotePeer. Messages are trasmitted using `Uint8Arrays` and we offering helper functions
// to convert them into JSON and vice versa.
room.onMessageReceived = ({ room, peer, message }) => {
const json = uint8ArrayToValue(message);
};

// Called, when a RemotePeer updated its userdata. Userdata is trasmitted using `Uint8Arrays` and we offering helper functions
// to convert them into JSON and vice versa.
room.onUserDataChanged = ({ room, peer }) => {
const json = uint8ArrayToValue(peer.data);
// This can be information, if the user muted his mic or other user related information.
};

// Called, when one AudioInput or AudioOutput that is attached to the room is transmitting audio data.
// `media.kind` is either 'audio-input' (own peer) or 'audio-output' (remote peer); for outputs,
// `media.peer.userId` identifies the speaker.
room.onAudioActivity = ({ media }) => {
console.log(media.isActive);
};

Peer Events

There are two types of Peers, the

LocalPeer

(always one per room), representing the own peer, and

RemotePeer

, representing other peers in the room. All

PeerEvents

are also available on the

Room

, however, it can be very handy in some context to have them directly attached to the specific peer they are related to.

Most relevant peer events

Example peer events
// Called, when this RemotePeer started to transmit audio.
peer.onAudioOutputStarted = ({ room, peer, media }) => {
// media is a AudioOutput, can be used to adjust playback volume or get acitvity
const volume = state.getVolumeFor(peer.id);
// Default is 1
media.setVolume(volume);
// UI handling, for example that the peer has audio
};

// Called, when this RemotePeer stopped to transmit audio.
peer.onAudioOutputStopped = ({ room, peer, media }) => {
// UI handling, for example that this peer has no audio
};

// Called, when a message was received from this RemotePeer. Messages are trasmitted using `Uint8Arrays` and we offering helper functions
// to convert them into JSON and vice versa.
peer.onMessageReceived = ({ room, peer, message }) => {
const json = uint8ArrayToValue(message);
};

// Called, when this RemotePeer updated its userdata. Userdata is trasmitted using `Uint8Arrays` and we offering helper functions
// to convert them into JSON and vice versa.
peer.onUserDataChanged = ({ room, peer }) => {
const json = uint8ArrayToValue(peer.data);
// This can be information, if the user muted his mic or other user related information.
};

// Called, when one of the AudioInputs or AudioOutputs of this peer are active.
peer.onAudioActivity = ({ media }) => {
console.log(media.isActive);
};

// Called, when the powerLevel of one of the AudioInputs or AudioOutputs of this peer emits a new value.
peer.onPowerLevel = ({ media }) => {
console.log(media.powerLevel);
};

Media (AudioInput / AudioOutput) Events

Similar to Peers, Medias are separated into

AudioInput

and

AudioOutput

. As explained in the getting started section, the

AudioInput

is representing the local audio capturing and is created locally. Usually one. AudioOutputs are representing the audio playback from

RemotePeer

's.

More details about configuring the

AudioInput

will be provided in the working with devices section, this guide focuses only on events.

Most relevant media events

Example media events
// Called, when the media's voice activity state changes (edge-triggered).
// For AudioOutput, the callback receives an isActive boolean.
audioOutput.onAudioActivity = (isActive) => {
console.log(isActive);
};

// For AudioInput, the callback receives the AudioActivityPayload like at room/peer level.
audioInput.onAudioActivity = ({ media }) => {
console.log(media.isActive);
};

// Called, when the powerLevel changed. Values provided in rmsDbfs scale.
media.onPowerLevel = ({ media }) => {
console.log(media.powerLevel);
};