Back to All Concepts
NetworkingProtocolsReal-timeExpert

WebRTC (Peer-to-Peer Communication)

Web Real-Time Communication protocol enabling direct peer-to-peer audio, video, and data transfer between browsers without intermediary servers, powering low-latency applications like Zoom and Google Meet.

What is WebRTC?

WebRTC (Web Real-Time Communication) is an open-source technology that enables peer-to-peer audio, video, and data sharing directly between browsers and mobile apps without requiring plugins or intermediary servers.

The magic: Your browser talks directly to your friend's browser, not through a server.

Why P2P? The Latency Problem

Traditional streaming (Client → Server → Client):
You → YouTube Server → Friend
Latency: 5-30 seconds (buffering, processing)

WebRTC (Peer-to-Peer):
You → Friend (direct connection)
Latency: 50-300ms (near real-time)

Use cases: Video calls, live gaming, screen sharing
Click to expand code...

Comparison

FeatureTraditional StreamingWebRTC P2P
Latency5-30 seconds50-300ms
Server CostHigh (relay all traffic)Low (signaling only)
QualityBuffered, stableReal-time, adaptive
Use CaseYouTube, NetflixZoom, Discord, Gaming

Core Components

WebRTC has 3 main APIs:

1. MediaStream (getUserMedia)

Access camera and microphone.

javascript
// Get video and audio from user's device
const stream = await navigator.mediaDevices.getUserMedia({
  video: { width: 1280, height: 720 },
  audio: true
});

// Display on page
const videoElement = document.querySelector('video');
videoElement.srcObject = stream;
Click to expand code...

2. RTCPeerConnection

Create peer-to-peer connection.

javascript
// Create connection
const peerConnection = new RTCPeerConnection({
  iceServers: [
    { urls: 'stun:stun.l.google.com:19302' }
  ]
});

// Add local stream
stream.getTracks().forEach(track => {
  peerConnection.addTrack(track, stream);
});

// Receive remote stream
peerConnection.ontrack = (event) => {
  remoteVideo.srcObject = event.streams[0];
};
Click to expand code...

3. RTCDataChannel

Send arbitrary data peer-to-peer.

javascript
// Create data channel
const dataChannel = peerConnection.createDataChannel('chat');

dataChannel.onopen = () => {
  dataChannel.send('Hello!');
};

dataChannel.onmessage = (event) => {
  console.log('Received:', event.data);
};
Click to expand code...

The NAT Problem

Problem: Most devices don't have public IP addresses. They're behind routers with NAT (Network Address Translation).

Your Computer: 192.168.1.5 (private IP)
       ↓
   Router NAT
       ↓
ISP: 203.0.113.42 (public IP)
Click to expand code...

Challenge: Friend can't connect to 192.168.1.5 directly. They need your public IP and port.


NAT Traversal: STUN, TURN, ICE

STUN Server (Session Traversal Utilities for NAT)

"Mirror" server that tells you your public IP and port.

javascript
const config = {
  iceServers: [
    { urls: 'stun:stun.l.google.com:19302' },
    { urls: 'stun:stun1.l.google.com:19302' }
  ]
};

const pc = new RTCPeerConnection(config);
Click to expand code...

How it works:

You → STUN Server: "What's my public address?"
STUN → You: "You are 203.0.113.42:54321"
You → Friend: "Connect to me at 203.0.113.42:54321"
Click to expand code...

TURN Server (Traversal Using Relays around NAT)

Relay server when direct P2P fails (e.g., strict corporate firewalls).

javascript
const config = {
  iceServers: [
    { urls: 'stun:stun.l.google.com:19302' },
    {
      urls: 'turn:turn.example.com:3478',
      username: 'user',
      credential: 'password'
    }
  ]
};
Click to expand code...

When needed:

Symmetric NAT or Strict Firewall:
You ← Can't connect directly → Friend
       ↓          ↓
    TURN Server (relay)

You → TURN → Friend
(Slower, but works)
Click to expand code...

Cost: TURN servers relay all media traffic = expensive bandwidth

ICE (Interactive Connectivity Establishment)

Protocol that tries multiple connection methods, picking the best.

ICE candidates (in order of preference):
1. Host candidate: Direct LAN connection (192.168.x.x)
2. Server reflexive: STUN-discovered public IP (P2P via router)
3. Relay candidate: TURN server relay (fallback)

ICE tries all candidates, uses fastest successful connection
Click to expand code...

WebRTC Connection Flow

