Vibe Engines
YouTube
System Design

Design a Video Conferencing System

Step 1 / 9

Learn system design by building a video conferencing system like Zoom or Google Meet step by step.

The numbers to beatN²mesh connectionsN-1×uploads per person~4-5mesh ceiling

The whole design, in writing

Learn system design by building a video conferencing system like Zoom or Google Meet step by step. An interactive guide covering why real-time media needs UDP/WebRTC not TCP, why a peer-to-peer mesh explodes at N², the SFU that forwards streams and scales calls, simulcast + adaptive bitrate, signaling with SDP and ICE/STUN/TURN NAT traversal, geo-distributed and cascaded SFUs with active-speaker selection, and the unhappy paths (packet loss, congestion, recording).

Every step of the build above, written out: the problem each piece solves, the option that was taken and the ones that were not, the numbers, and how it fails in production.

The big idea

What makes video calling hard?

A dozen faces, live, from around the world, must stay in sync with under ~150ms of delay or conversation falls apart (people talk over each other). Video is a huge, continuous stream; networks drop packets and vary wildly; and every extra participant multiplies the media. This is a real-time systems problem, not a web request.

Participant Asends 1 stream
New in this step: Participant A.

A video conferencing system moves real-time audio/video among many participants with minimal latency, adapting to every network. The key choices: the right transport (UDP/WebRTC, not TCP), the right topology (a media server SFU, not a peer mesh), adapt quality per receiver (simulcast), and connect peers through NATs (signaling + ICE/STUN/TURN).

What the new pieces do

Participant Aclient
A person in the call. Captures their camera/mic and sends one uplink stream; receives a downlink stream per other participant.

Step 1 · Not a web request

Real-time media needs UDP

Your instinct might be HTTP/TCP — reliable, familiar. For live video it's exactly wrong. Why does the protocol that reliably delivers web pages ruin a video call?

Participant Asends 1 stream
The system as it stands at this step.

Why is TCP the wrong transport for live video/audio?

  1. Setup time matters a little, but the real killer is TCP's reliability model: it retransmits and delivers in order, so one lost packet stalls everything behind it (head-of-line blocking) — fatal for real-time media where a late frame is useless.

  2. For live media a packet that arrives late is worthless — you want the *next* frame, not a resend of the old one. TCP's retransmit-and-wait stalls the stream; UDP lets you skip lost/late packets and keep going. WebRTC uses UDP (RTP) with its own loss handling.

  3. TCP can carry any bytes; the issue isn't capability but semantics. Its reliable, ordered delivery is wrong for media, where timeliness beats completeness — hence UDP.

Live media values timeliness over reliability: a video frame that arrives late is useless — you want the next one. TCP's reliable, in-order delivery causes head-of-line blocking (one lost packet stalls everything behind it). So real-time media uses UDP (via RTP), tolerating loss and skipping late packets. This is what WebRTC — the browser real-time media stack — is built on.

Why this piece earns its place

Choosing UDP also means taking on everything TCP was doing for us for free — loss recovery, ordering, pacing — as our own problem, and every later step on this page is part of that bill. But the assumption worth naming now is smaller and harder: it assumes the network will pass UDP at all. Plenty of corporate, hotel and captive-portal networks block UDP outright, or allow nothing outbound except TCP on port 443, and for a user on one of those no amount of loss tolerance helps — there is no path to be timely on. So a serious client keeps a TCP-framed fallback through a relay and treats it as an explicitly degraded mode rather than an equal one, because the moment media is carried inside a reliable, ordered stream the head-of-line stall we picked UDP to escape comes straight back: a single retransmit holds up everything behind it, and it surfaces as audio drifting a beat behind the speaker’s lips. Worth knowing which path a session ended up on, too — a call that is quietly wedged on that fallback is a support ticket you can answer in one lookup instead of guessing.

Step 2 · Two people is easy

Peer-to-peer for a 1:1 call

For a simple two-person call, do you even need a server carrying the media? The lowest-latency path between two people is a straight line.

Participant Asends 1 stream
The system as it stands at this step.

For 1:1, connect the two peers directly (peer-to-peer): each sends media straight to the other. It's the lowest latency (no server hop) and cheapest (no media server cost). This works beautifully for two people — the question is what happens when the call grows.

Why this piece earns its place

