Scaling applications to support large calls

Video call UI displays many participants on the call

Daily defines large meetings as real-time calls with 50 to 100,000 participants. Additionally, you can output large calls to an RTMP platform and live stream your video call. All large calls need to route hundreds or even thousands of participants' media tracks at a time, which means there's quite a bit of room for optimizing calls through Daily configurations and app UI adjustments.

This guide covers the Daily recommended settings for few-to-many calls, including Daily's 100,000 participant interactive live streams. We'll also review best practices to scale large calls in terms of interacting with the Daily APIs and recommended app-level decisions.

Defining large experiences

There are three main categories of large calls to consider when building your Daily app or choosing your room configurations:

  • Large real-time calls where everyone (up to 1,000 participants) is active, meaning anyone in the call can turn on their devices. This is possible in custom Daily apps and apps using Daily Prebuilt.
  • Interactive live streaming of up to 100,000 participants: Up to 25 active participants can join with their devices on in a call, and up to 100,000 participants can join the call in real-time. This is a type of "few-to-many" call setting available through Daily, which supports a group of speakers in a call and a large group of participants.
  • RTMP output (live streaming): Stream your real-time call to a third-party streaming platform to grow your live viewership into the millions. In this configuration, you are connecting Daily's WebRTC calls to an HLS platform.

We'll take a look at these options below.

We'll also review additional ways to make your app more performant in our Best Practices section, including how to incorporate:

For all calls with over 200 participants, the max_participants room property must be updated. (The default value is 200.) Contact our support team for more information.


Large real-time calls with up to 1,000 participants

Daily defines large calls as sessions with up to 1,000 active participants. Conference breakout rooms or workshops, corporate trainings, and other virtual events where any participant may need to speak all fall under this category.

Platforms to support large sessions can be built using either Daily Prebuilt or Daily's call object. In either case, core Daily features like recording, output to live streaming platforms, and transcription are all available.

Let's now take a look at the specific settings required to support calls of up to 1,000 participants in both Daily Prebuilt and custom ("call object") Daily apps.

Daily Prebuilt calls: Up to 1,000 participants

To support large calls of over 50 participants and up to 1000 participants, you'll need the following settings:

The experimental_optimize_large_calls room configuration will have several side effects in Daily Prebuilt to help manage your call's performance with such a large number of participants:

  • The local participant will be automatically muted on join. They can unmute as needed once they've joined a call.
  • The grid view will show a maximum of 12 participants in the grid at a time, compared to the default of 25 and maximum of 49.
  • Only 8 users can be unmuted at the same time. When more than 8 users are unmuted, the oldest active speaker will be automatically muted.

In addition to these room settings, Daily Prebuilt is built to optimize performance based on its layout options. With Daily Prebuilt, each participant can view the call with either of the following two views:

  1. Active speaker layout, where the person currently speaking takes up the majority of the screen.

Daily Prebuilt active speaker mode

  1. Grid view, a paginated grid layout with a subset of participants visible on screen.

Daily Prebuilt active speaker mode

In larger calls, grid view will automatically be paginated and active speaker mode will have a scrollable participant bar for the non-active participants. These features help avoid all media tracks being rendered at the same time.

Custom Daily app calls: Up to 1000 participants

Interactive sessions built on the Daily call object not only offer deeper control over branding, design, and experience, but also open up potential for direct track subscription management.

These calls can support up to 1000 participants with all cameras on, with each participant viewing (and receiving tracks from) at most 12 other participants at a time.

In order to support calls of up to 1000 participants, you'll need to set the enable_mesh_sfu option to true. To further optimize performance of custom apps hosting large calls, read our Best Practices section below.


Interactive live streaming and RTMP output (traditional live streaming)

Daily's interactive live streaming refers to calls where up to 25 participants can turn on devices and a total of 100,000 participants can watch the call in real time.

Alternatively, Daily sessions can be broadcast to multiple live streaming platforms, using RTMP output.

Choosing between Interactive live streaming and RTMP output for traditional live streaming

Both interactive live streaming and RTMP output can support large audiences. They are two different technologies that offer your audience different latencies and levels of engagement.

Interactive live streaming is useful when all participants need to view and hear the discussion in real time. Some examples include:

  • Live commerce, where attendees need to bid or shop in real time
  • A town hall, where an attendee doesn't need to have their camera on, but needs to be able to submit chat questions in real time