mermaid
sequenceDiagram
    participant A as Alice
    participant S as Signaling Server
    participant B as Bob
    
    A->>A: getUserMedia()
    A->>A: createOffer()
    A->>S: Send offer (SDP)
    S->>B: Forward offer
    B->>B: setRemoteDescription(offer)
    B->>B: createAnswer()
    B->>S: Send answer (SDP)
    S->>A: Forward answer
    A->>A: setRemoteDescription(answer)
    
    Note over A,B: ICE Candidates Exchange
    A->>S: ICE candidate
    S->>B: Forward candidate
    B->>S: ICE candidate
    S->>A: Forward candidate
    
    Note over A,B: P2P Connection Established!
    A->>B: Media stream (direct P2P)
Click to expand code...

Step-by-Step

  1. Get media: Both users grant camera/mic access
  2. Create offer: Alice creates SDP offer describing her capabilities
  3. Signal offer: Alice sends offer to Bob via signaling server (WebSocket)
  4. Create answer: Bob creates SDP answer
  5. Signal answer: Bob sends answer to Alice
  6. ICE candidates: Both exchange network information
  7. Connect: WebRTC establishes best possible connection
  8. Stream: Media flows peer-to-peer

Complete Implementation

Signaling Server (Node.js + Socket.IO)

javascript
// server.js
const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server);

const rooms = new Map();

io.on('connection', (socket) => {
  console.log('User connected:', socket.id);
  
  socket.on('join-room', (roomId) => {
    socket.join(roomId);
    
    // Notify others in room
    socket.to(roomId).emit('user-joined', socket.id);
  });
  
  // Forward WebRTC signaling messages
  socket.on('offer', (data) => {
    socket.to(data.target).emit('offer', {
      offer: data.offer,
      sender: socket.id
    });
  });
  
  socket.on('answer', (data) => {
    socket.to(data.target).emit('answer', {
      answer: data.answer,
      sender: socket.id
    });
  });
  
  socket.on('ice-candidate', (data) => {
    socket.to(data.target).emit('ice-candidate', {
      candidate: data.candidate,
      sender: socket.id
    });
  });
  
  socket.on('disconnect', () => {
    socket.broadcast.emit('user-left', socket.id);
  });
});

server.listen(3000);
Click to expand code...

Client (JavaScript)

javascript
// client.js
const socket = io('http://localhost:3000');
const roomId = 'room123';

const config = {
  iceServers: [
    { urls: 'stun:stun.l.google.com:19302' },
    { urls: 'stun:stun1.l.google.com:19302' }
  ]
};

let localStream;
let peerConnections = {};

// Get local media
async function startCall() {
  localStream = await navigator.mediaDevices.getUserMedia({
    video: true,
    audio: true
  });
  
  document.getElementById('localVideo').srcObject = localStream;
  socket.emit('join-room', roomId);
}

// Create peer connection
function createPeerConnection(peerId) {
  const pc = new RTCPeerConnection(config);
  
  // Add local stream
  localStream.getTracks().forEach(track => {
    pc.addTrack(track, localStream);
  });
  
  // Handle remote stream
  pc.ontrack = (event) => {
    const remoteVideo = document.getElementById(`remote-${peerId}`);
    remoteVideo.srcObject = event.streams[0];
  };
  
  // Handle ICE candidates
  pc.onicecandidate = (event) => {
    if (event.candidate) {
      socket.emit('ice-candidate', {
        target: peerId,
        candidate: event.candidate
      });
    }
  };
  
  peerConnections[peerId] = pc;
  return pc;
}

// User joined room
socket.on('user-joined', async (peerId) => {
  const pc = createPeerConnection(peerId);
  
  // Create and send offer
  const offer = await pc.createOffer();
  await pc.setLocalDescription(offer);
  
  socket.emit('offer', {
    target: peerId,
    offer: offer
  });
});

// Receive offer
socket.on('offer', async ({ offer, sender }) => {
  const pc = createPeerConnection(sender);
  
  await pc.setRemoteDescription(new RTCSessionDescription(offer));
  
  // Create and send answer
  const answer = await pc.createAnswer();
  await pc.setLocalDescription(answer);
  
  socket.emit('answer', {
    target: sender,
    answer: answer
  });
});

// Receive answer
socket.on('answer', async ({ answer, sender }) => {
  const pc = peerConnections[sender];
  await pc.setRemoteDescription(new RTCSessionDescription(answer));
});

// Receive ICE candidate
socket.on('ice-candidate', async ({ candidate, sender }) => {
  const pc = peerConnections[sender];
  await pc.addIceCandidate(new RTCIceCandidate(candidate));
});

// User left
socket.on('user-left', (peerId) => {
  if (peerConnections[peerId]) {
    peerConnections[peerId].close();
    delete peerConnections[peerId];
  }
});
Click to expand code...

Protocols & Codecs

Transport Protocols

