Avatar and User Cursor for Collaborative Editor
Feature Overview
The Active User Presence Emitter is a real-time identity synchronization system that maintains and broadcasts the list of all currently connected users within a collaborative session (scoped by version_id).
This system ensures:
- A single authoritative source of truth for user identity
- Live awareness of who is online
- Consistent visual identity across all clients
The backend maintains the authoritative session state. The frontend only renders what the server declares, eliminating:
- Identity conflicts
- Duplicate users
- Desynchronization issues
Avatar Stack
Displayed on the navbar component:
- Shows profile images of active users
- Instantly updates on:
- User join
- User leave
-
Network reconnect
-
Enables:
-
Team visibility
- Editing awareness in collaborative mode
Live Cursor
Each user is assigned:
- A unique color
- A name label
- A real-time cursor stream
This ensures:
- Clear distinction between collaborators
- Reduced confusion in dense editing environments
- Visual ownership of movements and edits on the canvas
2. Architecture & Data Flow
The system is built using a centralized WebSocket broadcaster architecture with version-scoped rooms.
High-Level Architecture
- Client: React / Web UI
- Auth Provider: Google OAuth
- Presence Server: FastAPI + WebSockets
- Scope:
version_id
Each version_id acts as an isolated collaboration space.
3. Lifecycle: Join and Announce Pattern
Phase 1: Authentication (Client Side)
User logs in via Google OAuth.
OAuth returns:
sub(Google unique ID)emailnamepicture
Backend/Auth Middleware maps this to:
uuid(internal system ID)displayNameavatar URL
Data Prepared by Client
Before opening the WebSocket connection, the client prepares:
This guarantees:
- No anonymous connections
- Verified identity binding
-
Support for:
-
Audit logs
- Access control
- Ownership mapping
Phase 2: WebSocket Connection (Handshake)
Connection endpoint:
Each connection is strictly scoped to a project version.
Identity Transmission Methods
Method 1: Query Parameters
Method 2: Handshake Payload (Recommended)
After establishing the WebSocket connection, immediately send:
{
"type": "presence_handshake",
"payload": {
"uuid": "a23f-983k-92df",
"name": "User1",
"avatar": "https://..."
}
}
Benefits of the handshake method:
- Prevents socket spoofing
- Ensures identity binding before cursor streaming
-
Rejects:
-
Duplicate UUIDs in the same room
- Malformed identity objects
Phase 3: Registration & Server-Side Color Assignment
Backend Responsibilities (FastAPI)
-
Validate
-
UUID exists in DB
-
User has project access
-
Register
-
Store user in memory:
-
Assign
-
A unique session color (e.g.
#E91E63,#2196F3)
Stored Server Object Example
{
"uuid": "a23f-983k-92df",
"name": "User1",
"avatar": "https://...",
"color": "#E91E63",
"socket_id": "ws-38912"
}
Why Server-Side Color Is Mandatory
| Client-Side Color | Server-Side Color |
|---|---|
| Random per browser | Consistent for all users |
| Mismatch across peers | Single visual identity |
| Breaks cursor recognition | Stable collaboration |
| No room-level uniqueness | Enforced uniqueness |
Example Failure Without Server Control: User A appears red locally but blue on User B’s screen—breaking shared spatial awareness.
Phase 4: Broadcast (Emission)
After registration:
- Server updates:
active_users[version_id] - Server broadcasts full state:
{
"type": "presence_update",
"version_id": "v123",
"users": [
{
"uuid": "a23f-983k-92df",
"name": "User1",
"avatar": "https://...",
"color": "#E91E63"
},
{
"uuid": "f88d-239f-33ab",
"name": "User2",
"avatar": "https://...",
"color": "#2196F3"
}
]
}
Frontend Reactions
- Avatar stack re-renders
- Live cursor labels update
- Name–color pair refreshed
- Duplicate cursor conflicts avoided
Key Guarantee: All clients hold the exact same presence state at all times.
Phase 5: Disconnection & Cleanup
Triggers
- Browser tab closed
- Network drop
- WebSocket timeout
- Manual logout
Backend Actions
- Detect socket closure
- Identify user by
socket_id - Remove from:
- Re-broadcast updated list
This prevents:
- Ghost users
- Stale cursors
- Incorrect concurrency indicators
4. System Guarantees
This architecture guarantees:
- Strong real-time consistency
- Single identity per user per version
- Deterministic color assignment
- Instant UI updates on join/leave
- Safe multi-user scalability
- Zero frontend trust for identity
5. Failure Handling & Edge Cases
Duplicate Login (Same User, Two Tabs)
Strategies:
- Allow both with same UUID
- Or disconnect the older socket
Network Flap
-
Graceful reconnect using:
-
Same UUID
- New
socket_id - Same color restored
Server Restart
- Clients auto-reconnect
- Presence state rebuilt dynamically
6. Data Model Summary
Active User Object
type ActiveUser = {
uuid: string;
name: string;
avatar: string;
color: string;
socket_id: string;
};
Server Store
7. Business & UX Value
- Real-time collaboration confidence
- Prevents edit collisions
- Improves team situational awareness
-
Enables future features:
-
User focus tracking
- Editor locks
- Ownership highlights
- Access-level visualization