Live cursors — those tiny avatars showing where other people are clicking — became table stakes for collaborative apps after Figma popularized them. The interview tests whether you understand the WebSocket plumbing, the throttling, the smooth-interpolation tricks, and the presence layer that powers them.
Functional requirements
- Show other users’ cursor positions in real time
- Each cursor has a name/avatar
- Cursors disappear when users leave
- Smooth movement, not jumpy
- Scales to dozens of concurrent users on the same document
Architecture
WebSocket connection per client. Server broadcasts cursor positions to all clients on the same document.
For high-scale: each document is partitioned to a single server instance. WebSocket clients on that document connect to that instance directly, avoiding cross-instance routing.
Throttling
Mouse moves fire many events per second — too many to broadcast.
Client-side throttling:
- Sample mouse position at 30Hz (every 33ms)
- Only send if position changed by >1 pixel
- Dispatch over WebSocket as a small binary message
Server-side throttling:
- Optional: aggregate cursor updates and broadcast batches every 50ms instead of one-by-one
- Reduces fanout cost at high concurrency
Smooth interpolation
The receiving client gets cursor updates at ~30Hz. To render at 60fps without stutter, interpolate:
- Buffer the last 2 received positions
- Tween between them using requestAnimationFrame
- If new position arrives, smoothly transition to it
The result feels like 60fps movement even though server pushes at 30Hz.
Cursor metadata
Each cursor needs:
- User ID (for color assignment)
- Name (initial display)
- Avatar (optional)
- Color (deterministic from user ID, so it is stable across sessions)
Presence layer
Beyond cursors:
- Active users list (sidebar, top bar)
- “Who is editing this” tags on individual elements
- Selection rectangles (for shape tools, highlight a user’s selection)
Presence is a separate channel from data updates. Don’t conflate.
Disappearing cursors
When a user disconnects:
- Server detects WebSocket close
- Broadcasts “user left” to others
- Other clients fade their cursor with a 200ms transition
Edge case: brief network drops. Use a “last seen” timeout (30 seconds) before declaring a user gone.
Coordinate systems
The cursor position must translate between:
- The sender’s viewport coordinates
- The document’s logical coordinates
- The receiver’s viewport (with their own pan/zoom)
Send logical coordinates over the wire. Each client transforms to their own viewport.
Bandwidth
Per-user bandwidth: ~30 messages/sec × 50 bytes = 1.5 KB/sec downlink per user. With 50 users on a document, each client receives 75 KB/sec — fine on broadband.
For mobile or constrained network, reduce update rate to 15Hz or only send when significant change.
Implementation libraries
- Liveblocks: drop-in presence + cursors + collaboration. Most-used in 2026.
- Yjs / PartyKit: CRDT-based; more flexible, more setup
- Custom: for full control, build on raw WebSocket
Common mistakes
- Sending mouse events on every browser event (60+ Hz)
- Hard-coded colors that collide between users
- No interpolation — cursors jump from frame to frame
- Cursors persisting after users leave (zombie cursors)
- Coordinate system mismatch between sender and receiver
Frequently Asked Questions
How do you handle a tab going inactive?
Browser throttles requestAnimationFrame. Document.visibilityState event tells you the tab is hidden. Pause sending updates; clear cursor on remote clients.
What is the latency budget?
Sub-100ms feels real-time. 200–300ms feels noticeable but acceptable. Above 500ms feels broken.
Why does Figma feel smoother than competitors?
Significant engineering on the cursor pipeline: aggressive throttling, smart interpolation, partition-aware connection management. Other apps cut corners and feel jittery.