The cheap answer is right here and still loses more often than it looks. A direct connection delivers each peer’s packets to the other’s actual address, which means the two people learn each other’s IP address — fine between colleagues, a reportable privacy problem in any product that connects strangers, and reason enough that plenty of shipped products route even a two-person call through the media server. Going direct also leaves you with no vantage point on the call: no server ever touches the media, so recording, moderation, compliance capture and per-stream quality telemetry have nothing to attach to, and the only view of what happened is whatever the clients choose to report. And the two-party assumption expires the first time someone invites a third person, at which point you are changing topology during a live call — a renegotiation path that is rarely exercised and therefore rarely works. So the honest version of this step is that peer-to-peer is optimal for 1:1 on latency and cost alone, and that latency and cost are not the only things a product is buying.

Step 3 · Why the mesh explodes

N² connections

Extend peer-to-peer to a group: everyone connects directly to everyone else (a mesh). For 4 people that's fine. Why does it collapse at, say, 10 or 20?

Participant Asends 1 streamParticipant Breceives many
New in this step: Participant B.

In a full P2P mesh of N people, what blows up?

  1. Signaling grows too, but the crippling cost is media: each participant must *upload* their camera stream separately to every other participant, so uplink bandwidth and encoding scale with N — brutal on the sender.

  2. A mesh has no media server — that's the point. The explosion is on the *clients*: everyone sends N-1 copies of their stream and receives N-1, so upload bandwidth and CPU scale with N per person, and total connections scale as N².

  3. In a mesh every participant encodes and uploads a separate copy of their video to each of the N-1 others, so a sender's uplink and encoding cost grow linearly with the group while total connections grow as N². Home upload bandwidth caps this at ~4-5 people.

A full mesh is O(N²) connections, and worse, each participant must upload their stream N-1 times — once to every other peer. Uplink bandwidth and encoding CPU scale with N per person, and home upload links choke at ~4–5 participants. The mesh has no server cost, but it pushes an impossible burden onto each client. Groups need a different topology.

Why this piece earns its place

That ~4–5 ceiling is not a property of the protocol — it is a property of whoever in the room has the worst upload link, so it is set by the weakest participant and changes every time the membership does. It moves with content too: a group that is comfortable at a low resolution falls apart the moment someone’s camera goes HD, because the sender’s obligation is the stream multiplied by the audience. What makes it genuinely nasty is how it fails. Nothing errors. The sender whose uplink has saturated sees a perfectly healthy call, because their own video is rendered locally and never crosses the network, while everybody else watches them freeze — so the one person who could fix it by turning off their camera is the last to find out, and every complaint about that call gets filed by somebody else, about a picture the sender has no way of seeing. Any product that ships a mesh at all therefore needs a sender-side uplink measurement and a rule that moves the call onto a server before the participants discover the ceiling for themselves.

  • N²mesh connections
  • N-1×uploads per person
  • ~4-5mesh ceiling

What the new pieces do

Participant Bclient
Another participant. Renders everyone else's video and mixes their audio locally.

Step 4 · Upload once

The SFU (Selective Forwarding Unit)

The mesh's fatal flaw is that each person uploads their stream many times. What if each person uploaded once, to a server, and the server distributed it? What should that server do — and crucially, not do?

Participant AParticipant BSFU
New in this step: SFU. · swipe to pan the diagram

To fix the mesh, put a server in the middle. What should it do with the media?

  1. A Selective Forwarding Unit receives each participant's single uplink stream and forwards copies to the other participants — it routes packets, it doesn't decode/re-encode. So each peer uploads once (fixing the N² sender cost) and the server stays cheap (no transcoding). This is how Zoom/Meet scale.

  2. An MCU (mixer) does decode + composite + re-encode a single stream per participant — simple for clients but extremely CPU-heavy on the server and adds latency and inflexibility. SFUs won because forwarding is far cheaper and lets each client control its own layout.

  3. Storing and pulling adds huge latency — fatal for a live call. The server must forward in real time, not buffer for retrieval.

Use an SFU (Selective Forwarding Unit). Each participant uploads one stream to the SFU; the SFU forwards (routes, without decoding/re-encoding) that stream to every other participant. Now each peer uploads once — killing the mesh's N² sender cost — and the SFU stays cheap because it only forwards packets, not transcodes. This is the topology behind modern large calls.

Why this piece earns its place

