Learn WebSocket - Video/Audio Signaling with WebRTC
Episode 25 of 34

Learn WebSocket - Video/Audio Signaling with WebRTC

This episode covers WebSocket's role in WebRTC: signaling for SDP and ICE candidate exchange, the offer and answer flow, room management, and STUN and TURN server configuration.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

WebRTC lets two browsers share video and audio directly, without a server relaying the data. But there is one part that actually needs WebSocket: signaling. Before two peers can connect, they must get to know each other — and that introduction exchange is signaling.

Episode 25 covers WebSocket's role as the signaling channel for WebRTC: SDP offer and answer exchange, the ICE candidate flow, room management, and STUN and TURN configuration to get through NAT.

WebRTC Overview

Peer-to-Peer, but with Signaling

WebRTC flows media directly between browsers, but the initial exchange cannot happen out of the blue — peers do not know each other's addresses.

Signaling's role in WebRTC
peer A -> signaling (WebSocket) -> peer B
media  -> langsung antara peer A dan B

WebSocket only carries control messages: SDP (the media session description) and ICE candidates (candidate network addresses). Once the two peers agree, media flows directly without the server.

SDP and ICE Candidates

  • SDP: a document describing media capabilities — codecs, resolution, and formats.
  • ICE candidate: IP addresses and ports that can be used to connect.
  • STUN: a server that tells you your public address.
  • TURN: a server that relays media when a direct connection fails.

WebSocket's job is to shuttle SDP and ICE candidates back and forth until both sides connect.

WebSocket as a Signaling Channel

The Signaling Server

A signaling server is very simple: receive a message from one peer, forward it to the target peer.

JSWebRTC signaling server
const { Server } = require("socket.io");
 
const io = new Server(server);
 
io.on("connection", (socket) => {
  socket.on("signal", (data) => {
    io.to(data.tujuan).emit("signal", {
      dari: socket.id,
      tipe: data.tipe,
      payload: data.payload,
    });
  });
});

io.to(data.tujuan).emit("signal", ...) forwards the message to the target peer. The signaling server does not need to understand the SDP or ICE content — it is just a reliable courier.

The Signaling Flow

Offer and Answer

The complete connection establishment flow:

JSThe offer creator on the client
const pc = new RTCPeerConnection(config);
 
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
 
socket.emit("signal", {
  tujuan: peerId,
  tipe: "offer",
  payload: offer,
});

pc.createOffer() creates an SDP offer, which is then sent over WebSocket. The receiving peer answers with pc.createAnswer(), and both sides accept each other's remote description.

ICE Candidate Exchange

During connection establishment, both peers discover address candidates gradually.

JSSending and receiving ICE candidates
pc.onicecandidate = (event) => {
  if (event.candidate) {
    socket.emit("signal", {
      tujuan: peerId,
      tipe: "candidate",
      payload: event.candidate,
    });
  }
};
 
socket.on("signal", async (data) => {
  if (data.tipe === "candidate") {
    await pc.addIceCandidate(data.payload);
  }
});

pc.onicecandidate triggers the sending of each new candidate, and pc.addIceCandidate(data.payload) adds it to the connection. ICE trickling lets the connection start forming before all candidates are found.

Rooms and Peer Discovery

The Signaling Server with Rooms

A WebSocket room (episode 9) acts as a meeting room: everyone who joins the same room can signal each other.

JSRoom as a meeting room
socket.on("room:join", (room) => {
  socket.join(room);
 
  const hadir = io.sockets.adapter.rooms.get(room) || new Set();
  const lainnya = [...hadir].filter((id) => id !== socket.id);
 
  socket.emit("room:peer", lainnya);
 
  socket.to(room).emit("room:new-peer", socket.id);
});

hadir gives the list of existing peers, and socket.to(room).emit("room:new-peer", ...) tells the old ones a newcomer arrived. Each newcomer creates a WebRTC connection to all old peers — the mesh pattern.

The Mesh Limit

In a 3-4 person conference, mesh (everyone connected to everyone) is still fine. Above that, video and audio multiply and overwhelm bandwidth — that is when an SFU (Selective Forwarding Unit) like MediaSoup is needed, which is beyond this episode's scope.

STUN and TURN

ICE Server Configuration

Browsers need STUN and TURN servers to get through NAT.

JSICE configuration on the client
const config = {
  iceServers: [
    { urls: "stun:stun.l.google.com:19302" },
    {
      urls: "turn:turn.example.com:3478",
      username: "user",
      credential: "secret",
    },
  ],
};
 
const pc = new RTCPeerConnection(config);

stun:stun.l.google.com:19302 is a free public STUN. turn:turn.example.com:3478 provides a relay for connections that fail directly. TURN requires credentials — the signaling server shares them securely.

When Direct Connections Fail

Symmetric NATs and strict firewalls often block direct connections. ICE then chooses the TURN route: media is relayed through the server, at the cost of extra latency and bandwidth. For production, run your own TURN because public services are not meant for application scale.

Closing

Episode 25 showed the collaboration of WebSocket and WebRTC: WebSocket as the courier delivering SDP and ICE, WebRTC as the direct peer-to-peer media carrier. One arranges, the other flows.

Key takeaways:

  • WebRTC flows media directly; signaling needs WebSocket.
  • SDP describes media capabilities, ICE candidates are candidate addresses.
  • A signaling server just forwards messages without understanding the content.
  • Offers are created with createOffer, answered with createAnswer.
  • WebSocket rooms act as meeting rooms for peer discovery.
  • STUN gets through simple NAT; TURN saves connections that fail.

In the next episode we cover testing WebSocket applications: unit and integration testing, load testing with Artillery and k6, and chaos testing to simulate failures.

Learn WebSocket - Video/Audio Signaling with WebRTC | Learn WebSocket