Design a Video Conferencing System like Zoom

HardMessaging & CollaborationWebRTCSFUReal-Time MediaSimulcast

Problem

Design a video conferencing service like Zoom or Google Meet. Users schedule or start a meeting and share a link that others use to join from a browser or app. Participants send and receive live audio and video with low delay, and anyone can share their screen. Meetings support up to 500 participants, and hosts can record a meeting to the cloud. The service carries 5 million concurrent participants at peak across 1 million concurrent meetings, with users on every continent.

  • What end-to-end delay is acceptable? Is 150 ms mouth to ear a reasonable target?
  • In a 500 person meeting, how many people need to be visible at once, and how many speak at once?
  • Do we need to support users behind strict corporate firewalls that block UDP?
  • Is end-to-end encryption required, or is encryption in transit to our servers acceptable?
  • What video quality is expected: up to 1080p for the active speaker, lower for thumbnails?
  • Are chat, breakout rooms, and live transcription in scope?

Items in bold are existing requirements. The rest are suggestions. Confirm with the interviewer whether they are relevant first.

  • Create a meeting and join it with a link and optional passcode.
  • Send and receive real-time audio and video between participants.
  • Share a screen with all participants.
  • Support meetings of up to 500 participants.
  • Record meetings to the cloud for later playback.
  • Host controls: mute others, remove participants, waiting room.
  • In-meeting text chat.

Items in bold are existing requirements. The rest are suggestions. Confirm with the interviewer whether they are relevant first.

  • 5 million concurrent participants in 1 million meetings at peak.
  • Mouth to ear latency under 150 ms within a region and under 300 ms across continents.
  • Adapt quality to each receiver's bandwidth and CPU without affecting others in the meeting.
  • Media bandwidth: each participant uploads about 1.5 Mbps (simulcast of three layers) and downloads 1 to 3 Mbps, so media servers move on the order of 10 Tbps in total at peak.
  • Meeting availability: a media server failure should cause a reconnect of a few seconds, not a dropped meeting.
  • Packet loss of a few percent must degrade quality gracefully rather than freeze the call.

A control plane handles accounts, scheduling, and signaling, and assigns each meeting to a selective forwarding unit (SFU) in a data center near its participants. Clients send their media once to the SFU using WebRTC over UDP, with simulcast layers at several resolutions, and the SFU forwards the right layer of each stream to each receiver without decoding it. Large or geographically spread meetings are served by a cascade of SFUs in several regions, and a recording bot joins as a hidden participant to capture the meeting.

Loading diagram...

Join. The client calls POST /meetings/:id/join. The signaling service checks the passcode and waiting room, asks the media allocator for an SFU (reusing the one already assigned to the meeting, or choosing the least loaded server in the region nearest most participants), and returns the SFU address plus a short-lived join token. The client opens a WebSocket to signaling for control messages and exchanges SDP offers and answers through it.

Media path. The client gathers ICE candidates, connects to the SFU over UDP (falling back to TURN over TCP or TLS on port 443 if UDP is blocked), and starts sending audio plus video in three simulcast layers: for example 1080p, 360p, and 180p. Media is encrypted with SRTP.

Forwarding. For each receiver, the SFU picks which senders to forward (the active speakers plus visible tiles) and which layer of each, based on the receiver's estimated bandwidth and the tile size it requested. It rewrites packet headers and forwards without decoding. When bandwidth drops, the SFU switches the receiver to a lower layer at the next keyframe.

Cross-region. If participants join from another continent, the allocator assigns them an SFU in their own region, and the two SFUs exchange one copy of each stream over a cascade link on the provider backbone.

Recording. When the host starts recording, a recording bot joins the meeting through the SFU, receives the streams, composes them into a gallery or speaker layout, encodes the result, and writes segments to object storage. When the meeting ends, transcoding workers produce the final playable file.

Control plane for meetings: authentication, joins, participant state, host controls, and WebRTC session negotiation.

  • Stateless service with WebSocket connections, backed by a relational database for meetings and Redis for live meeting state.

API

  • POST /meetings with { topic, startTime?, passcode? } returns { meetingId, joinUrl }.
  • POST /meetings/:id/join returns { sfuAddress, joinToken, iceServers }.
  • WebSocket messages: offer, answer, ice-candidate, mute, remove-participant, layout-request.

Data Model

  • meetings(meeting_id, host_id, passcode_hash, scheduled_at, settings)
  • Live state in Redis: meeting:{id} with participant list, roles, and assigned SFU.

Tradeoffs

Signaling is kept separate from media so control traffic scales with joins, not bandwidth, and so a media server can fail without losing meeting state. WebSockets give a bidirectional channel for renegotiation and host commands.

Media server that receives each participant's streams once and forwards selected streams to every other participant.

  • C++ or Rust media servers (similar in role to open source SFUs such as mediasoup or Janus) on hosts with high network throughput.

Diagram

Loading diagram...

Scalability

An SFU host can forward a few thousand streams. Meetings are pinned to one SFU when small; a 500 person meeting spreads across several SFUs in a cascade, and each receiver gets at most about 25 video streams (one page of tiles) plus the top three audio streams by loudness. Audio level is carried in an RTP header extension, so active speaker detection needs no decoding.

Tradeoffs

A mesh (everyone sends to everyone) needs no servers but collapses beyond four or five people because each client uploads N minus one streams. An MCU decodes and mixes everything into one stream, which is easy on clients but very expensive on servers and adds latency. An SFU sits in between: clients upload once, servers do no transcoding, and each receiver still gets individually adapted quality. End-to-end encryption is possible with an SFU because it only reads headers, not payloads.

Chooses which SFU and region serve each participant.

  • Service reading a live load table that every SFU updates every few seconds.

Scalability

Placement uses GeoDNS or anycast to estimate the client's region, then picks the least loaded healthy SFU there. Meetings that grow beyond one server are extended to a second SFU in the same region. If an SFU dies, clients detect missing media within seconds, call signaling again, and are reassigned.

Tradeoffs

Pinning a whole meeting to one SFU is simplest and lowest latency for local meetings, but forces distant participants through a long public internet path. Cascading per region costs extra server bandwidth but moves long-haul traffic onto a private backbone with lower loss.

Relays media for clients that cannot reach the SFU directly.

  • TURN servers listening on UDP 3478 and TCP or TLS 443.

Tradeoffs

TCP relay suffers from head-of-line blocking when packets are lost, so it is used only as a fallback. Most corporate networks allow outbound UDP, and those that do not still allow 443.

Captures, composes, and stores meeting recordings.

  • Headless recording bots that join through the SFU, FFmpeg-based encoding, and object storage.

Scalability

At one 720p composite at 1.5 Mbps, an hour of recording is about 700 MB. Bots are scheduled on a separate pool so recording load never competes with live forwarding. Segments are uploaded every few seconds so a crash loses little.

Tradeoffs

Recording as a participant reuses the SFU's existing forwarding and layout logic. Recording each raw stream separately would allow later re-editing but multiplies storage by the number of participants, so it is reserved for products that need it.

Related questions