Forwarding is what makes the SFU cheap, and it is equally the constraint we are accepting: the server never decodes, so it can never improve on what a sender gave it. It cannot shrink a stream for a struggling receiver, it cannot re-encode for an endpoint that speaks a different codec, and it cannot compose a picture — which is why the quality problem has to be handed back to the sender rather than solved on the box, and why anything that genuinely needs pixels has to subscribe like a participant and pay for its own decode. The other half of the cost did not vanish either; it moved. Each peer uploads once now, but the SFU still sends every stream to every other participant, so traffic across the server’s network interface grows with the square of the room while no individual client feels any of it. That makes capacity planning a count of forwarded streams, not of calls, and since many calls share a machine, one very large meeting becomes a noisy neighbour for every unrelated call on it. Admission control — knowing how many streams a box will accept before it accepts them — matters more here than raw bandwidth does.

  • upload 1×per participant
  • forwardno transcode
  • SFUscales the call

What the new pieces do

SFUservice
Selective Forwarding Unit: each peer uploads one stream; the SFU forwards it to every other peer. The scalable hub of the call.

Step 5 · Everyone's network differs

Simulcast & adaptive bitrate

One participant is on gigabit fiber, another on a spotty phone. If the SFU forwards the same high-quality stream to both, the phone user's video freezes and stutters. But the sender uploads only one stream — how can each receiver get a quality that fits their network?

Participant BParticipant CSFU
New in this step: Participant C. · swipe to pan the diagram

The sender uploads one stream, but receivers have wildly different bandwidth. How does each get a fitting quality?

  1. A weak receiver can't cope — its video freezes and audio breaks. You must send each receiver a quality matched to its bandwidth without burdening the sender.

  2. That's the mesh problem again — sender uplink scales with N. The trick is to upload a few quality layers once and let the SFU pick per receiver.

  3. With simulcast (or SVC) the sender encodes and uploads a few resolutions/bitrates simultaneously; the SFU, watching each receiver's reported bandwidth, forwards the highest layer that fits. One upload effort, per-receiver quality — the phone gets low-res, the fiber gets HD, and the SFU adapts in real time.

Use simulcast (or scalable video coding, SVC): the sender uploads a few quality layers at once (e.g. low/medium/high). The SFU, tracking each receiver's available bandwidth (via congestion feedback), forwards the highest layer that fits each receiver — HD to the fiber user, low-res to the phone. And it can drop to audio-only when needed. One upload, adaptive per-receiver quality, all decided at the forwarding server.

Why this piece earns its place

Simulcast is not free for the sender: encoding several versions of one camera costs real CPU and more total uplink than encoding one, and it lands hardest on exactly the device most likely to be constrained — a phone that is already thermally throttling. So the layer count is a knob rather than a constant. Drop to two layers, or to SVC where a single layered stream can be thinned instead of separately re-encoded, whenever the sender is the constrained party rather than the receiver. The second knob is how eagerly the SFU switches. Promoting a receiver to a higher layer requires a keyframe from that layer, and a keyframe is a large frame, so every optimistic upgrade puts a burst on the precise link you were unsure about. Follow the bandwidth estimate too closely and a receiver oscillates between resolutions, which people find more distracting than simply sitting on the lower one. The setting that works is deliberately asymmetric: switch down immediately, because the alternative to a blurry picture is a frozen one, and switch up slowly, only once the headroom has held for a while.

  • 3 layerslow / med / high
  • per receiverbest-fit layer
  • adaptivefollows congestion

What the new pieces do

Participant Cclient
A participant on poor bandwidth — the reason we send multiple quality layers and adapt per receiver.

Step 6 · Just connecting is hard

Signaling & NAT traversal

Before any media flows, two peers on the internet — both behind home routers (NAT) with no public address — have to find a network path to each other and agree on codecs. Neither can just be "dialed." How do they connect at all?

Participant Asends 1 streamParticipant Breceives manySignalingSDP · ICESTUN / TURNNAT traversal
New in this step: Signaling, STUN / TURN.

Use a signaling server to exchange setup info: SDP offer/answer (codecs, parameters) and ICE candidates (possible network paths). To pierce NAT: STUN tells a peer its own public address so peers can try a direct connection; when a direct path is impossible (strict/symmetric NAT, firewalls), TURN relays the media through a server as a fallback. Signaling only sets up the call; once connected, media flows peer↔SFU directly.

