patterntypescriptModerate
Cursor Sharing Throttling Prevents Performance Collapse in Large Rooms
Viewed 0 times
cursor sharingmousemove throttlecollaborative canvasreal-time cursorperformancelodash throttle
Problem
In a collaborative canvas or document with many users, broadcasting raw mousemove events at 60fps from every user overwhelms the WebSocket server and causes lag spikes for all participants.
Solution
Throttle cursor position updates on the client before sending. 30ms (about 30fps) is imperceptible to human viewers and reduces traffic by ~95% compared to raw mousemove.
import { throttle } from 'lodash-es';
const sendCursor = throttle((x: number, y: number) => {
ws.send(JSON.stringify({ type: 'cursor', x, y, userId }));
}, 30);
canvas.addEventListener('mousemove', (e) => {
sendCursor(e.clientX, e.clientY);
});
// On receiving cursor updates, use CSS transitions for smooth rendering
function renderRemoteCursor(userId: string, x: number, y: number) {
const el = document.getElementById(`cursor-${userId}`);
if (el) {
el.style.transform = `translate(${x}px, ${y}px)`;
// CSS: transition: transform 30ms linear;
}
}Why
mousemove fires 100–250 times per second depending on the OS. At 10 users each sending 200 events/sec, the server receives 2,000 messages/sec just for cursor tracking. Throttling to 30ms caps this at 333 events/user/sec.
Gotchas
- Use CSS transform transitions (not top/left) for remote cursor rendering — GPU-composited and avoids layout.
- Send cursor positions relative to the document, not the viewport, so scroll position is accounted for.
- Cancel throttle timer on mouseleave to avoid a stale cursor appearing after the user moves away.
- In a canvas app, scale cursor coordinates by the zoom level before broadcasting.
Revisions (0)
No revisions yet.