On this pageOPENCLOSE
Real-time is a spectrum
Polling, long polling, SSE, WebSocket, and WebRTC: what each one is actually good at, and why the wire matters.
Goal
- 01Tell the difference between polling, long polling, SSE, WebSocket, and WebRTC — and when each one is the wrong tool.
- 02Understand why WebSocket and WebRTC solve different layers of the real-time problem.
- 03Recognize the latency, cost, and complexity trade-offs that decide an architecture before you write code.
The concept, plainly
Real-time is not one technology. It is a set of trade-offs between **how often data changes**, **which direction it flows**, **how many people receive it**, and **how much infrastructure you can afford**. HTTP still powers most of the web; real-time transports extend it where request/response is a bad fit.
**HTTP polling** asks for updates on a timer. It is easy to cache, debug, and scale, but it is inherently late by up to one polling interval and mostly returns `304` or empty responses. holds a request open until an update arrives, which lowers latency but still consumes a request per event and breaks under many idle clients.
opens a long-lived HTTP response and streams text events from server to browser. It reconnects automatically, works through most proxies, and is excellent for notifications, logs, and dashboards. It is one-way by design, so commands still need normal HTTP requests. upgrades one HTTP connection into a full-duplex message channel; both sides can push at any time with low framing overhead. It is the standard choice for chat, presence, multiplayer state, and .
goes further. It sends encrypted audio, video, and arbitrary data directly between peers, or through a media server, using , , and . It has the lowest latency for media and can avoid a central media server entirely. The catch: browsers do not define how peers exchange the connection details. You must build or buy and provide / infrastructure.
A common production system uses several transports at once: HTTP for auth and uploads, WebSocket for room state and signaling, WebRTC for media, and SSE for server-only notifications. Choosing a transport per feature beats forcing one protocol to do everything. The important question is not “what is most real-time?” but “how late can this update be, and who needs to receive it?”
See it move
Interactive diagram
One browser, one server, five transports
Short polling
The browser sends GET /updates every few seconds. The server answers immediately, usually with nothing. Simple and cacheable, but late and wasteful.
Hover a node for its role. Each step isolates one transport. Signal orange = media or the primary packet; blue = data/control.
Behind the scenes
What the browser actually does
fetch and EventSource reuse the HTTP connection pool. A WebSocket starts as an HTTP/1.1 GET with Upgrade headers, then the browser hands the socket to its WebSocket implementation; in HTTP/2, extended CONNECT can carry WebSockets. WebRTC does not use fetch at all: it asks the OS for camera/mic access, gathers ICE candidates from network interfaces, runs DTLS, and sends RTP/RTCP over UDP (or TCP through TURN). Each API has a different lifecycle and failure mode.
What the server and network see
Short polling looks like a steady stream of requests and is friendly to CDNs and load balancers. Long polling and SSE occupy connections, which matters when proxies, mobile networks, or server limits cap them. WebSocket connections are stateful, so a normal round-robin load balancer can strand reconnects on another instance; you need sticky sessions or shared room state. WebRTC media bypasses your HTTP stack entirely, so your server cost shifts to signaling, TURN relay capacity, and media servers.
A decision rule that holds up
If updates are periodic and can be minutes late, use caching plus polling. If one server pushes events to many browsers, use SSE. If both sides exchange small messages often, use WebSocket. If you need sub-second audio/video or direct data with privacy and bandwidth advantages, use WebRTC and budget for signaling, STUN, and TURN. Never use WebRTC just to send a typing indicator, and never use polling to carry a live video call.
Code walkthrough
A correct baseline: abort in-flight work on cleanup, back off when the server is unhealthy, and do not hammer a hidden tab.
async function pollUpdates(signal: AbortSignal) {let cursor = localStorage.getItem("cursor") ?? "0";let delay = 2000;while (!signal.aborted) {try {const response = await fetch(`/api/updates?after=${cursor}`, {signal,headers: { accept: "application/json" },});if (!response.ok) throw new Error(`HTTP ${response.status}`);const data = await response.json();cursor = data.cursor;localStorage.setItem("cursor", cursor);applyUpdates(data.events);delay = 2000;} catch (error) {if (signal.aborted) return;delay = Math.min(delay * 2, 30_000);}await new Promise<void>((resolve) => setTimeout(resolve, delay));}}const controller = new AbortController();pollUpdates(controller.signal);// Later: controller.abort();
Do and don't
Do
Choose by latency and direction
Write down the acceptable delay and who sends data. That usually eliminates three options immediately.
Keep one responsibility per transport
Use HTTP for commands, SSE for feeds, WebSocket for signaling and presence, and WebRTC for media. Mixed responsibilities become invisible coupling.
Budget for cleanup
Long-lived connections must be closed on navigation, logout, tab hide, and error paths. Track every socket and stream in one place.
Don't
Don't poll every 500 ms “to be safe”
Idle clients can generate more traffic than active ones. Use the slowest interval the product tolerates, then push when you truly need immediacy.
Don't call WebRTC a signaling protocol
WebRTC deliberately does not define signaling. Without a server exchange, two browsers cannot start a session, even on the same network.
Don't use WebSocket for large file uploads
HTTP has resume, caching, CDNs, and mature tooling. Send a signed upload URL over WebSocket, then use HTTP for the bytes.
Practice
Hands-on lab
Feel the latency difference
Open the workbench, connect, and inspect how quickly a frame round-trips compared with a poll interval. Then close the socket and observe the reconnect path.
- 1Connect to the signaling server and send a JSON message. Note the round-trip time.
- 2Enable heartbeat and watch the latency samples update without any application request.
- 3Close the connection with code 1000, then reconnect. Compare deliberate close with a lost connection.
- 4Only one tab? Everything in this lab works solo — the server echoes your frames.
Solo? The workbench is single-user by design. For peer-to-peer media, use the loopback mode in the 1:1 lab.
Quick quiz
Knowledge check
0 of 4 answered
- 01
A dashboard needs server-to-browser notifications every few seconds and never sends messages from the browser. Which transport is the simplest fit?
- 02
Why does WebRTC need signaling if it can connect peers directly?
- 03
What is the main cost of long polling compared with WebSocket?
- 04
Which statement is true about real-time architecture?
Key takeaways
- Pick transports by direction, latency tolerance, audience size, and operational cost — not by novelty.01
- WebSocket gives you a persistent bidirectional message pipe; WebRTC gives you encrypted media and data paths with a much larger infrastructure story.02
- SSE is underused: it is excellent for one-way feeds and reconnects without custom logic.03
- Every long-lived connection needs an owner, a heartbeat, a cleanup path, and a reconnection policy.04
Common errors and fixes
Terms in this lesson
Done with this lesson?
Mark it complete, then keep the thread.