For these kinds of use cases, the ability for all participants to click on a link and join in real time is key. With Daily's Interactive live streaming, up to 100,000 participants can join; they can watch/listen (as well as chat) in real time. Up to 25 participants can have their cams/mics on.

In contrast, you may not need full audience engagement in real-time. You only may need to "broadcast" an interview, where a broadcast delay is acceptable. For example, you can use Daily Prebuilt or build a custom app with our client SDKs to conduct the interview. You then can connect that Daily call to a streaming platform like AWS IVS, YouTube Live, Twitch, or your own custom embedded player via RTMP output. Your audience is watching the stream of the video call with latencies that range between 8 and 20 seconds (depending on the streaming platform you connect to). That is in contrast to the sub-400ms latency of a Daily interactive live stream.

In summary, Daily's interactive live streaming is more engaging and dynamic than RTMP. Up to 100,000 people can participate in real time. If your use case calls for larger numbers of passive viewers, you can use our RTMP output to stream to the audience.

Let's take a closer look at interactive live streaming, which accommodates the largest real-time call sizes.

Interactive live streaming: Up to 100,000 participants

Large call with 100,000 participants

Interactive live streaming is a type of few-to-many call. It allows up to 25 call participants to turn on their devices and up to 100,000 attendees to view the call in real-time.

General rules for 100,000-participant calls

Participants who can turn on their devices are considered "active" and participants who cannot turn on devices are considered "hidden."

Active participants are participants who have a "presence" in a call. They can turn on their devices during the call and have their participant information available in the participants() method return value.

Hidden (or "passive") participants are considered to not have a "presence." They can view a real-time call but cannot turn on their devices and do not have their participant information returned by the participants() method.

Active and hidden participant count totals can be retrieved via the participantCounts() method and tracked via the "participant-counts-updated" event.

There are a few key numbers to be aware of when supporting calls of up to 100,000 participants with Daily:

  • Daily calls involving more than 1,000 participants should be configured as interactive live streams. Use the room configurations listed below once you plan to have 1,000+ participants in your call.
  • At most, 25 participants can be active. This means 25 cameras and microphones can be on, as well as one screen share.
  • All other participants must have cameras and microphones off.
  • Every client will receive real-time video and audio, whether they're a speaker or attendee.

When we say "real-time", we mean it! We have a 82ms median end-to-end network latency for all sessions across our global user base.

Custom implementations can have their performance further optimized with our Best Practices suggestions below.

There are a few required room settings to support call sizes this large and create the active vs. hidden participant dynamic. Keep reading to learn about settings you'll need for Daily Prebuilt or custom apps.

Daily Prebuilt configurations to support 100,000 participants

To set up interactive live streaming with Daily Prebuilt, the following room and/or domain properties must be set to true:

Here is an example cURL command enabling these room properties:

Custom app configurations to support 100,000 participants

To set up interactive live streaming session with a custom Daily app, the following room properties must be set to true:

Here is an example cURL command enabling these room properties:

To control who starts a call as a presenter and who starts as a viewer, consider using the start_audio_off and start_video_off meeting token properties.

  • Presenters should have these properties set to false, the default. This means their devices will start by being on.
  • Viewers should have these properties set to true, so their devices are off when they join the call.

You can also limit who can turn on devices via the is_owner meeting token property, and only allow meeting owners to access their devices.

Balancing the number of total participants with the number of devices turned on is important for managing app performance. To host calls with more than 12 active participants (i.e. with devices on) and more than 200 total participants, contact our support team.

Daily sessions can be broadcast to multiple live streaming platforms, like AWS IVS or YouTube Live. Live streaming can be the best option for a large call if hundreds of thousands of people or more could be tuning in, or if providing a viewing link to attendees is preferred to managing silent participants on a call. For more details on Daily live streaming and how to implement it, head to the guide.

To learn more about customizing the layout of Daily live streams or adding custom graphics, read our guide on using Daily's Video Component System (VCS).


Best practices to scale large calls

As the number of call participants increases, the number of audio, video, and screen media tracks that need to be routed and then handled by each web client multiplies. This can drain CPU, strain networks to their limits (risking undelivered media streams), and degrade user experience quickly, especially on older or mobile devices.