Why this piece earns its place

Two details here decide how this feels in production. The first is that ICE is the part users experience as joining. If a client gathers every candidate — local, STUN-discovered, relay — before it sends its offer, the slowest candidate sets the join time for everyone. Trickle ICE sends candidates as they are found and starts checking pairs immediately, so a direct path is usually live before the relay candidate has even been allocated. The same machinery has to run again mid-call: a phone crossing from wifi to cellular changes address, and without an ICE restart the media just stops on a connection that still believes it is healthy. The second is that signaling is a separate availability domain from media, and that is easy to misread. If signaling dies, every call already in progress keeps running — media goes peer to SFU and never touches it — so the traffic graphs stay calm while nobody can join, nobody can renegotiate, and nobody who drops can get back in. That is an outage hiding behind healthy numbers, which is why it needs its own alarm on join success rather than on concurrent streams.

  • SDPcodecs / params
  • STUNdiscover public IP
  • TURNrelay when direct fails

What the new pieces do

Signalinggateway
Sets up connections: exchanges SDP offers/answers and ICE candidates so peers can find a path to each other.
STUN / TURNservice
STUN discovers a peer's public address; TURN relays media when a direct connection is impossible (strict NAT/firewall).

Step 7 · Big and global calls

Geo-distributed & cascaded SFUs

A single SFU has limits — its bandwidth caps how many streams it can forward, and a call with participants on different continents shouldn't route everyone through one distant server. How do you scale to huge, global calls?

SFUgeo · cascadedCascade SFUother region
New in this step: Cascade SFU.

Distribute SFUs geographically so each participant connects to a nearby one (low latency), and cascade them: SFUs peer with each other so a stream crosses between regions once rather than every remote participant pulling it across the ocean. For very large calls (webinars, thousands of viewers), forward only what matters — active-speaker selection and a few top video streams — instead of everyone's stream to everyone. The topology becomes a tree of SFUs, not one hub.

Why this piece earns its place

Cascading buys locality and pays for it in hops. Each additional SFU between a speaker and a listener is another queue inside the ~150ms budget, so the thing to bound is hops per stream rather than servers per region: one hop between any two regions is a design, and a chain of relays that accumulated as regions were added is a slow outage nobody declared. The second thing this step quietly introduces is editorial. Once we stop forwarding everyone’s video, the system is deciding what each person sees, and it fails in a very specific way — a cough, a closing door, a laugh flips the active speaker and the grid churns while the actual conversation is elsewhere. So the switch needs a hold time and a margin over the runner-up rather than a bare loudest-wins comparison, and ideally a pinned position for whoever is presenting. Audio should stay out of that selection entirely: audio streams are small, and a participant muted by the layout because they were off screen cannot interrupt — which is the one affordance a live conversation genuinely cannot do without.

  • geo SFUsconnect to nearest
  • cascadecross regions once
  • active speakerforward what matters

What the new pieces do

Cascade SFUservice
A second SFU (another region / large call) that peers with the first, so big and global calls fan out without one server carrying everyone.

Step 8 · The sharp edges

Loss, congestion, echo & recording

Real networks are hostile: packets drop, bandwidth fluctuates, audio echoes, and you often need to record the call — all while keeping latency low.

SFUgeo · cascadedRecordingtranscode · store
New in this step: Recording.

Survive packet loss with a jitter buffer (smooth timing), NACK (selective retransmit of just-lost packets when time allows), and FEC (forward error correction — send redundancy so small losses self-heal). Run congestion control (estimate available bandwidth and tell simulcast which layer to send) so the call adapts instead of collapsing. Do echo cancellation / noise suppression on the client audio. And record by attaching a special subscriber that mixes and transcodes the streams to storage — off the live path.

Why this piece earns its place

NACK and FEC look like two tools for the same job, but they spend different currencies and the choice between them is mostly about round-trip time. A retransmit costs nothing until something is actually lost, yet it only helps if the replacement lands before the jitter buffer has to play that instant — on a long path it is simply too late to ask. Redundancy costs bandwidth on every packet whether or not anything is lost, and in exchange repairs small gaps with no round trip at all. The split falls out of that: audio is cheap to protect and the most damaging thing to lose, so it can carry redundancy continuously, while video is the expensive stream and leans on retransmission while there is still time, accepting a dropped frame when there isn’t. The trap is that recovery traffic is still traffic. Redundancy and retransmits are counted by the same bandwidth estimate that drives which simulcast layer goes out, so a client that responds to loss by adding more repair pushes the link further into congestion, loses more, and repairs harder. Loss recovery has to sit inside the congestion controller’s budget, not alongside it, or the healing mechanism becomes the thing breaking the call.

