Livestream Mode
When using LiveKit's livestream mode, the system optimizes for large rooms by controlling which participant information gets broadcasted to all users in the room.
Overview
Livestreaming Mode, also known as "skip-subscriber-broadcast", is needed for Livestream use cases where there is a room with:
> 1,000 subscriber participants (the exact number is higher but this is a good threshold to use for the details outlined in this guide)
a small number of hosts/publisher participants
In these cases, clients can get overwhelmed by all of the participant events. So, enabling this feature will disable all events of participants who are non-publishing (i.e., only subscribers).
Participant Types
In livestream mode, participants are divided into two types:
Publishers
These participants have their information sent to all room participants. A user becomes a publisher when either of the following is true:
User has published one or more tracks
Metadata or attributes have been set on the user
Viewers
These participants have not set any metadata or attributes and are not publishing tracks.
Their information is not sent to others to optimize performance in large rooms.
Impact on Interactive Features
After enabling this mode, there are a number of potential impacts to be aware of:
RemoteParticipants: In the client SDK, Remote Participants will only contain publishers
The full list of participants will only be accessible from the server APIs.
Chat: When using chat components, the participant name will not be available (because remote participants are not populated). So, you will need to send the name of the sender encoded with the message.
Most customers will send the message as a JSON object with the sender name + the message. For example:
{ "sender": { "username": "johndoe", "Full Name": "John Doe" }, "message": "Hello World!" }This is just one example of the JSON format - you are welcome to use any format (or even use something other than JSON).
Subscriber Participant Events: The SDK will not fire participant events (like participant join, etc.) for subscribers.
If dependent on these events, please re-architect to avoid this dependency.
Events will fire for publishers
React Hooks: React hooks work differently in livestream mode:
useRemoteParticipantswill work but will not include viewer participantsuseLocalParticipantworks the same way for viewers
Metadata Impact: If you set metadata on a participant, that data will be sent to everyone even in livestream mode
Clearing Publisher Flag: Once a participant's information starts being broadcasted, there's currently no way to stop it during the same session by clearing metadata, as the system would need to send updates to everyone to clear the already-set metadata.
Workarounds
Break up the large room into smaller rooms
If you need the participant events, one option is to split the room into multiple smaller rooms. The rooms should be < 500 participants.
If you do this approach, then you will also likely need a single large room with all participants where the livestream mode is enabled. i.e., you can use the large room for showing a video to everyone, where the smaller rooms will be for handling smaller interactions.
Building with Livestream Mode
Getting participant count in livestream mode
When livestream mode is enabled, clients don't receive join or leave events for participants who aren't publishing, so counting remoteParticipants or listening for ParticipantConnected will undercount viewers. Instead, read room.numParticipants (and room.numPublishers). The server sends these counts to every connected client as part of room info, at most about every 5 seconds. The SDK doesn't emit an event when the count changes, so poll the property on an interval and update your UI when the value differs:
ts
import { Room, RoomEvent } from 'livekit-client';const room = new Room();await room.connect(url, token);let lastCount = -1;const updateCount = () => { const count = room.numParticipants; if (count !== lastCount) { lastCount = count; renderViewerCount(count); // your UI update }};updateCount(); // initial value from the join responseconst timer = setInterval(updateCount, 2000);room.on(RoomEvent.Disconnected, () => clearInterval(timer));The same value is available in other SDKs (num_participants on RoomInfo in Rust; Unity exposes it as of client-sdk-unity#177). If you need counts on your backend, for example for a page listing active streams, read the count from an agent already in the room, or connect a lightweight server-side participant with the Go SDK and watch the same synced value. Polling ListRooms() on a schedule also works but will lag. Avoid relying on participant counts in webhooks for this, since they can be inaccurate when room state hasn't yet synced across servers.
Showing the participant list in livestream mode
In livestream mode, LiveKit doesn't broadcast participant info for subscriber-only participants to anyone in the room, including the host or publisher. room.remoteParticipants will only contain participants who are publishing a track or have non-empty metadata or attributes, and ParticipantConnected events won't fire for other viewers.
To show a host the full viewer list, build it on your backend and deliver it to the host yourself. Webhooks are not affected by livestream mode: participant_joined and participant_left are still sent for every participant. Alternatively, call the ListParticipants server API on demand.
Notes
Webhooks can be retried or arrive out of order. Key the list on participant identity so duplicate events are harmless. If exact accuracy matters, periodically reconcile against
listParticipants.Only the host needs this list. Don't have every viewer poll the endpoint; at livestream scale that recreates the load livestream mode is meant to avoid. For the viewer count alone, viewers can use
room.numParticipants(see "Getting participant count in livestream mode").To make specific non-publishing participants visible to everyone in the room, such as a moderator agent, co-hosts, or a viewer with a raised hand, set metadata or attributes on them, either in the access token or with
UpdateParticipant. Reserve this for a small number of participants; making every viewer visible defeats the purpose of livestream mode.
