WebRTC and WebTransport
TL;DR
Two transport APIs beyond WebSocket. WebRTC is peer-to-peer real-time — video/audio/data between browsers without a server in the middle (after signaling). The senior knowledge: STUN/TURN/ICE for NAT traversal, signaling is your problem (not standardized), RTCPeerConnection for streams, RTCDataChannel for arbitrary data. WebTransport (newer) is a server-client low-latency transport over HTTP/3 — alternative to WebSocket with multiple streams, datagrams, no head-of-line blocking. Mostly senior trivia / for system design rounds; rarely used directly in app code.
WebRTC Q&A
Q: What’s WebRTC for?
A: Peer-to-peer, low-latency audio/video/data in the browser. Use cases:
- Video calls (Zoom-like, Jitsi, Google Meet uses WebRTC).
- Voice chat / Discord-style.
- Screen sharing.
- File transfer (P2P, no server bandwidth).
- Multiplayer games (data channel for state).
- Collab features (Figma’s cursor presence uses WebRTC over data channel in some setups).
The killer feature: direct browser-to-browser, not relayed through your server (most of the time). Lower latency, no server bandwidth cost for media.
Q: The signaling problem.
A: WebRTC needs the peers to exchange connection info (ICE candidates, SDP offers/answers) before they can connect directly. This exchange is not standardized — you provide a signaling channel (WebSocket, HTTP, anything).
Peer A ───(SDP offer)──→ your signaling server ───(SDP offer)──→ Peer B
Peer A ←──(SDP answer)── your signaling server ←──(SDP answer)── Peer B
Peer A ──(ICE candidates)→→→... ←←← Peer B
After signaling completes, peers connect directly. Signaling is “out of band” — it’s your code, typically a WebSocket server that relays messages between peers.
Q: ICE, STUN, TURN — what each does.
A: NAT traversal infrastructure:
| What | |
|---|---|
| ICE (Interactive Connectivity Establishment) | the framework — gather candidate addresses, try them in priority order until one works |
| STUN | “what’s my public IP and port?” — lightweight server tells the peer its NAT-translated address. Free; many public servers (e.g., Google’s). |
| TURN | full relay through a server when peer-to-peer fails (~10-20% of cases — symmetric NAT, restrictive firewalls). Expensive to host (uses your bandwidth). |
A real app config:
const pc = new RTCPeerConnection({
iceServers: [
{ urls: "stun:stun.l.google.com:19302" },
{ urls: "turn:your.turn.server:3478", username: "x", credential: "y" },
],
});
Without TURN: ~80% of calls connect P2P, ~20% fail. With TURN: ~100% connect (some through relay). TURN bandwidth costs are why hosted services charge per minute.
Q: RTCPeerConnection setup flow.
A:
const pc = new RTCPeerConnection({ iceServers: [...] });
// Local side: get media, add tracks
const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true });
stream.getTracks().forEach(track => pc.addTrack(track, stream));
// Remote side: handle incoming streams
pc.ontrack = (e) => {
remoteVideoEl.srcObject = e.streams[0];
};
// ICE candidates as they're discovered
pc.onicecandidate = (e) => {
if (e.candidate) signaling.send({ type: "ice", candidate: e.candidate });
};
// Caller: create offer
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
signaling.send({ type: "offer", sdp: offer });
// Callee: receive offer, create answer
async function onOffer(offerSdp) {
await pc.setRemoteDescription(offerSdp);
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
signaling.send({ type: "answer", sdp: answer });
}
// Both sides: handle incoming ICE candidates
async function onIce(candidate) {
await pc.addIceCandidate(candidate);
}
Verbose. Libraries like simple-peer, PeerJS, or full SDKs (LiveKit, Agora, Twilio) hide most of this.
Q: RTCDataChannel — arbitrary data without media.
A:
const channel = pc.createDataChannel("chat", { ordered: true });
channel.onopen = () => channel.send("hello");
channel.onmessage = (e) => console.log("got", e.data);
// Receiver
pc.ondatachannel = (e) => {
const channel = e.channel;
channel.onmessage = (e) => console.log("received", e.data);
};
Options:
ordered: true(default) — packets arrive in order. Like TCP.ordered: false, maxRetransmits: 0— fire and forget. Like UDP. For game state.maxRetransmits/maxPacketLifeTime— bounded reliability.
Use for: real-time game state, collaborative cursors (Figma’s presence), file transfer (P2P), arbitrary low-latency messages.
Q: Selective Forwarding Unit (SFU) — when peer-to-peer breaks down.
A: Pure mesh WebRTC doesn’t scale — 10 peers in a call means each uploads 9 streams (10×9 connections). At ~500 KB/s per stream, that’s 4.5 MB/s upload per peer — too much.
SFU is a server that receives one upload from each peer and forwards to others. Each peer uploads once (to the SFU), receives N streams. Bandwidth is server-side, not P2P.
MCU (Multipoint Control Unit) — older approach; server mixes streams into one and re-encodes. Heavier CPU, easier client.
Real video-conference apps (Zoom, Meet) use SFUs/MCUs, not pure P2P, beyond ~3-4 participants.
WebRTC libraries serving this: mediasoup, Janus, LiveKit, Jitsi Videobridge.
Q: Security — is WebRTC encrypted?
A: Yes. WebRTC media and data is always DTLS-SRTP encrypted (DTLS for the data, SRTP for media). No opt-out. Even peer-to-peer with no server, the data is encrypted between peers.
Caveat: end-to-end encrypted means peer-to-peer, but a SFU is on the path — it sees the encrypted streams but in some products terminates and re-encrypts (so “E2EE” needs Insertable Streams or similar to be true end-to-end).
Q: When use WebRTC vs WebSocket?
A:
| Use WebRTC | Use WebSocket |
|---|---|
| audio/video streams | text messaging |
| low-latency game data | server pushes (notifications) |
| direct peer-to-peer (file transfer, cursor sync) | server is the source of truth |
| huge throughput (avoiding server bandwidth) | request/response-like |
For server-mediated chat / collab where the server is canonical, WebSocket is simpler. For peer-to-peer / media / extreme latency / massive throughput, WebRTC.
WebTransport Q&A
Q: What’s WebTransport?
A: A newer transport API (Chrome 97+) for bidirectional client-server communication over HTTP/3. Designed as a more capable WebSocket replacement.
const transport = new WebTransport("https://example.com:4433/path");
await transport.ready;
// Reliable, ordered, bidirectional stream
const stream = await transport.createBidirectionalStream();
const writer = stream.writable.getWriter();
const reader = stream.readable.getReader();
await writer.write(encoder.encode("hello"));
const { value, done } = await reader.read();
// Unreliable datagrams (UDP-like)
const dgramWriter = transport.datagrams.writable.getWriter();
await dgramWriter.write(encoder.encode("packet"));
for await (const dgram of transport.datagrams.readable) {
// received datagram
}
Advantages over WebSocket:
- Multiple streams — no head-of-line blocking between streams.
- Datagrams — UDP-like, no retransmission, lowest latency.
- HTTP/3 / QUIC — multiplexed, encrypted, 0-RTT, connection migration.
- Backpressure built-in via streams API.
Disadvantages:
- Newer, less broadly supported — Chrome + Edge + Firefox (rolling out); not Safari (as of 2024-2025).
- Server support is also rolling out — needs a QUIC-capable server (some Node frameworks, Cloudflare Workers, custom).
- More complex API than WebSocket.
Q: When use WebTransport vs WebSocket?
A:
Use WebTransport when:
- You need multiple independent streams (no HOL blocking between them).
- You need unreliable datagrams (game state, voice/video metadata).
- You’re already on HTTP/3 infrastructure.
Use WebSocket when:
- Broad browser support matters (everywhere).
- Single ordered stream is enough.
- Familiar tooling / debugging.
For 2026 most apps: WebSocket is still the right default. WebTransport for niche performance-critical use cases (games, real-time audio metadata, edge networking).
Q: WebTransport vs WebRTC data channels?
A:
| WebTransport | WebRTC DataChannel | |
|---|---|---|
| Topology | server-client | peer-to-peer |
| Transport | HTTP/3 (QUIC) | DTLS/SCTP over UDP |
| Setup | URL → connect | signaling → ICE → DTLS handshake |
| Use | server-mediated streams | P2P data |
DataChannel = peer-to-peer; WebTransport = server-mediated. Different shapes; pick by topology.
Gotchas / edge cases
- WebRTC signaling is your responsibility — not built into the API. Plan it (WebSocket, HTTP polling, anything).
- TURN bandwidth is expensive — hosted TURN providers (Twilio, coturn) cost per GB. Account for it in pricing models.
- WebRTC + corporate networks — many block UDP. TURN fallback over TCP/443 is essential.
- Browser permissions —
getUserMediarequires HTTPS + user gesture; permission must be granted explicitly. - WebRTC API churn — older Promise vs callback variants; verify against modern docs.
- WebTransport URL must be HTTPS + QUIC-capable server — fails silently if the server doesn’t support HTTP/3.
- Datagrams aren’t reliable — packets can be lost or reordered. Implement application-level reliability if needed.
- Streams are not FIFO across multiple streams — each stream is ordered; streams relative to each other are not.
What a senior is expected to say
- “WebRTC is peer-to-peer real-time — audio/video/data between browsers, encrypted by default. Signaling (how peers find each other) is your problem; ICE/STUN/TURN handle NAT traversal.”
- “TURN is the relay fallback when P2P fails — ~10-20% of cases. Bandwidth-expensive; budget for it.”
- “Pure mesh WebRTC doesn’t scale past 3-4 peers — use an SFU (Selective Forwarding Unit) for video conferences.”
- “Use libraries (simple-peer, LiveKit, Agora) rather than hand-rolling — the raw API is verbose and full of edge cases.”
- “WebTransport is the newer HTTP/3-based alternative to WebSocket — multiple streams, datagrams, no HOL blocking. Not yet broadly supported; WebSocket is still the default.”
- “RTCDataChannel for arbitrary P2P data (game state, cursors, file transfer); WebTransport for server-mediated streams when WebSocket’s single ordered stream isn’t enough.”
Cross-references
- WebSocket (the simpler default): ../11_apis_data_fetching/07_websockets.md
- Server-side chat & presence (often signaling is a WebSocket): ../../system_design/07_worked_designs/05_chat_presence.md
- HTTP/3 + QUIC (what WebTransport runs on): ../../backend/12_protocols/http/02_http_versions.md
Further reading
- MDN — WebRTC API: https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API
- MDN — WebTransport: https://developer.mozilla.org/en-US/docs/Web/API/WebTransport_API
- WebRTC samples (canonical reference): https://webrtc.github.io/samples/
- LiveKit (modern WebRTC SFU): https://livekit.io/
- W3C WebTransport spec: https://www.w3.org/TR/webtransport/