Optimize for Low Bandwidth Networks - Javascript
When participants join a call from a weak or unstable network, the available bandwidth may not be enough to support the call at its default media settings. VideoSDK provides several controls that you can use to adapt the experience to changing network conditions.
This guide covers recommended approaches for managing the media each participant sends and receives, including choosing the video quality to publish, controlling subscriptions, and keeping participants informed as conditions change. These options can help you reduce bandwidth usage and maintain a reliable call experience.
Before the Call
Measure the participant's connection before media is published, then use the result to choose a starting configuration that suits the connection.
Test the Connection
- Run
runPreCallTest()before the participant joins the meeting. It returns a quality score from 1 (BAD) to 5 (EXCELLENT) for both uplink and downlink, along with a factors array that identifies the conditions affecting the score. Use these results to choose a starting media configuration that is appropriate for the participant's connection. - See Precall Setup for the parameters accepted by
runPreCallTest(), the complete result object, and the recommended configuration for different scores and factors.
Evaluate uplink and downlink separately, and use the score as an overall measure of connection quality rather than as an indication of available bandwidth. A low score can result from factors such as high latency even when sufficient bandwidth is available. In that case, lowering video quality may not improve the connection.
Configure the Meeting
Use the following meeting settings to control how participants publish and receive media. Select the settings based on the participant's network conditions and the requirements of your application. These settings are passed to VideoSDK.initMeeting().
Configure Published Media
- Use
customCameraVideoTrackandcustomMicrophoneAudioTrackto pass media tracks configured for the participant's connection, based on your use case and the pre-call test results. If these are not provided, VideoSDK uses its default track configuration. - Create the camera track with
createCameraVideoTrack()and the microphone track withcreateMicrophoneAudioTrack(). Both accept anencoderConfig, while the camera track also acceptsbitrateModeandmaxLayer. - For constrained connections, consider
bitrateMode: "bandwidth_optimized"for the camera to reduce bandwidth usage at the selected resolution. See Optimize Video Tracks for the bitrate used by each video configuration and Optimize Audio Track for the available audio configurations. - You can also configure the video
codecto suit your application's requirements. Codec support and configuration options vary, so see Video Codecs before changing the codec or using it with other video settings. - To change the published quality during the meeting without replacing the track, use
meeting.setWebcamQuality()withlow,med, orhigh. - To enforce an upper limit on the amount a participant can send, set Maximum Send Bitrate Per Participant for the API key in the VideoSDK Dashboard. The limit applies to every session using that API key and overrides higher bitrate settings configured in your application. See Optimize Audio and Video Usage for details.
Keep Simulcast Enabled
- Keep
multiStreamenabled, which is the default. WithmultiStream: true, the camera publishes multiple video quality layers. VideoSDK can then deliver an appropriate layer to each participant based on the receiving conditions and requested video quality. - With
multiStream: false, the camera publishes a single video layer at the configured resolution. All participants then receive that same published layer, even when their network conditions or video layouts differ. - See Simulcast (Adaptive Bitrate) for more information.
Control Media Consumption
- For large calls, consider
autoConsume: falsewhen the application needs explicit control over which streams each participant receives. With this setting, streams are consumed only when requested, allowing the application to subscribe only to the participants required by the interface. - Use
participant.consumeWebcamStreams(),participant.consumeMicStreams(),participant.stopConsumingWebcamStreams(), andparticipant.stopConsumingMicStreams()as participants enter and leave the visible part of the interface.
Choose the Participant's Media Mode
Use the mode setting to control whether a participant publishes and receives media.
| Mode | Description |
|---|---|
SEND_AND_RECV | Publishes and receives media. This is the default. |
RECV_ONLY | Receives media without publishing. Consider this for participants who only need to watch or listen, or when upload bandwidth is limited. |
SIGNALLING_ONLY | Does not publish or receive media. Consider this when the participant does not need real-time media. |
- Choose a mode that matches the participant's role and network conditions.
- Use
meeting.changeMode()to switch between modes during the meeting.
Improve the In-Call Experience
Network and media conditions can change during a meeting. Use VideoSDK events and statistics to keep participants informed when the connection is reconnecting, video quality changes, or media conditions affect their experience.
Keep Participants Informed
Use call and media events to provide clear feedback when conditions change during the meeting. Depending on your application, this can be a toast, banner, status indicator, or other in-call message.
| Signal | What it tells you | Recommended response |
|---|---|---|
quality-limitation on meeting | Reports a limitation affecting the local participant's outgoing media. The type can be bandwidth, congestion, or cpu, and the state can be detected or resolved. See Monitoring Network Quality. | When a limitation is detected, consider informing the participant. For bandwidth- or congestion-related limitations, you can also reduce the quality of the published video, for example with setWebcamQuality() as described in Configure Published Media. |
meeting-state-changed on meeting | Indicates a change in the meeting connection state, including RECONNECTING when the connection is interrupted. See Meeting Connection State Events. | Show a clear reconnecting message or status indicator so the participant knows the connection is being restored. |
state on stream | Reports the state of a remote participant's video or screen-share stream as active, freeze-detected, freeze-resolved, stuck, or ended. See Stream Events. | Show feedback on the participant's tile while their stream is frozen or stuck, so the interruption is explained rather than appearing as a broken tile. Clear the feedback when the stream reports freeze-resolved or active. |
video-quality-changed on participant | Indicates that a participant has switched to a different simulcast layer. See Simulcast (Adaptive Bitrate). | When relevant to your interface, show a video-quality indicator or other feedback so the participant understands that the received video quality has changed. |
getVideoStats() and getAudioStats() | Provide statistics for a remote participant's incoming media, including values such as bitrate, rtt, jitter, and packetsLost. See Understanding Call Quality. | Use these statistics when your application needs to identify or display more detailed information about the quality of a remote stream. |
getTransportStats() | Reports the total bitrate being sent and received by the participant. See Monitoring Transport Stats. | Use this when your application needs additional information about overall media usage or to support custom quality-related UI. |
Optimize Incoming Video
We recommend rendering incoming camera video with participant.renderVideo(). It returns a container element that manages the stream for you, requesting a quality that matches the container's size and pausing the stream while the container is out of view.
| Recommendation | How |
|---|---|
Render camera tiles with renderVideo() | Leave maxQuality at its default of "auto". The container is watched for resizes and for visibility, so the participant receives a layer suited to the container and nothing while it is off screen. See Scalability for Large Participant. |
| Pin a quality when the layout is fixed | Pass maxQuality as low, med, or high to request that layer instead of sizing automatically. |
| Match quality to tile size yourself | When you render your own <video> element instead, use participant.setQuality() with high for a participant displayed prominently and low for smaller tiles. See Manual Quality Selection and Display Audio and Video. |
| Pause streams that are not visible | When you render your own video element, use stream.pause() while a participant is outside the visible area and stream.resume() when they return. See Grid with Pagination. |
| Reduce screen-share quality | Use participant.setScreenShareQuality("low") when bandwidth is limited. Screen-share quality is managed separately from camera video. |
| Enable adaptive subscriptions | Use meeting.enableAdaptiveSubscription() to let VideoSDK prioritise streams for you. Under bandwidth constraints it pauses muted participants' video first, then the least dominant speakers, and keeps pinned participants at the highest quality. See Scalability for Large Participant. |
| Pin the participants that must stay sharp | Adaptive Subscription keeps pinned participants at the highest quality, so use participant.pin() with CAM, SHARE, or SHARE_AND_CAM to protect a speaker or a shared screen, and participant.unpin() when they no longer need it. |
| Stop receiving video altogether | Use meeting.pauseAllStreams("video") to drop to an audio-only call when the connection cannot carry video, and meeting.resumeAllStreams("video") when it recovers. Audio is a small share of the call, so the conversation continues. |
Optimize Data Transfers
Call media is not the only traffic using the participant's connection. Image captures and application updates also consume bandwidth, so keeping these transfers efficient can help preserve bandwidth for the call, especially on weak networks.
Capture Images Efficiently
captureImage()captures a frame from the video stream on the device that calls it. On the device that owns the camera, the image comes from the local camera track at its capture resolution.- On another participant's device, the image comes from the video layer that device received, which may be lower resolution on a constrained connection.
- For use cases such as document capture or identity verification, capture the image on the device that owns the camera rather than relying on a received video layer. This avoids increasing the published video quality just to obtain a higher-resolution image.
Consider the following when transferring a captured image:
| Recommendation | How |
|---|---|
| Request the capture from the camera owner | Use meeting.pubSub to request the image from the participant who owns the camera, then capture it locally on that device. See Image Capturer. |
| Capture only the required size | Pass width and height to captureImage() when a smaller image is sufficient. If the requested size exceeds the camera's capture resolution, the camera resolution is used instead. |
| Transfer large images carefully | Send the image over meeting.pubSub as a Base64 string. For larger images, split the data into chunks and include a transfer ID, chunk index, and total number of chunks so the receiver can reassemble it. See Overview. |
Optimize Message Delivery
Chat, reactions, application state, and other updates can also use bandwidth during a call. Choose the delivery method based on how important the data is and how quickly it becomes outdated.
| Use case | Recommendation | Guide |
|---|---|---|
| Cursor positions, reactions, or other frequently changing updates | Use UNRELIABLE when an update can be skipped safely. This prevents an outdated message from delaying newer updates. | DataStream |
| Persisted messages delivered when a participant joins | Set oldMessageLimit on meeting.pubSub.subscribe() when the full message history is not required. | Overview |
| High-frequency realtime topics where stale updates have little value | Set realtimeOverflow: "drop" so new updates are not blocked by messages waiting to be delivered. | Overview |
API Reference
The API references for all the methods and events utilized in this guide are provided below.
- runPreCallTest()
- createCameraVideoTrack()
- createMicrophoneAudioTrack()
- initMeeting()
- setWebcamQuality()
- changeMode()
- enableAdaptiveSubscription()
- quality-limitation
- meeting-state-changed
- video-quality-changed
- renderVideo()
- setQuality()
- setScreenShareQuality()
- pin()
- unpin()
- pauseAllStreams()
- resumeAllStreams()
- captureImage()
- getVideoStats()
- getAudioStats()
- getTransportStats()
- consumeWebcamStreams()
- stopConsumingWebcamStreams()
- consumeMicStreams()
- pause()
- resume()
- pubSub
Got a Question? Ask us on discord