To optimize performance on calls with hundreds of participants, pagination, track subscriptions, and simulcast layer control can all be implemented using Daily’s APIs. Analyzing call logs can also help improve call quality.

Pagination

Pagination limits the number of participants displayed on a screen at a time, so a call participant has to click to rotate through all other attendees. This reduces the load on any individual participant’s CPU and bandwidth.

Demo app displays colored tiles that rotate when the right and left arrow buttons are clicked

The gif highlights a demo app built on React, but pagination can be implemented regardless of the framework used.

Use the Daily participants() method to access the full list of active participants in a call. Once the participants are known, you can do the following to build out your pagination logic:

  1. Establish constant UI elements like the minimum participant video tile width, default aspect ratio, and the maximum number of tiles per page.

  2. Use the UI constants and the number of call participants to calculate the total number of pages. The number of pages, and which participants are on each page, should update as the number of call participants changes, so set up the state management of your choice.

  3. Add click handlers to the pagination buttons that update the app state to reflect the current page being viewed.

  4. Set up a handler to determine the visible participants on a given page, using the current page, number of participants, and maximum number of tiles per page to make a copy of the participants object that only includes visible participants.

  5. Iterate over the visible participants object to render each participant in the UI.

Track subscriptions

Daily calls operate on a publish-subscribe model: participants publish audio, video, and screen MediaStreamTracks, and are subscribed to other participant’s tracks.

By default, Daily routes a participant’s distinct set of tracks to all the other participants on a call. In large calls with hundreds and thousands of participants, decoding all that video can demand a lot of network bandwidth and processing power.

Turning off Daily’s default track handling in favor of manual track subscriptions can deliver better call quality during sessions with many participants. Track subscriptions can also enable features like breakout groups, and improve features like pagination.

There are three steps to set up direct track subscriptions:

  1. Set the subscribeToTracksAutomatically call object property to false.

This turns off Daily’s default track management. This property can be passed on join(), at createCallObject(), or via the setSubscribeToTracksAutomatically() method. The latter is a good option if you want to wait to turn on track subscriptions until a certain number of participants have joined the call.

  1. Call updateParticipant() or updateParticipants() to change the subscribed value of a participant's tracks property.

Each individual participant’s tracks property can be found on the Daily participants object.

The tracks for a participant include audio (microphone), video (camera), and screenAudio and screenVideo (screenshare) properties. Each track type contains: a raw media stream track object available on both track and persistentTrack, the state of the track, and its subscribed value.

state indicates if the track can be played (full possible states in the participants() documentation).

subscribed tells us whether or not the local participant is receiving the track information from the participant who the track belongs to. Its value is true if the local participant is receiving, false if they are not, or "staged".

A subscribed status of "staged" keeps the connection for that track open, but stops any bytes from flowing across. Compared to a complete connection teardown through an unsubscribe, staging a track speeds up the process of showing or hiding participants' video and audio. Staging also cuts out the processing and bandwidth required for that track.

On calls with many participants, setting the subscribed value to staged or false depending on the interface can minimize the load on participants’ connections, improving the user experience. Paginated grid apps (including Daily Prebuilt!) often subscribe to the tracks of participants on a current page, stage those on the previous and next pages, and unsubscribe from the rest.

Graphic with subscribed participant tiles in green, staged in the previous and next tiles in yellow, unsubscribed in red

Use the updateParticipant() and updateParticipants() Daily methods to update participants’ subscribed states.

updateParticipant() receives an object with the participant’s id as the key, and the value a setSubscribedTracks object indicating the new subscribed status of each track type.

If using updateParticipants(), combine all participants’ updates into one object.

  1. Listen for participant events to reflect updated participant state in the app interface.

The updateParticipant() method fires a corresponding "participant-updated" event. Add a listener to this event to update the app interface as participants’ subscribed statuses change.

Simulcast layer control

To take advantage of simulcast layers, the call must be happening over an SFU connection. To test over SFU in development, use the setNetworkTopology() method. This is only for testing. In production, once a fifth participant joins a call or if recording, transcription, or a live stream is started an SFU connection will be automatically established.

Once a fifth participant joins a Daily call or if recording, transcription, or a live stream is started instead of direct peer-to-peer (P2P) connections, tracks are first sent to a Selective Forwarding Unit (SFU). From there, the SFU processes, re-encrypts, and routes media tracks to participants, allowing tracks to be "selectively" forwarded.

