ESC

Type to search the knowledge base.

Design Realtime Collaboration Cursors

Frontend system design for multiplayer cursors — presence, throttling, smoothing, layers, and awareness protocols.

advanced4 min read
  • system-design
  • interview
  • architecture
  • realtime
  • collab

Scope the problem

Design multiplayer presence cursors on a shared canvas or document: show other users’ pointers/carets in near realtime.

In scope: client presence channel, throttle/batch, render layer, identity colors, leave/join, performance under many peers.

Out of scope: full CRDT document sync (mention separation), WebRTC mesh topology deep dive unless needed.

Requirements

Type Examples
Functional see peers move; names/avatars; hide self
Non-functional < 100ms perceived lag on good networks; 60fps UI
Scale 10–50 concurrent viewers typical; call out 500+

Separate concerns

Document state sync (CRDT/OT)  ≠  Awareness/presence (ephemeral)

Cursors are awareness — lossy, high-frequency, no need for durable persistence.

Architecture

Pointer events → local cursor state → throttle → WS send
                                              ↓
                                         server relay (room)
                                              ↓
Other clients → interpolate/render overlay layer

Room id = document id. Auth: signed room token.

Wire protocol (sketch)

type AwarenessMsg =
  | {
      type: "cursor";
      userId: string;
      // document-relative coordinates OR index in text model
      x: number;
      y: number;
      ts: number;
      selection?: { anchor: number; head: number };
    }
  | { type: "leave"; userId: string }
  | { type: "join"; userId: string; name: string; color: string };

Coordinate space: use document-relative positions (canvas world coords or ProseMirror positions), not raw viewport pixels — otherwise scroll/zoom desyncs peers.

Client modules

AwarenessClient
├── connection (WS/SSE)
├── localPublisher (throttle 30–50ms)
├── remoteStore (Map<userId, RemoteCursor>)
└── CursorOverlay (React/Canvas)

Throttle publish

function createPublisher(send: (m: AwarenessMsg) => void, ms = 33) {
  let last: AwarenessMsg | null = null;
  let timer: number | null = null;
  return (m: AwarenessMsg) => {
    last = m;
    if (timer != null) return;
    timer = window.setTimeout(() => {
      timer = null;
      if (last) send(last);
    }, ms);
  };
}

On pointerup / blur send final position. On tab hidden stop sending; on leave send leave.

Rendering

Approach Pros Cons
DOM nodes per cursor simple a11y labels reflow cost
Single canvas overlay fast for many text a11y harder
SVG layer sharp similar to DOM

Smoothing: interpolate between last two remote points with rAF; don’t animate to stale targets after long gaps (jump).

// each frame
remote.displayX += (remote.targetX - remote.displayX) * 0.4;

Presence list

Sidebar avatars bound to same awareness store. Idle timeout: if no packet for 10s, mark away; 60s remove.

Scaling & backend interface

  • Pub/sub room channel; don’t durable-log cursor stream
  • Binary messages (MessagePack) if JSON CPU-bound
  • Server can drop intermediate cursor packets under load (lossy OK)
  • Sticky sessions not required if room state is in Redis pubsub

Performance budgets

  • Cap rendered cursors (nearest N, or active speakers)
  • Avoid React re-render of whole doc tree — isolate overlay store (zustand/external store)
  • Passive pointer listeners; batch reads

Privacy & UX

  • Opt-out “Share my cursor”
  • Color contrast for labels
  • Don’t expose emails — display names only
  • Follow mode (viewport follows lead user) as extension

Relation to document sync

Cursors may include selection ranges that must map through same document model version. If CRDT not yet synced, selection indices can be wrong — either:

  1. Attach to stable CRDT relative positions
  2. Or only show free-floating pointer until sync catches up

Tradeoffs

  1. WS vs WebRTC datachannel — server relay simpler; WRTC lower latency peer meshes hard
  2. DOM vs canvas overlay
  3. High frequency vs battery on mobile — adaptive throttle
  4. Accuracy vs smoothness

Interview close

Ephemeral awareness channel separate from durable doc sync; document-relative coords; throttle publishes; smooth remote render; lossy server OK; isolate overlay from React doc renders. Extend to selections and follow mode.

Further reading