Sweden
Loading...
India
Loading...

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)
  • email
  • name
  • picture

Backend/Auth Middleware maps this to:

  • uuid (internal system ID)
  • displayName
  • avatar URL

Data Prepared by Client

Before opening the WebSocket connection, the client prepares:

{
  "uuid": "a23f-983k-92df",
  "name": "User1",
  "avatar": "https://lh3.googleusercontent.com/..."
}

This guarantees:

  • No anonymous connections
  • Verified identity binding
  • Support for:

  • Audit logs

  • Access control
  • Ownership mapping

Phase 2: WebSocket Connection (Handshake)

Connection endpoint:

ws://api.domain.com/ws/presence/{version_id}

Each connection is strictly scoped to a project version.

Identity Transmission Methods

Method 1: Query Parameters

ws://api.domain.com/ws/presence/v123?uuid=...&name=...&avatar=...

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)

  1. Validate

  2. UUID exists in DB

  3. User has project access

  4. Register

  5. Store user in memory:

    active_users[version_id]
    
  6. Assign

  7. 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

  1. Detect socket closure
  2. Identify user by socket_id
  3. Remove from:
active_users[version_id]
  1. 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

active_users: {
  [version_id: string]: ActiveUser[];
}

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