Back to blog
Ai News

Real-Time Cursors and Presence

5 min read

Live cursors — seeing a colleague's mouse move across a shared document in real time — became a defining feature of modern collaborative software after tools like Figma and Google Docs made it table stakes. It looks simple from the outside: a colored dot moving smoothly across the screen. Underneath, it's a small but genuinely tricky real-time systems problem, and getting it wrong produces the laggy, jittery, or stale cursors that make a collaboration feature feel broken rather than magical.

What "presence" actually covers

Presence is broader than just cursor position. A full presence system typically tracks:

  • Who's currently viewing or editing the document/page/canvas
  • Where their cursor or selection is, updated many times per second
  • What they're actively doing — typing, selecting, dragging
  • Connection status — online, idle, or recently disconnected

Cursor tracking is the most visually obvious piece, but the underlying presence layer — knowing who's connected right now — is what most collaborative features (avatars, "user is typing" indicators, live selection highlighting) are built on top of.

The transport layer: why WebSockets, not HTTP polling

Presence data is inherently high-frequency and bidirectional — a cursor can move dozens of times per second, and every connected client needs to see every other client's movement near-instantly. Traditional request/response HTTP, even with frequent polling, adds both latency and unnecessary server load: polling every 100ms per client to check for cursor updates means constant round trips even when nothing changed.

WebSockets (or similar persistent-connection protocols) solve this by keeping a single open connection per client, over which the server can push updates the instant they happen rather than waiting for the client to ask. This is why virtually every production live-cursor implementation is built on WebSockets, Server-Sent Events for one-way cases, or a managed real-time platform built on top of them (Pusher, Ably, PartyKit, Liveblocks, Supabase Realtime) rather than polling.

The core challenge: broadcast fan-out

The hard part isn't sending one cursor position — it's the fan-out. With N users in the same document, every cursor movement from any one user needs to reach all N-1 others. Naively, that's O(N²) message volume as the number of collaborators grows, which becomes a real bottleneck once a document has more than a handful of simultaneous editors.

Production systems handle this a few ways:

  • Throttling and batching — rather than broadcasting every single mouse-move event (which can fire 60+ times a second), cursor position updates are throttled to a sensible rate (commonly 20–30 updates/second) and often batched with other presence data in the same message.
  • Server-side room/channel scoping — the server only broadcasts a cursor update to clients actually viewing the same document or "room," not to every connected client system-wide, which keeps fan-out bounded to actual collaborators rather than the whole user base.
  • Delta compression — sending only the change in position rather than full state each time, when the transport and use case support it.

Smoothing: interpolation on the receiving end

Even with a good update rate, raw position updates arriving over the network produce visibly jerky motion, because network jitter means updates don't arrive at perfectly even intervals. The standard fix is client-side interpolation: instead of snapping a cursor directly to each new position the moment it arrives, the receiving client animates smoothly toward that position over the expected interval, effectively hiding small timing irregularities. This is the same technique used in real-time multiplayer games for smoothing other players' movement, and it's the difference between a cursor that feels alive and one that feels laggy even when the underlying data rate is identical.

Conflict and ordering

Presence data itself rarely has "conflicts" in the way document content does — a cursor position is just the latest known value, and if two updates arrive out of order, simply keeping the most recent (by server timestamp, not client timestamp, to avoid clock skew issues) resolves it cleanly. This is meaningfully simpler than collaborative content editing, which requires actual conflict resolution strategies like Operational Transformation or CRDTs to merge simultaneous edits to the same text. Teams building live cursors alongside live co-editing should treat these as two separate problems: presence is comparatively easy (last-value-wins), content merging is hard (needs a real CRDT or OT implementation).

Handling disconnects gracefully

A cursor that freezes in place when its owner closes their laptop, rather than disappearing, is a common and jarring bug. Robust presence systems need:

  • Heartbeats or connection health checks so the server can detect a silently dropped connection (not every disconnect sends a clean close signal — a laptop closing the lid or losing wifi often doesn't).
  • A timeout-based removal — if no heartbeat or update is received within a short window (commonly a few seconds), treat the user as offline and remove their cursor/presence indicator for everyone else.
  • Reconnection handling on the client — when connectivity resumes, the client needs to re-establish presence and re-sync, not just silently stay in a stale state.

Why this matters beyond "collaboration tools"

Live presence isn't only useful in document editors. Any product with concurrent multi-user interaction on the same resource benefits from it — a shared support inbox where agents can see who's already viewing a ticket avoids duplicate replies, a live dashboard showing who else is looking at the same data prevents conflicting actions, and a support chat widget showing "an agent is typing" gives visitors real-time confidence that someone's actually there, which measurably reduces the chance they abandon the conversation. A well-built Support Bot or shared-inbox tool benefits from exactly this kind of lightweight presence signaling, even without full live cursors — knowing someone else is actively engaged with the same conversation avoids the duplicated or crossed-wires responses that hurt trust in a live support channel.

The pragmatic build-vs-buy call

Building presence and live cursors from scratch — WebSocket infrastructure, room scoping, reconnection logic, interpolation — is a real engineering investment that's easy to underestimate. For most teams, using a managed real-time backend (rather than hand-rolling WebSocket infrastructure) is the faster and more reliable path unless real-time collaboration is the core differentiator of the product itself. The visible feature — a smoothly moving colored cursor — is simple to describe and surprisingly involved to build well, which is exactly why most teams outsource the transport layer and focus their own engineering time on the product logic sitting on top of it.

Get new posts as they publish

No spam — just the next post, straight to your inbox.

Keep reading

Discussion