What the new pieces do

Recordingdata
Subscribes like a participant to mix/record the call and transcode it for later playback.

You did it

You just designed a video conferencing system.

Participant AParticipant BParticipant CSignalingSTUN / TURNSFUCascade SFURecording
The finished design, end to end. · swipe to pan the diagram

Everything you assembled, in order

  • Real-time media values timeliness over reliability — use UDP/WebRTC, drop late packets, not TCP.
  • A direct peer-to-peer connection is ideal for a 1:1 call (lowest latency, no server).
  • A full mesh is O(N²) and forces each peer to upload their stream N-1 times — it collapses at ~5 people.
  • An SFU lets each peer upload once and forwards streams (no transcoding) — the scalable topology.
  • Simulcast + adaptive bitrate let the SFU send each receiver a quality that fits its network.
  • Signaling (SDP) + ICE/STUN/TURN connect peers across NATs; TURN relays when direct fails.
  • Geo-distributed, cascaded SFUs + active-speaker selection scale to huge, global calls; loss/congestion handled with jitter buffers, NACK, FEC.

Where an interviewer pokes next

Getting the boxes right is the easy half. These are the questions that separate a candidate who drew the diagram from one who has run the thing. Answer each one out loud before you open it.

  1. Why does real-time media use UDP when TCP is more reliable?

    Because the two protocols optimize for opposite things, and live media wants the opposite of what TCP provides. TCP guarantees reliable, in-order delivery, which for a web page is exactly right. But it achieves that by retransmitting lost packets and holding back everything that arrives after a gap until the missing piece is re-sent — head-of-line blocking. For real-time audio and video, a packet that shows up late is worthless: you don't want a re-sent frame from 300ms ago, you want the current frame, now. TCP's wait-and-retransmit therefore stalls the stream and destroys the low latency a conversation needs. UDP is unreliable and unordered, which sounds bad but is precisely what you want here — you can simply skip lost or late packets and keep playing, handling loss selectively (retransmit only if there's still time, or paper over it with error correction). This is why WebRTC is built on UDP/RTP with its own timeliness-aware loss handling rather than on TCP.

  2. Why does a peer-to-peer mesh collapse, and how does an SFU fix it?

    In a full mesh every participant connects directly to every other, which means each person must independently encode and upload a copy of their camera stream to each of the other N-1 participants. So a single sender's upload bandwidth and CPU grow linearly with the group size, and the total number of connections grows as N². The binding constraint is usually the sender's uplink: typical home upload bandwidth can't send four or five simultaneous HD copies, so the mesh caps out at a handful of people. An SFU fixes the sender-side explosion by putting a server in the middle: each participant uploads their stream exactly once, to the SFU, and the SFU forwards copies to everyone else. Crucially, the SFU only forwards (routes packets) rather than decoding and re-encoding, so it stays cheap and low-latency while the per-client upload cost drops to a single stream regardless of how many people are in the call. That's the trade — you take on server bandwidth and make the server a critical hub, in exchange for making the call scale past the mesh's ~5-person ceiling.

  3. SFU vs MCU — what's the difference and why did SFUs win?

    Both are media servers that solve the mesh's upload problem, but they do fundamentally different work. An MCU (Multipoint Control Unit) decodes every incoming stream, composites them into a single mixed video (and mixed audio), and re-encodes one outgoing stream per participant. That makes clients trivially simple — each receives just one stream — but it is extremely CPU-intensive on the server (decode + mix + encode for every participant), adds latency from the transcoding pipeline, and forces one server-chosen layout on everyone. An SFU (Selective Forwarding Unit) instead just forwards each participant's uploaded stream to the others without decoding it, so the server does far less work per participant, adds minimal latency, and lets each client receive separate streams and arrange its own layout and choose its own quality. SFUs won for scale and flexibility: forwarding is dramatically cheaper than mixing, it composes naturally with simulcast (the SFU picks which quality layer to forward per receiver), and clients keep control of layout and per-stream quality. MCUs still appear where endpoints are too dumb to render many streams (some hardware/telephony gateways), but the mainstream architecture is the SFU.

  4. How does simulcast let one uploaded stream serve receivers with very different bandwidth?

    The problem is that within one call receivers have wildly different network conditions — someone on fiber can take full HD, someone on a weak phone connection can't — yet you don't want the sender to upload a custom stream per receiver, which would reintroduce the mesh's N-scaling upload cost. Simulcast resolves this by having the sender encode and upload a small number of quality layers simultaneously (for example a low, medium, and high resolution/bitrate version), which is a fixed amount of work regardless of audience size. The SFU then watches each receiver's reported available bandwidth and congestion signals and, for each one independently, forwards the highest layer that fits — HD to the fiber user, a low layer to the struggling phone, or even audio-only when a connection is very poor. Because the adaptation decision lives at the SFU, which sees every receiver's conditions and can react in real time, one person's bad wifi only lowers the layer they receive rather than degrading the stream for everyone. Scalable Video Coding (SVC) is a more efficient variant where a single layered stream can be selectively thinned, but the principle is the same: encode a few options once, choose per-receiver at the forwarding server.

  5. Why is just connecting two peers so hard, and what do STUN and TURN do?

    Most devices don't have a public internet address — they sit behind NAT (a home or corporate router) that maps many private devices onto one public IP, so a peer can't simply be "dialed" at a known address, and two peers behind different NATs have no obvious path to each other. WebRTC solves this in two parts. First, signaling: a signaling server (over any channel you like) relays the SDP offer/answer, which negotiates codecs and parameters, plus ICE candidates, which are the possible network addresses each peer might be reachable at — the signaling server carries no media, it just brokers the handshake. Second, NAT traversal via ICE, which tries candidate paths in order of preference. STUN helps a peer discover its own public IP:port as seen from outside its NAT, so the two peers can often establish a direct connection by hole-punching through their NATs. But some NATs and firewalls (notably symmetric NATs, or networks that block UDP) make a direct connection impossible; for those, TURN provides a relay server that both peers can reach and that forwards their media for them. TURN always works but costs the most (all media flows through the relay), so ICE uses it only as a last resort after direct and STUN-assisted attempts fail — which is why losing TURN silently breaks connectivity specifically for participants on the most restrictive networks.

Check yourself — the answers, and why

Nine steps in, these are the calls you should be able to make cold. Pick one, then read why.

  1. Real-time video uses UDP/WebRTC rather than TCP because…

    Media is perishable; drop late packets and keep going instead of stalling to re-send.

  2. A full peer-to-peer mesh collapses because…

    The sender-side upload cost is the killer; home uplinks cap the mesh at ~5 people.

  3. An SFU scales calls by…

    Forward, don't mix: cheap server, one upload per peer, clients control their own layout.

  4. Simulcast lets receivers with different bandwidth each get a fitting quality by…

    One upload effort (a few layers); the SFU adapts which layer each receiver gets to congestion.

  5. TURN is needed because…

    STUN enables direct paths; TURN is the always-works relay fallback when direct is impossible.

How you’d open this design in an interview

Before any boxes: agree what it must do, pin the qualities that shape everything, then build — naming each trade-off as you make it. The walkthrough above is that exact order.

What it must do

Agree on these before drawing a single box.

  • Live media: move audio/video among many participants with under ~150ms of delay.
  • Group call: each participant sends one uplink stream and receives a downlink per other participant.
  • Adapt quality: give each receiver a video quality that fits their network.
  • Connect anywhere: set up peers across NATs and firewalls via signaling and ICE/STUN/TURN.
  • Scale & record: support big, global calls and capture the session for playback.

The qualities that shape everything

Each one names the mechanism that buys it.

Low-latency media that tolerates loss
UDP via WebRTC/RTP drops late packets and keeps going, instead of TCP’s retransmit-and-wait head-of-line stall — a late frame is useless anyway.
Scale past the mesh’s ~5-person ceiling
An SFU lets each peer upload one stream and forwards it (without decoding) to everyone else, killing the mesh’s N-times-upload cost.
One bad network doesn’t degrade everyone
The sender uploads a few simulcast layers; the SFU forwards the highest layer each receiver’s bandwidth can take, adapting to congestion.
Connect peers with no public address
Signaling exchanges SDP + ICE candidates; STUN discovers public addresses for a direct path, and TURN relays media when strict NAT/firewalls block it.
Huge, global calls without one overloaded hub
Geo-distributed SFUs each serve nearby peers and cascade — a stream crosses regions once — plus active-speaker selection instead of every stream to everyone.
Stay watchable on hostile networks
A jitter buffer smooths timing, NACK selectively retransmits, FEC self-heals small losses, and congestion control drives which simulcast layer to send.

The trade-offs you say out loud

Senior signal isn’t the boxes — it’s naming what you gave up and why it was the right price.

UDP / WebRTC (RTP) over TCP

TCP’s reliable, in-order delivery causes head-of-line blocking — one lost packet stalls everything behind it — which is fatal for media where a frame that arrives late is worthless. UDP lets you skip lost/late packets and keep playing the next frame.

An SFU over a full peer-to-peer mesh

A mesh forces each participant to upload their stream to all N-1 others, so uplink and encoding scale with N per person and home links choke at ~4–5 people. An SFU lets each peer upload once and forwards the rest — the sender-side cost is the real killer a server fixes.

Simulcast layers over a separate stream per receiver

Uploading a custom quality per receiver reintroduces the mesh’s N-scaling upload cost. The sender encodes a few layers once, and the SFU — which sees every receiver’s conditions — forwards the highest layer that fits each one.

ICE: direct → STUN → TURN in order over relaying all media through TURN

TURN relays every byte through a server, so it always works but costs the most. ICE tries a direct connection and STUN-assisted hole-punching first, falling back to a TURN relay only when strict/symmetric NAT or firewalls make a direct path impossible.

The answer, out loud

What a strong answer to “Design a Video Conferencing System” sounds like, first question to last trade-off. It is about 7 minutes of talking; the whiteboard and the interviewer fill the rest of the 45. Read it aloud once, then close the page and give it yourself.

  1. 0–3 min

    What we’re actually building

    Let me scope it before I draw anything. We’re moving live audio and video between a group of people, and the number governing every decision is that delay has to stay under about 150 milliseconds — past that people talk over each other and it stops being a conversation. So this isn’t a request-response system with a latency budget bolted on, it’s a continuous stream that is worthless if it’s late. I’ll decide four things in order: the transport, the topology, how quality adapts per person, and how two machines behind home routers find each other at all.

  2. 3–8 min

    Not a web request

    Transport first, because it’s the one people coming from web work get wrong. The instinct is TCP, and TCP is exactly the wrong shape. It guarantees reliable, in-order delivery, so one lost packet stalls everything behind it until the resend arrives. For a page that’s correct. For a call it’s fatal — I don’t want the old frame resent, I want the current one now. So it’s UDP, via RTP, which is what WebRTC is built on: skip what’s lost and keep playing. Timeliness over reliability.

    Built in step 1: Real-time media needs UDP
  3. 8–14 min

    Where the simple version dies

    Topology next. For two people I’d connect them directly, peer to peer — lowest latency, no server in the media path. The interesting question is what happens when it grows. The obvious extension is a full mesh, everyone connected to everyone, and it collapses faster than people expect. Connections go as N squared, but that isn’t the killer. The killer is that each person encodes and uploads a separate copy of their camera to every other participant, so one sender’s upload and CPU grow with the group. Home upload links run out at about four or five people. The mesh has no server cost, it just moves an impossible cost onto every client.

    Built in step 3: N² connections
  4. 14–21 min

    A server that forwards, not mixes

    So each person should upload once, which means a server in the middle — and the design question is what it does with the media. If it decodes everything, composites one picture and re-encodes a stream per participant, that’s an MCU: clients get simple, but the server transcodes for every person, adds latency, and picks everyone’s layout. I’d use an SFU instead. Each participant uploads one stream and the server forwards copies to the others without decoding them. It routes packets. Cheap server, low latency, and every client lays out and prioritises whatever it wants.

    Built in step 4: The SFU (Selective Forwarding Unit)
  5. 21–28 min

    One weak network shouldn’t cost everyone

    The participants don’t share a network. One’s on fibre, one’s on a struggling phone, and if the SFU forwards the same high-quality stream to both, the phone freezes. I can’t ask the sender for a custom stream per receiver — that’s the mesh cost again. So, simulcast: the sender encodes a few layers at once, low, medium and high, which is fixed work no matter how many people watch. The SFU sees each receiver’s congestion feedback and forwards the best layer they can take — HD to the fibre user, the low layer to the phone, audio only if it gets bad enough.

    Built in step 5: Simulcast & adaptive bitrate
  6. 28–34 min

    Getting connected at all

    None of that matters if the ends can’t find each other, and most devices sit behind NAT with no address anyone can dial. So there’s a signaling server carrying no media at all — it relays the SDP offer and answer, which negotiates codecs and parameters, and the ICE candidates, which are the addresses each side might be reachable on. ICE then works through those in order of preference. STUN tells a peer what its public address looks like from outside, which is usually enough to punch a direct path through. When the NAT is symmetric or the firewall blocks it, TURN relays the media. It always works and costs the most, so it’s the last resort.

    Built in step 6: Signaling & NAT traversal
  7. 34–40 min

    Big calls, and global ones

    One SFU has a ceiling, and routing someone in Singapore through a server in Virginia spends latency I don’t have. So I’d run SFUs in several regions, connect each participant to a nearby one, and cascade them — peer the servers so a stream crosses between regions once, rather than every remote participant dragging their own copy across the ocean. The topology becomes a tree, not a hub. And at real scale I’d stop forwarding everything: send the active speaker and a handful of others, because nobody watches forty faces anyway.

    Built in step 7: Geo-distributed & cascaded SFUs
  8. 40–43 min

    The network is hostile

    Then the unhappy path, which is most of real operation. Packets arrive late and out of order, so a jitter buffer smooths playout. Loss gets handled two ways: NACK to ask for a specific packet back when there’s still time, and forward error correction so small gaps heal themselves. Bandwidth moves constantly, so congestion control estimates what’s available, and that estimate is what tells simulcast which layer to send. Echo and noise are cleaned up on the client. Recording attaches as another subscriber that mixes and transcodes to storage, off the live path so it can never slow the call.

    Built in step 8: Loss, congestion, echo & recording
  9. 43–45 min

    What I’m trading

    To close: UDP because late is useless, an SFU because the mesh puts the cost on whoever can least pay it, simulcast because the SFU can only forward what it was given, ICE with a relay fallback because some networks won’t connect otherwise, and cascading once one server isn’t enough. The trade running through all of it is that I bought scale by centralising, so the SFU is a hub whose failure takes the call with it — which is why I’d run redundant ones and reconnect clients. With more time I’d work through end-to-end encryption, since a forwarding server doesn’t need to read what it routes, and the reconnection path, which is what people experience as reliability.

What this teaches

Learn system design by building a video conferencing system like Zoom or Google Meet step by step. An interactive guide covering why real-time media needs UDP/WebRTC not TCP, why a peer-to-peer mesh explodes at N², the SFU that forwards streams and scales calls, simulcast + adaptive bitrate, signaling with SDP and ICE/STUN/TURN NAT traversal, geo-distributed and cascaded SFUs with active-speaker selection, and the unhappy paths (packet loss, congestion, recording).

Key takeaways

  • Real-time media values timeliness over reliability — use UDP/WebRTC, drop late packets, not TCP.
  • A direct peer-to-peer connection is ideal for a 1:1 call (lowest latency, no server).
  • A full mesh is O(N²) and forces each peer to upload their stream N-1 times — it collapses at ~5 people.
  • An SFU lets each peer upload once and forwards streams (no transcoding) — the scalable topology.
  • Simulcast + adaptive bitrate let the SFU send each receiver a quality that fits its network.
  • Signaling (SDP) + ICE/STUN/TURN connect peers across NATs; TURN relays when direct fails.
  • Geo-distributed, cascaded SFUs + active-speaker selection scale to huge, global calls; loss/congestion handled with jitter buffers, NACK, FEC.

Concepts covered

  • What makes video calling hard?
  • Real-time media needs UDP
  • Peer-to-peer for a 1:1 call
  • N² connections
  • The SFU (Selective Forwarding Unit)
  • Simulcast & adaptive bitrate
  • Signaling & NAT traversal
  • Geo-distributed & cascaded SFUs
  • Loss, congestion, echo & recording
built to move a face across the planet in 150 milliseconds — make the calls, kill the SFU, run the gauntlet.
Finished this one? 0 / 65 System Designs done

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More System Designs