Technical case studiesCollaborative tools / 2025
Multiplayer Canvas
An engineering study in concurrent editing, per-collaborator undo, and the limits of offline recovery.
Multiplayer Canvas is a technical prototype for shared editing. The interesting work sits beneath the drawing surface: merging concurrent changes, keeping each person’s undo history local, and deciding what “saved” means when a connection disappears.
My contribution
I built the SVG editor and its document commands, then connected them to Yjs, a WebSocket relay, local persistence, and organization-based access. I also worked through failure cases around reconnects, room changes, and asynchronous loading. The project is an engineering study, with known limits documented in its repository audit.
Decision: separate shared content from presence
The browser holds a local Yjs document so editing does not wait for a server round trip. The WebSocket service relays document updates; Neon Postgres stores debounced snapshots. Cursor and selection presence remain ephemeral rather than entering document history.
Browser: SVG editor + local Yjs document
├─ IndexedDB snapshot and outbound queue
└─ authenticated WebSocket
↓
Node service: organization rooms + update relay
└─ debounced snapshots → Neon Postgres
Yjs provides deterministic convergence for concurrent updates. That does not remove application-level conflicts or validate the meaning of every incoming shape. Typed editor commands help at the client boundary; server-side validation of the resulting document remains a hardening task.
Decision: scope undo to the person editing
Restoring an old document snapshot can overwrite changes that arrived from someone else. Shared mode instead uses Yjs’s UndoManager with local transaction origins.
The intended behavior is concrete: undo your text edit against the current shared document while preserving a collaborator’s shape. The repository includes a regression for a local edit to remotely restored content. Local single-player history remains separate from collaborative history.
Decision: make recovery limits explicit
IndexedDB stores a local snapshot and outbound updates, and the client drains queued work after reconnecting. The relay verifies a Clerk session and organization access when a connection opens.
There is an important durability gap: a server acknowledgement confirms in-memory acceptance before the debounced database write. A crash in that interval can lose acknowledged work. Connection-lifetime authorization and multi-tab offline storage also need further hardening. The next reliability milestone is durable acknowledgement or reconciliation that can recover missing updates, backed by crash-recovery tests.
Evidence and outcome
The September 11 repository audit recorded a 32.6ms encode time for a 10,000-shape Yjs snapshot in one local sample. That measures a document operation, not browser frame rate, drag latency, database performance, or multiplayer capacity. The same audit records local regressions for incremental relaying, queue acknowledgement races, and failed-token retry.
The public demo lets visitors try shapes, labels, and local undo without an account; those drawings last until reload. Shared mode requires configured collaboration services and organization membership. The audit used test doubles for authenticated service integration, so it does not establish end-to-end production reliability.
The result is a working editor prototype with inspectable collaboration boundaries—and a concrete next step for making its save semantics trustworthy.
Inside the product
Built into the details.
Shared document updates merge through Yjs; cursor and selection presence stay outside document history.
Collaborative undo tracks local transaction origins rather than restoring an old whole-document snapshot.
IndexedDB stores local snapshots and queues outbound updates for reconnect.
The WebSocket service checks Clerk identity and organization access when a connection opens.
Debounced Neon snapshots persist room state; acknowledgements are not a durable-save guarantee.
Focusable shapes support keyboard selection, movement, and selection announcements.