Web Real-Time Communication (WebRTC) is a technology that allows browsers to establish direct peer-to-peer connections with each other, bypassing traditional centralized servers. By routing data directly from User A to User B, you achieve the lowest possible latency. This is the underlying engine powering Google Meet, Discord Voice, and browser-based multiplayer games.
Module 1: The Role of the Signaling Server
Although the media stream is peer-to-peer, browsers cannot magically find each other on the internet. They need a "Signaling Server" to exchange connection data before the P2P connection can begin. The signaling server is usually a simple WebSocket server whose only job is to relay messages between peers.
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
ws.on('message', (message) => {
// Broadcast the signaling payload to the other connected peer
wss.clients.forEach((client) => {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
});
});Module 2: Accessing the Hardware (MediaDevices)
Before making a call, we must request permission to use the user's camera and microphone.
let localStream;
async function initializeMedia() {
try {
localStream = await navigator.mediaDevices.getUserMedia({
video: { width: 1280, height: 720 }, // Request 720p
audio: true
});
// Display the local feed so the user sees themselves
document.getElementById('localVideo').srcObject = localStream;
} catch (error) {
console.error("User denied media access", error);
}
}Module 3: NAT Traversal (STUN and TURN)
Because most users sit behind home routers or corporate firewalls (NAT), their computer doesn't actually know its own public IP address. WebRTC relies on ICE (Interactive Connectivity Establishment) to find the best path.
ICE Servers Explained
- STUN Servers: Very cheap servers that simply echo back your public IP address. They work for ~80% of consumer connections.
- TURN Servers: Expensive relay servers. If two users are behind strict corporate firewalls that block P2P traffic, the TURN server acts as a middleman, relaying the video data. You MUST deploy a TURN server (like Coturn) in production to guarantee 100% call success rates.
Module 4: The Offer/Answer Handshake
To connect, Peer A creates an Offer (an SDP string describing its video codecs and IP address). It sends this Offer through the Signaling WebSocket to Peer B. Peer B receives it, and replies with an Answer.
const configuration = {
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' },
// Production requires a TURN server here
]
};
const pc = new RTCPeerConnection(configuration);
// 1. Add our local video tracks to the connection
localStream.getTracks().forEach(track => pc.addTrack(track, localStream));
// 2. Listen for incoming video from the remote peer
pc.ontrack = (event) => {
document.getElementById('remoteVideo').srcObject = event.streams[0];
};
// 3. Send ICE candidates (network paths) to the other peer via WebSocket
pc.onicecandidate = (event) => {
if (event.candidate) {
websocket.send(JSON.stringify({ type: 'candidate', candidate: event.candidate }));
}
};
// --- Peer A (The Caller) ---
async function startCall() {
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
websocket.send(JSON.stringify({ type: 'offer', offer }));
}
// --- Peer B (The Receiver) ---
websocket.onmessage = async (message) => {
const data = JSON.parse(message.data);
if (data.type === 'offer') {
await pc.setRemoteDescription(new RTCSessionDescription(data.offer));
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
websocket.send(JSON.stringify({ type: 'answer', answer }));
}
if (data.type === 'answer') {
await pc.setRemoteDescription(new RTCSessionDescription(data.answer));
}
if (data.type === 'candidate') {
await pc.addIceCandidate(new RTCIceCandidate(data.candidate));
}
};