Realtime Collaborative Whiteboard
Build a multiplayer whiteboard with CRDT sync, presence, and scalable WebSocket backends.
Design & Architecture
Choose CRDT vs OT, transport (WebSocket/WebRTC), and persistence model.
Why
Whiteboard architecture is locked in by the sync algorithm. CRDT (Yjs/Automerge) vs OT determines whether you support offline editing, peer-to-peer, and conflict semantics. Switching after launch means rewriting the core data layer and migrating every document.
When
Before building the canvas. Spend 1-2 weeks prototyping sync on a toy document and measuring conflict resolution on concurrent edits.
How
Example
Common Problems
- ⚠Choosing OT then needing offline support — OT cannot merge offline edits without a central server replay
- ⚠Deeply nested CRDT trees — Y.Map inside Y.Map inside Y.Map causes quadratic merge cost on large boards
- ⚠No snapshot strategy — reconnecting replays the full op log, taking seconds on a 10K-op document
Best Practices
- ✓Pick CRDT (Yjs) if you need offline or P2P — it is the only algorithm that merges without a central authority
- ✓Keep the document shallow: a flat Y.Map of shape Y.Maps, with a separate Y.Array for z-order
- ✓Snapshot every 30s / 50 ops and garbage-collect old ops — reconnects load one snapshot + delta, not the full history
Resources for this stage
Tools
Templates
Implementation & Realtime Sync
Build the canvas, sync engine, and presence (cursors and selections).
Why
The sync engine is where whiteboards break — lost updates, cursor jitter, and reconnection storms. Presence (showing other users' cursors) must be low-latency but throttled, or a fast mover floods the room with 100 updates/sec and starves the document sync.
When
After the sync prototype validates conflict resolution. Build canvas + sync + presence, then shapes and export.
How
Example
Common Problems
- ⚠Sending cursor on every mousemove — 200 events/sec flood starves document sync and hits WebSocket frame limits
- ⚠Mutating shape objects directly instead of through Y.Map — edits are local-only and lost on sync
- ⚠Global undo stack — user A's undo reverts user B's edit, destroying collaboration trust
Best Practices
- ✓Throttle presence updates to 30/sec and document edits to 60fps — bandwidth is the bottleneck, not the renderer
- ✓Always edit through the CRDT API (Y.Map.set) — direct mutation bypasses sync and causes silent divergence
- ✓Use Y.UndoManager with trackedOrigins per client so undo only reverts your own edits
Deployment & Scaling
Deploy WebSocket servers with sticky sessions, scale horizontally via Redis pub/sub, and persist snapshots.
Why
WebSocket servers hold in-memory Yjs documents — a crash loses unsynced edits. Horizontal scaling needs a pub/sub backbone (Redis) so a room on server A can sync with a client on server B. Without snapshot persistence, reconnecting reloads the full op history and tanks load time.
When
When the whiteboard works for 5 concurrent users. Load-test to 50 concurrent editors per room before launch.
How
Example
Common Problems
- ⚠Single WebSocket server with no Redis — crashes lose all in-memory rooms and unsynced edits
- ⚠No snapshot cron — reconnecting a 10K-op room replays the full log, taking 5+ seconds
- ⚠Skipping the load test — works great with 3 demo users, falls over at 30 concurrent editors
Best Practices
- ✓Back every WebSocket server with Redis pub/sub — it enables horizontal scaling and survives restarts for free
- ✓Snapshot every 30s / 50 ops and load snapshot + delta on reconnect — bounds load time regardless of document age
- ✓Load-test to 50 concurrent editors per room before launch — sync latency is the metric users feel
Resources for this stage
Tools
Templates
Full Developer Lifecycle
A 14-stage developer roadmap from idea to maintenance — visualize dependencies and track progress.
Next Step