W3docs

WebRTC

WebRTC (Web Real-Time Communication) enables real-time communication directly between web browsers without plugins or server routing.

WebRTC: Real-Time Communication in the Browser

WebRTC (Web Real-Time Communication) is an open standard and set of browser APIs that let two browsers exchange audio, video, and arbitrary data directly with each other — peer-to-peer — without routing the media through a server and without any plugin. It powers video conferencing, voice calls, screen sharing, live game state, and low-latency file transfer.

This chapter explains what WebRTC gives you, the three APIs it is built from, the signaling and NAT-traversal concepts that confuse most beginners, and a complete working data-channel example you can run in this page.

What WebRTC actually does

The browser already knows how to talk to a server (see Fetch API and WebSockets). WebRTC adds the missing piece: a way for two clients to talk to each other. Once a peer connection is open, media and data travel along the shortest possible path — often directly between the two machines — which is why WebRTC latency is so much lower than relaying everything through your backend.

Typical uses:

  • Audio and video calling — real-time voice/video, like Zoom or Google Meet.
  • Screen sharing — capture a screen or window with getDisplayMedia() and stream it to a peer.
  • Real-time data — game state, chat, cursor positions, or file chunks over a RTCDataChannel.

Why use WebRTC

  • Peer-to-peer, low latency. Media flows directly between peers, not through your server, so round-trips are short and your bandwidth bill stays small.
  • Plugin-free. Built into every modern browser — no Flash, no native app, no download.
  • Encrypted by default. Media uses SRTP and data channels use DTLS; encryption is mandatory and cannot be turned off.
  • Cross-platform. Works on desktop and mobile browsers and from native apps via the same standard.
  • Open standard. Maintained by the W3C and IETF, so it keeps improving without vendor lock-in.

The three core APIs

WebRTC is really three APIs working together:

  1. MediaStream API (getUserMedia) — grabs audio/video from the microphone, camera, or screen as a MediaStream.
  2. RTCPeerConnection — the heart of WebRTC. It negotiates, encrypts, and maintains the connection between two peers and carries the media tracks.
  3. RTCDataChannel — a bidirectional channel for sending arbitrary data (strings or binary) peer-to-peer, similar to WebSockets but without a server in the middle.

Signaling: the part WebRTC does not do

Two browsers cannot find each other out of thin air. Before a peer connection can open, the peers must exchange two kinds of information:

  • SDP (Session Description Protocol) — an "offer" and "answer" describing codecs, media formats, and connection parameters.
  • ICE candidates — possible network addresses (IP/port pairs) at which each peer can be reached.

WebRTC does not define how this hand-off happens — that is your job, and it is called signaling. You build a signaling channel with whatever server transport you like; WebSockets is the most common choice. The flow is always:

  1. Caller creates an offer and sends it through the signaling server.
  2. Callee receives the offer, creates an answer, and sends it back.
  3. Both peers trade ICE candidates as they are discovered.
  4. The direct peer-to-peer connection is established; the signaling server is no longer needed.

STUN and TURN: getting through firewalls

Most devices sit behind NAT (Network Address Translation) and firewalls, so a peer rarely knows its own public address. Two server types solve this:

  • STUN server — tells a peer its public IP/port so peers can attempt a direct connection. Cheap and stateless; Google runs free public ones.
  • TURN server — relays the media when a direct connection is impossible (strict corporate firewalls). It costs bandwidth, so it is a fallback, not the default.

You list these servers in the iceServers config passed to RTCPeerConnection:

const configuration = {
  iceServers: [
    { urls: 'stun:stun.l.google.com:19302' },
    // A TURN server is added for hard-to-reach networks:
    // { urls: 'turn:turn.example.com', username: 'user', credential: 'pass' }
  ],
};

const peerConnection = new RTCPeerConnection(configuration);

Example: capturing the camera with getUserMedia

The first step of any video call is getting the local stream. getUserMedia returns a Promise, so you can use async/await:

<video id="localVideo" autoplay playsinline muted></video>
const localVideo = document.getElementById('localVideo');

async function startCamera() {
  try {
    const stream = await navigator.mediaDevices.getUserMedia({
      video: true,
      audio: true,
    });
    localVideo.srcObject = stream; // show the live camera feed
    return stream;
  } catch (error) {
    console.error('Could not access camera/microphone:', error.message);
  }
}

startCamera();

getUserMedia only works on a secure context (HTTPS or localhost) and prompts the user for permission — you can never silently record someone.

Example: a complete data channel between two peers

The clearest way to see WebRTC work is to connect two RTCPeerConnection objects to each other in a single script. Here both peers live in the same page, so we can wire their signaling directly instead of through a server — the SDP/ICE exchange is exactly what a real signaling server would carry. Paste this into a browser console on any HTTPS page to watch it run:

// Two peers, normally on different machines.
const caller = new RTCPeerConnection();
const callee = new RTCPeerConnection();

// --- Signaling: hand each peer's ICE candidates to the other.
// In production this travels over WebSockets; here we call directly.
caller.onicecandidate = (e) => e.candidate && callee.addIceCandidate(e.candidate);
callee.onicecandidate = (e) => e.candidate && caller.addIceCandidate(e.candidate);

// The callee listens for the channel the caller opens.
callee.ondatachannel = (event) => {
  const channel = event.channel;
  channel.onmessage = (e) => {
    console.log('Callee received:', e.data);
    channel.send('pong'); // reply over the same channel
  };
};

// The caller creates the data channel.
const channel = caller.createDataChannel('chat');
channel.onopen = () => channel.send('ping');
channel.onmessage = (e) => console.log('Caller received:', e.data);

// --- Offer / answer negotiation.
async function connect() {
  const offer = await caller.createOffer();
  await caller.setLocalDescription(offer);
  await callee.setRemoteDescription(offer);

  const answer = await callee.createAnswer();
  await callee.setLocalDescription(answer);
  await caller.setRemoteDescription(answer);
}

connect();
// Logs:
// Callee received: ping
// Caller received: pong

The pattern is the same for video: instead of createDataChannel, you call peerConnection.addTrack(track, stream) for each track from getUserMedia, and read incoming media in an ontrack handler:

const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
stream.getTracks().forEach((track) => peerConnection.addTrack(track, stream));

// The remote peer's media arrives here:
peerConnection.ontrack = (event) => {
  document.getElementById('remoteVideo').srcObject = event.streams[0];
};

Common gotchas

  • It needs HTTPS. getUserMedia and getDisplayMedia fail outside a secure context (HTTPS or localhost).
  • You still need a signaling server. WebRTC handles the media, but you must build the offer/answer/ICE exchange yourself — usually over WebSockets.
  • Always create the offer/answer in order. setLocalDescription must run before setRemoteDescription for the matching side, or negotiation fails.
  • Don't forget a TURN server in production. STUN alone fails on restrictive networks; without TURN, ~10–20% of real-world connections will not establish.
  • Permissions can be denied. Wrap getUserMedia in try/catch and handle the rejection gracefully.

Summary

WebRTC gives the browser true peer-to-peer audio, video, and data. Use getUserMedia to capture media, RTCPeerConnection to negotiate and carry the connection, and RTCDataChannel for arbitrary data. WebRTC does not provide signaling — you exchange SDP offers/answers and ICE candidates yourself, typically over WebSockets, and configure STUN/TURN servers to traverse NAT. With those pieces in place you can build video calling, screen sharing, and real-time collaboration entirely in the browser.

Practice

Practice
Which of the following are true capabilities of WebRTC?
Which of the following are true capabilities of WebRTC?
Was this page helpful?