SFU square in center of graphic forwards arrows out from other squares representing tracks forwarded to other participants

With WebRTC simulcast, instead of sending a single track to the SFU, a publishing participant’s web client sends the same source track at a few different resolutions, bitrates, and frame rates. Each of these groups of settings is known as a simulcast layer.

By default, the Daily client and SFU will work to send the highest layer possible.

There are several factors that affect which layer a participant receives. Those factors include:

  1. The available bandwidth on the receiving end: The SFU will send a lower layer if the current one exceeds the available bandwidth.
  2. The send-side is not sending all the layers: This typically occurs when the browser detects network or CPU issues or the highest video resolution of the device does not support the highest layers. It's also worth noting that the browser can and will modify the actual bitrate, frame rate, and resolution sent on each layer for these same reasons, so the actual settings used may not match the configuration.

Daily default desktop simulcast layers and their settings

Layer 0Layer 1Layer 2
Bitrate (bps)80,000200,000680,000
Frame rate (fps)101530
Resolution (width height)320x180640x3601280x720

Daily default mobile simulcast layers and their settings

Layer 0Layer 1
Bitrate (bps)80,000520,000
Frame rate (fps)1030
Resolution (width x height)320x1801280x720

While the default Daily simulcast management suits most use cases, it is possible to adjust the quality of both the video a participant publishes to the server (send-side) and the video that participants receive (receive-side). This can be useful in large calls as a tool to optimize bandwidth, but proceed with caution. Attempting to make changes while calls are in progress or to use values beyond browsers' capacities can cause problems. Please reach out if we can help.

Daily recommends aiming for a maximum bandwidth usage around 3 Mbps for most participants to ensure the best experience for everyone. For example, if you use camSimulcastEncodings to set a main speaker's highest spatial layer to 2 Mbps, then you'd want to make sure the other participant thumbnail videos don't add up to more than 1 Mbps. Otherwise, people's browsers will start using too much CPU to decode video and they'll see A/V sync issues, or other participants with slower connections will experience dropouts.

Change send-side video quality

Adjusting the quality of the video that participants send to the server can be useful in large calls. For example, the main active speaker showcased prominently in the UI could send higher quality video than the other call participants.

There are two properties that can be manipulated to adjust send-side video quality: camSimulcastEncodings or trackConstraints.

Use camSimulcastEncodings to specify spatial layer settings

The camSimulcastEncodings DailyIframe property defines maxBitrate, maxFramerate, and scaleResolutionDownBy values to set encodings for spatial layers that differ from Daily defaults.

  • maxBitrate: the desired video bitrate for the layer, in bits per second.
  • maxFramerate: an integer value lower than the maximum rate expected from the camera, in frames per second.
  • scaleResolutionDownBy: an integer power of 2. The horizontal and vertical dimensions of the source video will be divided by this number to get the resolution for the layer.

camSimulcastEncodings can be configured when the call object is created (via createCallObject) or on join().

Set video input quality with trackConstraints

The trackConstraints property, set via the setBandwidth() method, determines the 'input quality' of the video that a browser gets from a web cam by defining a maximum resolution (width and height values) and a maxFramerate.

The setBandwidth() call needs to happen before a video track is requested from the participant. Disable video on join() for participants whose bandwidth constraints need to be set manually, then call setBandwidth(), then enable the camera.

Adjust receive-side video quality

The Daily receiveSettings API can be used to override the SFU estimated forwarding and request a specific simulcast layer instead.

If many participant videos will be displayed or if they will be rendered on small mobile screens, for example, programmatically requesting a lower bitrate layer can improve call performance. Lower quality videos not only save downstream bandwidth, but also can dramatically improve CPU performance since they require fewer resources to render.

Graphic shows four participants at highest quality layer, nine at medium quality, twelve at lowest quality

To use Daily receiveSettings to request a specific simulcast layer:

  1. Request the updated simulcast layer, either through updateReceiveSettings() or by passing a receiveSettings object on join().

  2. Listen for the "receive-settings-updated" event to update the app interface in response.

Analyzing large call performance

See the logs and metrics guide for details on analyzing call quality.

Tutorials about scaling large calls