Real-Time Collaboration System
The real-time collaboration system allows multiple users to edit the same document simultaneously. Changes are instantly synchronized across all connected clients with Yjs handling the complexity of operational transformation and conflict resolution.
Core Technologies
- Yjs - CRDT-based shared data structure for conflict-free collaboration
- y-websocket - WebSocket provider for real-time synchronization
- CodeMirror - Rich text editor with Yjs integration
- Node.js WebSocket Server - Central hub for document synchronization
Architecture
System Components
┌─────────────────────────────────────────────────────────────┐
│ Web Browsers (Clients) │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌───────────┐ │
│ │ User A │ │ User B │ │ User C │ │
│ │ CodeMirror │ │ CodeMirror │ │ CodeMirror│ │
│ │ + Yjs Doc │ │ + Yjs Doc │ │ + Yjs Doc │ │
│ └────────┬─────────┘ └────────┬─────────┘ └─────┬─────┘ │
│ │ │ │ │
│ └─────────────────────┼──────────────────┘ │
│ │ │
│ WebSocket Updates │
│ (JSON/Binary) │
└─────────────────────────────────┼───────────────────────────┘
│
┌─────────────▼──────────────┐
│ WebSocket Server │
│ (Node.js + y-websocket) │
│ │
│ Document Rooms: │
│ - Room 1 (Project A) │
│ - Room 2 (Project B) │
│ - Room N (Project N) │
└────────────────────────────┘
Data Synchronization Flow
Single Edit Operation
User A types "function" in CodeMirror
│
▼
Yjs detects text insertion at position 42
│
▼
Creates update event with operation metadata
{
type: "insert",
position: 42,
content: "function",
clientID: "user-a-xyz",
clock: 1234
}
│
▼
WebSocket sends update to server
│
▼
Server broadcasts to all other clients (B, C, D, ...)
│
▼
Each client receives update
│
▼
Yjs applies update to local document state
│
▼
CodeMirror re-renders with new text
│
▼
✓ All clients see "function" at position 42
Concurrent Editing Example
Two Users Editing Simultaneously
Well, what happens when multiple user are on a single project, but different sections?
See below:
Initial State:
Document: "Hello"
Cursor positions: A at index 5, B at index 0
Operations:
User A (timestamp: T1) User B (timestamp: T1)
Inserts "!" at end Inserts ">> " at start
│ │
└───────────┬───────────────────┘
│
Local operation applied
Local reorder: T1-A, T1-B
│
▼
User A sees: ">> Hello!"
User B sees: ">> Hello!"
Without Conflict Resolution (Bad):
User A alone would see: "Hello!"
User B alone would see: ">> Hello"
Inconsistent state → ✗ BROKEN
With Yjs CRDT (Good):
Yjs uses client IDs and logical clocks
Deterministic ordering: (clock, clientID)
Both users converge to: ">> Hello!"
Consistent state → ✓ WORKS
Real-Time Update Propagation
Multi-Client Synchronization Timeline
How does multi-Client synchronization happens?
T0:00 - User A connects to project "thesis-2024"
│
├─ Handshake: Hello, I'm user-a-xyz
├─ Server: Here's the current document state
└─ CodeMirror renders initial content
T0:05 - User B connects to same project
│
├─ Server sends full document state to B
├─ Server notifies A that B joined
└─ Awareness layer shows "User B is editing..."
T0:10 - User A types "Introduction"
│
├─ Yjs creates update: insert(0, "Introduction")
├─ WebSocket sends to server (~5-10ms latency)
├─ Server broadcasts to B
├─ B receives and applies update
└─ B's CodeMirror updates live ✓
T0:15 - User B types "Section 1" (while A is still editing)
│
├─ B's update goes to server
├─ A receives B's update
├─ Yjs merges both edits (no conflicts!)
├─ Both see same final text
└─ Automatic sync ✓
T0:20 - User A disconnects
│
├─ Server marks A as offline
├─ B can continue editing
└─ When A reconnects, gets full sync
T0:25 - User C connects
│
├─ Server sends full document state
├─ C gets all A and B's edits
└─ C's document is immediately current ✓
Conflict Resolution
Automatic CRDT Merging
SCENARIO: Two users insert at same position simultaneously
Initial: "Hello world"
User A: Insert "beautiful " at position 6
Result intention: "Hello beautiful world"
User B: Insert "amazing " at position 6
Result intention: "Hello amazing world"
════════════════════════════════════════════════════════════
WITHOUT YEJS (Simple Last-Write-Wins):
├─ User A's update arrives first: "Hello beautiful world"
├─ User B's update arrives second: "Hello amazing world"
└─ Result: B's text overwrites A's → DATA LOSS ✗
════════════════════════════════════════════════════════════
WITH YEJS (CRDT - Conflict-Free Replicated Data Type):
├─ A's insert: (position: 6, clientID: A, clock: 1)
├─ B's insert: (position: 6, clientID: B, clock: 1)
├─ Deterministic ordering by (clock, clientID)
│ → clientID: A < B (alphabetically)
│ → A's insert happens first in canonical order
└─ Result: "Hello beautiful amazing world"
Both A and B converge to SAME final state ✓
NO DATA LOSS ✓
Awareness State
Seeing Who’s Online
┌─────────────────────────────────────────────────┐
│ Document: "thesis-2024/main.tex" │
├─────────────────────────────────────────────────┤
│ │
│ Online Users: │
│ ┌──────────────────────────────────────────┐ │
│ │ 🔴 Alice (you) Editing Line 42 │ │
│ │ 🟢 Bob Idle for 30s │ │
│ │ 🟡 Carol Editing Line 128 │ │
│ └──────────────────────────────────────────┘ │
│ │
│ Cursor Positions: │
│ │ │
│ │ Alice's cursor 🔴 │
│ │ Bob's cursor 🟢 │
│ │ Carol's cursor 🟡 │
│ │ │
│ └─ Each user sees others' cursors in real-time │
│ │
└─────────────────────────────────────────────────┘