Figma#
Problem statement (interviewer prompt)
Design Figma's multiplayer canvas: many users collaboratively edit a vector design file with shapes, components, prototypes, and comments. Cover the CRDT model, server-side document state, multiplayer cursors, undo/redo, version history, and plugin runtime.
flowchart LR
A[Editor A]
B[Editor B]
WS[Realtime Engine<br/>per file]
ST[(Object tree + history)]
CDN
A --> WS --> B
WS --> ST
CDN --> A
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class A,B,WS service;
class ST storage;
flowchart TB
subgraph Clients
APP([Figma client - WebGL])
PRES[Presence + cursors]
end
subgraph Edge
LB
WS[WebSocket gateway]
end
subgraph Engine[Multiplayer engine]
SVR[Per-file server<br/>single owner]
CRDT[Custom CRDT-ish merge<br/>object tree + properties]
OPS[[Op stream]]
SNAP[Snapshots]
PRES_SVC[[Presence pub/sub]]
end
subgraph Storage
OBJ[(Object tree per file)]
HIST[(Version history)]
ASSET[(Image assets + S3 + CDN)]
FONTS[Fonts service]
THUMB[Thumbnail renderer]
end
subgraph Features
COMM[Comments]
PLUG[Plugins / Widgets]
LIB[Components / styles libraries]
DEV[Dev mode + inspect]
PROTO[Prototyping]
end
subgraph Share
PERM[Permissions / teams / projects]
AUTH[SSO / SCIM]
end
Clients --> Edge --> Engine
Engine --> Storage
Features --- Engine
Share --- Storage
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class APP client;
class WS edge;
class PRES,SVR,CRDT,SNAP,FONTS,THUMB,COMM,PLUG,LIB,DEV,PROTO,PERM,AUTH service;
class HIST datastore;
class OPS,PRES_SVC queue;
class OBJ,ASSET storage;
Per-file owner#
- File "lives" on one process at a time → linearizes mutations.
- Migrations between hosts via op-log replay.
- Designed for tens of editors per file.
Asset handling#
- Images stored in S3 + CDN.
- Vector geometry lives in the in-memory object tree synced via ops.
Glossary & fundamentals#
Concepts referenced in this design. Each row links to its canonical page; the tag column shows whether it is a high-level (HLD) or low-level (LLD) concept.
| Tag | Concept | What it is | Page |
|---|---|---|---|
HLD |
CDN | edge caching for static assets | cdn |
HLD |
Pub/Sub & message brokers | topics, consumer groups, delivery semantics | pub-sub-pattern |
HLD |
CRDTs | commutative replicated data types | crdts |
HLD |
Realtime protocols | WS / SSE / polling / gRPC streaming | realtime-protocols |
Quick reference#
Functional#
- Real-time multiplayer canvas.
- Cursors, comments, history.
- Components / styles / libraries.
- Prototyping + Dev mode.
- Plugins.
Non-functional#
- Op echo < 200 ms locally.
- Tens of editors per file.
- 99.95% uptime.
Trade-offs#
- Single owner per file simplifies; needs migration on failover.
- Custom CRDT because graphics tree has parent/child + property merge semantics.
- WebGL client offloads rendering; server is purely state engine.
Refs#
- Figma engineering blog ("How Figma's multiplayer technology works").
- Yjs design comparisons.
- ByteByteGo "Design Figma".
FAQ#
How does Figma's multiplayer work?#
Each open file runs a dedicated realtime engine that holds the object tree in memory. Clients send incremental ops over WebSocket; the engine merges them with CRDT semantics and broadcasts to all editors.
Why does Figma use a CRDT model?#
Designs are a tree of properties where concurrent edits like move and resize must converge predictably. A CRDT per property lets users edit offline and reconnect without conflict prompts.
How does Figma sync vector graphics in real time?#
Vector shapes are stored as nodes with property maps. Updates send only the changed property deltas, so dragging a shape sends a position update at 60Hz rather than the whole object.
How are multiplayer cursors implemented in Figma?#
Cursors are ephemeral presence data broadcast on a side channel of the same WebSocket. They are not persisted, only relayed to current viewers at a throttled rate.
How does Figma store version history?#
The engine snapshots the object tree periodically and stores an immutable diff log. Users can branch off any snapshot to recover an older state without rewriting current history.