frontend / browser internals / 07_webrtc_and_webtransport.md

WebRTC and WebTransport

7 min read source

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 permissionsgetUserMedia requires 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

Further reading