UDP (User Datagram Protocol):
- Connectionless, unreliable
- Fast, low latency
- Preferred for WebRTC (video/audio)
- Drops packets instead of retransmitting

TCP (Transmission Control Protocol):
- Connection-oriented, reliable
- Slower, higher latency
- Fallback for DataChannel
Click to expand code...

Media Protocols

RTP (Real-time Transport Protocol):
- Delivers audio/video packets
- Timestamps and sequence numbers
- No encryption

SRTP (Secure RTP):
- Encrypted version of RTP
- Used by WebRTC
- Prevents eavesdropping

RTCP (RTP Control Protocol):
- Quality metrics and feedback
- Packet loss, jitter, round-trip time
Click to expand code...

Video Codecs

javascript
// Check supported codecs
RTCRtpReceiver.getCapabilities('video').codecs.forEach(codec => {
  console.log(codec.mimeType);
});

// Common codecs:
VP8:  Open source, good quality, lower CPU
VP9:  Better compression, higher CPU
H.264: Patent-encumbered, hardware accelerated, universal
AV1:  Next-gen, best compression, experimental
Click to expand code...

Real-World Applications

1. Zoom / Google Meet

javascript
// Multi-party video conferencing
// Each participant P2P with server for mixing

Participant A ←→ Media Server ←→ Participant B
                     ↕
               Participant C

// Server mixes streams (SFU - Selective Forwarding Unit)
Click to expand code...

2. Discord Voice Channels

javascript
// Voice chat with low latency
const dataChannel = pc.createDataChannel('voice', {
  ordered: false,          // Don't wait for missing packets
  maxRetransmits: 0       // Don't retransmit
});
Click to expand code...

3. Screen Sharing

javascript
// Capture screen instead of camera
const screenStream = await navigator.mediaDevices.getDisplayMedia({
  video: { cursor: 'always' },
  audio: false
});

peerConnection.addTrack(screenStream.getVideoTracks()[0]);
Click to expand code...

4. File Transfer (P2P)

javascript
// Send large files directly without server
const dataChannel = pc.createDataChannel('file-transfer');
const CHUNK_SIZE = 16384; // 16KB chunks

function sendFile(file) {
  const reader = new FileReader();
  let offset = 0;
  
  reader.onload = (e) => {
    dataChannel.send(e.target.result);
    offset += e.target.result.byteLength;
    
    if (offset < file.size) {
      readSlice(offset);
    }
  };
  
  const readSlice = (o) => {
    const slice = file.slice(offset, o + CHUNK_SIZE);
    reader.readAsArrayBuffer(slice);
  };
  
  readSlice(0);
}
Click to expand code...

Scaling Challenges

Mesh (Full P2P)

3 participants: 3 connections
A ←→ B
A ←→ C
B ←→ C

10 participants: 45 connections!
Each user uploads to 9 others = 9x bandwidth
Click to expand code...

Limitation: Doesn't scale beyond ~5 participants

SFU (Selective Forwarding Unit)

Server forwards streams without mixing

A → SFU → B, C, D
B → SFU → A, C, D
C → SFU → A, B, D

Each client uploads 1 stream, downloads N-1 streams
Server load: Linear with participants
Click to expand code...

Used by: Zoom, Google Meet, Discord

MCU (Multipoint Control Unit)

Server mixes all streams into one

A, B, C → MCU (mixes) → Composed stream → A, B, C

Each client uploads 1, downloads 1
Server load: High (CPU encoding)
Click to expand code...

Interview Tips

When discussing WebRTC in system design interviews:

  1. Explain P2P benefit: "Direct connection reduces latency to <300ms vs 5-30s for server relay..."
  2. NAT traversal: "We'd use STUN for most users, TURN relay as fallback for strict firewalls..."
  3. Signaling server: "Need WebSocket server for SDP offer/answer exchange, not for media..."
  4. Scaling: "For >5 users, implement SFU server to forward streams without mixing..."
  5. Fallback: "If WebRTC fails, fallback to traditional RTMP streaming..."
  6. Real examples: "Zoom uses WebRTC with SFU for scalability..."

Related Concepts

About ScaleWiki

ScaleWiki is an interactive educational platform dedicated to demystifying distributed systems, software architecture, and system design. Our mission is to provide high-quality, technically accurate resources for software engineers preparing for interviews or solving complex scaling challenges in production.

Read more about our Editorial Guidelines & Authorship.

Educational Disclaimer: The architectural patterns and system designs discussed in this article are based on common industry practices, technical whitepapers, and public engineering blogs. Actual implementations in enterprise environments may vary significantly based on specific product requirements, legacy constraints, and evolving technologies.

Related Articles