Broadcasting, Rooms & Presence
🏠 Broadcasting, Rooms & Presence
ws, which is exactly what Chapter 7's scaling approach will extend across multiple servers.
Modeling Rooms
Instead of Chapter 3's single global Set, a room system needs a collection of sets — one per room:
Tracking Rooms on the Socket
A ws socket is just a plain object — attaching socket.rooms = new Set() when a client connects gives each socket its own record of which rooms it's currently in, essential for cleanup on disconnect.
Map of Sets
The rooms Map holds one Set of sockets per room name — the same readyState-checked broadcast pattern from Chapter 3, just scoped to one Set at a time instead of one global Set.
Broadcasting to Just One Room
Cleaning Up on Disconnect
This is why tracking rooms on the socket itself (not just in the rooms Map) matters — without socket.rooms, there'd be no way to know which rooms to clean this socket out of.
👀 Presence: Who's Online
A "who's here" feature just means notifying a room when membership changes:
Chapter 3's Global Broadcast vs This Chapter's Room Broadcast
Chapter 3
One Set, one audience — every connected client, no matter what they're actually doing.
This Chapter
A Map of many Sets — each message reaches only the clients who actually joined that specific room.
Detecting Dead Connections Faster
An ungraceful disconnect (a dropped Wi-Fi connection, not a clean close) can take a while for the OS-level TCP timeout to notice. The ping/pong frames from Chapter 2 let the server detect a truly dead socket well before that, so presence data doesn't lag behind reality.
Single-Server Limitation
This entire rooms Map lives in one process's memory — a client connected to a different server instance has no visibility into it at all. Chapter 7 covers exactly this problem.
💻 Coding Challenges
Challenge 1: Implement getRoomMembers
Write a function getRoomMembers(roomName) that returns an array of userId values for every socket currently in that room (skip sockets that don't have a userId set).
Goal: Practice reading from the room Map structure for a "who's here" UI feature.
Challenge 2: Broadcast a "Left" Presence Event
Extend leaveRoom so that, in addition to removing the socket from the room, it broadcasts a { type: "presence", event: "left", userId } message to the room's remaining members.
Goal: Practice mirroring the "joined" presence pattern for the leave case.
Challenge 3: Find the Memory Leak
A joinRoom/sendToRoom implementation is used in production, but the close event handler is never wired up to call leaveRoom. Explain what accumulates over time and why it's a problem, even though messages still appear to broadcast correctly at first.
Goal: Practice reasoning about a bug that isn't visible until the system has been running a while.
It's easy to wire up joinRoom and sendToRoom correctly and never notice that leaveRoom isn't being called on disconnect — broadcasts still appear to work fine, because sendToRoom's readyState check silently skips closed sockets. The bug is invisible in normal testing and only shows up as a slow memory leak: every room's Set quietly accumulates references to long-closed sockets forever, since nothing is removing them. Always wire leaveRoom (for every room a socket was in) into the close event, not just into an explicit "leave" action a user might trigger.
🎯 What's Next
Rooms currently live entirely in one server's memory — the next chapter tackles what happens when there's more than one server: Scaling WebSockets — sticky sessions behind a load balancer, and a Redis pub/sub adapter for broadcasting across every instance.