WebSocket & Real-Time Architecture: Patterns for Chat, Gaming, and Live Data
WebSocket & Real-Time Architecture#
When your app needs data the instant it changes — chat messages, stock prices, live scores, multiplayer game state — you need real-time architecture.
HTTP request-response doesn't cut it. This guide covers the protocols, patterns, and scaling strategies for building real-time systems.
Real-Time Protocols#
WebSocket#
Full-duplex, persistent connection. Both client and server can send messages anytime.
Client ← → Server (persistent TCP connection)
Best for: Chat, gaming, collaborative editing, live dashboards Latency: < 50ms Scaling: Requires sticky sessions or pub/sub fan-out
Server-Sent Events (SSE)#
Server pushes events to client. One-directional (server → client only).
Client ← Server (HTTP stream, auto-reconnect)
Best for: Notifications, live feeds, stock tickers Latency: < 100ms Scaling: Easier than WebSocket (HTTP-compatible, works with CDNs)
Long Polling#
Client makes HTTP request, server holds it until data is available.
Client → Server (request) ... waits 30s ... ← response
Client → Server (immediately re-request) ... waits ...
Best for: Fallback when WebSocket isn't available Latency: 0-30s (variable) Scaling: Simplest, but highest server resource usage
Comparison#
| Feature | WebSocket | SSE | Long Polling |
|---|---|---|---|
| Direction | Bidirectional | Server → Client | Server → Client |
| Protocol | WS/WSS | HTTP | HTTP |
| Reconnect | Manual | Automatic | Manual |
| Binary data | Yes | No (text only) | Yes |
| CDN compatible | No | Yes | Yes |
| Complexity | Medium | Low | Low |
| Best for | Chat, gaming | Notifications, feeds | Fallback |
Architecture Patterns#
1. Chat Application#
Client A → WebSocket Gateway → Message Router
Client B → WebSocket Gateway → Message Router
↓
Redis Pub/Sub
↓ ↓
WS Gateway 1 WS Gateway 2
(Client A) (Client B)
↓
Message Store (PostgreSQL)
Presence Service (Redis)
Key decisions:
- Message ordering: Per-channel sequence numbers
- Delivery guarantee: At-least-once (client dedup by message ID)
- Presence: Track online users in Redis with TTL heartbeats
2. Live Dashboard#
Data Source → Kafka → Stream Processor → Redis (latest values)
↓
SSE/WebSocket Gateway
↓ ↓
Dashboard Dashboard
(User 1) (User 2)
Key decisions:
- SSE is simpler and sufficient (server → client only)
- Throttle updates to 1/second for UI (even if data changes faster)
- Cache latest state in Redis for new connections
3. Multiplayer Game#
Player A → WebSocket → Game Server (authoritative state)
Player B → WebSocket → Game Server
Player C → WebSocket → Game Server
↓
State broadcast every 50ms (20 ticks/sec)
Input processing + physics
Anti-cheat validation
Key decisions:
- Server authoritative: Server owns game state, clients send inputs
- Client prediction: Client predicts locally, server corrects
- Tick rate: 20-60Hz depending on game type
- Interpolation: Smooth movement between server updates
4. Collaborative Editing (Google Docs)#
User A types → Local CRDT → Broadcast delta via WebSocket
User B types → Local CRDT → Broadcast delta via WebSocket
↓
Sync Server (merge CRDTs)
↓
Document Store
Key decisions:
- CRDT (Conflict-free Replicated Data Type) for automatic merge
- OT (Operational Transform) as alternative (Google Docs uses this)
- Cursor positions broadcast separately (high frequency, no persistence)
5. Notifications#
Event occurs → Notification Service → check preferences
→ Redis Pub/Sub
→ SSE to connected clients
→ Push notification (FCM/APNs) for offline
→ Email for important notifications
Key decisions:
- SSE for in-app (simpler than WebSocket for one-way)
- Fallback to push/email for offline users
- User preferences control which channels
Scaling WebSocket#
The hard part: WebSocket connections are stateful. A user connected to Server A can't receive messages routed to Server B.
Pattern 1: Pub/Sub Fan-Out (Redis)#
Server A (1000 connections) ←→ Redis Pub/Sub ←→ Server B (1000 connections)
←→ Server C (1000 connections)
Every message published to Redis. All servers subscribe and forward to their connected clients.
Pattern 2: Sticky Sessions#
Load balancer routes same user to same server:
User ABC → hash("ABC") → always Server A
User DEF → hash("DEF") → always Server B
Simpler but limits horizontal scaling. Server failure disconnects all its users.
Pattern 3: Dedicated Connection Servers#
Separate WebSocket servers from business logic:
Client → WS Connection Server (stateless routing)
→ publishes to Kafka/Redis
→ subscribes to user's channels
Business Logic Servers → publish events to Kafka
→ WS servers fan out to clients
Connection servers handle millions of idle connections. Business logic scales independently.
Connection Management#
Heartbeats#
Detect dead connections:
Server → ping every 30s → Client responds pong
No pong in 60s → close connection → client reconnects
Reconnection#
Clients must handle disconnections gracefully:
const connect = () => {
const ws = new WebSocket(url);
ws.onclose = () => {
// Exponential backoff: 1s, 2s, 4s, 8s, max 30s
setTimeout(connect, Math.min(30000, 1000 * 2 ** retries++));
};
ws.onopen = () => { retries = 0; };
};
Message Queuing#
Buffer messages during disconnection:
Client disconnects → server queues messages in Redis (TTL: 5min)
Client reconnects → server sends queued messages → resume real-time
Tools & Libraries#
| Tool | What | Best For |
|---|---|---|
| Socket.io | WebSocket + fallbacks | Node.js apps, broad browser support |
| ws | Raw WebSocket (Node.js) | Performance, minimal overhead |
| Ably | Managed real-time | Chat, notifications without infra |
| Pusher | Managed WebSocket | Simple pub/sub, presence |
| LiveKit | WebRTC SFU | Video/audio, screen sharing |
| Phoenix Channels | Elixir real-time | High-concurrency, fault-tolerant |
| Centrifugo | Real-time server | Language-agnostic, scalable |
When NOT to Use WebSocket#
| Scenario | Use Instead |
|---|---|
| One-way server updates | SSE (simpler, HTTP-compatible) |
| Infrequent updates (< 1/min) | Polling or SSE |
| Static content delivery | HTTP + CDN |
| Request-response APIs | REST or GraphQL |
| Serverless (Lambda) | SSE or polling (WS needs persistent connections) |
Summary#
- WebSocket for bidirectional, low-latency (chat, gaming)
- SSE for server-push only (notifications, feeds) — simpler to scale
- Scale with pub/sub — Redis or Kafka for fan-out across servers
- Always handle reconnection — exponential backoff + message queuing
- Separate connection from logic — WS servers scale differently than business logic
- Don't use WebSocket for everything — SSE and polling have their place
Design real-time architectures at codelit.io — generate interactive diagrams with WebSocket, Kafka, and Redis patterns.
Try it on Codelit
Chaos Mode
Simulate node failures and watch cascading impact across your architecture
Related articles
AI Agent Tool Use Architecture: Function Calling, ReAct Loops & Structured Outputs
6 min read
AI searchAI-Powered Search Architecture: Semantic Search, Hybrid Search, and RAG
8 min read
AI safetyAI Safety Guardrails Architecture: Input Validation, Output Filtering, and Human-in-the-Loop
8 min read
Try these templates
Uber Real-Time Location System
Handles 5M+ GPS pings per second using H3 hexagonal geospatial indexing.
6 componentsReal-Time Collaborative Editor
Notion-like document editor with real-time collaboration, conflict resolution, and rich media.
9 componentsNetflix Video Streaming Architecture
Global video streaming platform with adaptive bitrate, CDN distribution, and recommendation engine.
10 components
Comments