How Figma's multiplayer technology works¶
Summary¶
Figma's 2019 post (republished 2025-08-16 on HN, surfacing 4 years after
original publication) is the foundational description of Multiplayer's
architecture and design-choice reasoning — written before the 2024
QueryGraph extension. It documents why Figma rejected Operational
Transforms (OT) despite their Google-Docs-era dominance, why they drew
inspiration from Conflict-free Replicated Data Types (CRDTs) without
using "true" CRDTs, and the document data model (DOM-like object tree,
reducible to Map<ObjectID, Map<Property, Value>>) that the multiplayer
protocol would operate on.
(Note: the scraped content covers the intro sections — architecture, prototype, OT-vs-CRDT reasoning, document model — stopping at "For the rest of this post, we will talk about the details of Figma's multiplayer algorithm..." The algorithm-details sections are not in the raw capture; ingest is scoped to what's captured.)
Key takeaways¶
-
Centralized client-server, one process per document. Figma clients are web pages talking to a server cluster over WebSockets. The server spawns a separate process per multiplayer document to which every concurrent editor of that document connects. This per-doc process is the single authoritative reconciler — it's why Figma can simplify the CRDT model, since one authority exists to tie-break.
-
Connect / reconnect are stateless-ish: download fresh state, replay offline edits, resume. When a client opens a document it downloads a full copy; updates flow bidirectionally over the WebSocket. On offline → reconnect, the client redownloads a fresh copy, reapplies any offline edits on top of the latest state, then resumes syncing over a new WebSocket. "All of the multiplayer complexity is in dealing with updates to already-connected documents" — bootstrap and rejoin are intentionally simple.
-
Multiplayer is only for document sync. Everything else (comments, users, teams, projects) is Postgres + a separate sync system. The two systems are similar but implemented separately because of different trade-offs in performance, offline availability, and security. Canonical instance of picking sync mechanism by data shape, not by uniformity.
-
A three-client simulator prototype preceded the production code. Before touching the real codebase, the team built a web-page playground that simulated three clients + a server and visualized "the whole state of the system" — deliberately engineered to be able to set up offline-client and bandwidth-limited scenarios. Ideas from the prototype were then "grafted onto the existing codebase." Figma frames this explicitly as the prerequisite for quick iteration on a fundamental change. (patterns/prototype-before-production)
-
Explicit rejection of OT: "unnecessarily complex for our problem space." OTs define operations through text-offsets; the post quotes Li & Li ("real-world distributed systems raise serious issues... formal proofs are very complicated and error-prone, even for OT algorithms that only treat two characterwise primitives") and notes the combinatorial explosion of possible states. "Since Figma isn't a text editor, we didn't need the power of OTs." (concepts/operational-transform)
-
CRDT-inspired, not CRDT-compliant, because Figma is centralized. True CRDTs target decentralized systems with no central authority and carry unavoidable performance/memory overhead for convergence-without-coordination. Figma's server is the central authority, so the extra overhead is stripped. "Figma's data structure isn't a single CRDT — it's inspired by multiple separate CRDTs and uses them in combination." Specifically cited:
- Grow-only set — updates only add; duplicate-add is a no-op; state = replay updates in any order.
-
Last-writer-wins register — single-value container; update = (new-value, timestamp, peer-ID); peer-ID breaks LWW ties. Even with a central authority, the CRDT literature is "still worth researching... well-studied solid foundation... helps build intuition." (concepts/conflict-free-replicated-data-type)
-
Document data model: DOM-like object tree, reducible to
Map<ObjectID, Map<Property, Value>>. Every Figma file is a tree rooted at a single document object → page objects → page-content trees (the left-panel layers). Each node has an ID + a property bag. Equivalent database-row view: rows are(ObjectID, Property, Value)tuples. Consequence: "adding new features to Figma usually just means adding new properties to objects" — the schema is open-extension, feature velocity compounds on the decision. (concepts/object-tree-document-model)
Architectural numbers / scale claims¶
None disclosed in this chunk. The post is pre-production-numbers (2019 design-narrative style); quantitative production claims about the Multiplayer stack land in the 2024 QueryGraph/dynamic-loading post (33% speedup on slowest loads, 70% client-memory reduction, 18% YoY file growth, 300–500 ms preload savings, >40% parallel-decode cut).
Design-decision reasoning (explicit)¶
Three bets in one:
- Ship multiplayer at all — customer research signal was negative ("hovering art directors", "design-by-committee catastrophes"), so the team shipped against it on gut-level "it just felt wrong not to" for a web product. Productivity-tool-wide vindication came later.
- Reject OT — problem shape (graphics, not text; non-linear structure) didn't need the power, and OT's correctness proofs were unmanageable even for insert+delete-only-text.
- Reject "true" CRDTs — centralization makes the extra overhead wasteful. Take the math, discard the decentralization cost.
All three collapse into one meta-principle: "our primary goal was for it to be no more complex than necessary to get the job done", which is the concepts/simplicity-vs-velocity tradition articulated by Warfield at S3 and re-echoed by Dropbox Nucleus — every simplification compounds on downstream delivery speed.
Relation to the 2024 dynamic-loading post¶
This 2019 post describes the unchanged substrate: the one-process-
per-doc server, the WebSocket protocol, the object-tree data model. The
2024 post (sources/2024-05-22-figma-dynamic-page-loading) layers
QueryGraph — a bidirectional read+write dependency index — over the
same data model, and makes Multiplayer server-side-aware of structure
(server-side decoding becomes critical path). The data-model insight
("adding features = adding properties to objects") is the precondition
for QueryGraph: properties that encode references are the edges
QueryGraph indexes. Frontmatter-linked foreign keys (instance →
componentID) named in the 2024 post are exactly the "property values"
from the 2019 model.
The 2019 post also grounds why Figma's CRDT flavor is centralized — the answer QueryGraph operationalizes: server is the single authority that computes reachability subsets on load and fans edits out server-side. That's only sound because the 2019 architecture decided the server owns the tie-break.
Caveats¶
- Raw capture truncates before the algorithm-details section ("what
happens when two users edit the same property simultaneously",
undo/redo, ordering). Those sections may exist on the live blog but
aren't in
raw/; re-scrape could recover. - No quantitative production data in this chunk.
- No description of Figma's binary file format in this post (it's covered in the 2020 file-format post, not ingested yet).
- "Separate process per multiplayer document" is stated without RAM / cost footprint. The 2024 post hints at the cost (full-decoded-file held in memory server-side) but doesn't quantify either.
Sources¶
- Figma Engineering Blog, How Figma's multiplayer technology works, published 2019-10-11, reshared 2025-08-16 (HN #44922362, 176 points).
- Raw file:
raw/figma/2025-08-16-how-figmas-multiplayer-technology-works-2019-99536ff9.md - URL: https://www.figma.com/blog/how-figmas-multiplayer-technology-works/