Online Whiteboard (Miro / Excalidraw)#
Problem statement (interviewer prompt)
Design an online whiteboard (Miro / Excalidraw): users collaboratively place shapes + arrows + sticky notes + freehand strokes on an infinite canvas. Handle 100+ concurrent editors per board, multiplayer cursors, undo/redo, exports, and embeds.
flowchart LR
A([User A])
B([User B])
WS[Realtime engine]
ST[(Scene state)]
A --> WS --> B
WS --> ST
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 client;
class WS service;
class ST datastore;
flowchart TB
subgraph Clients
CV([Canvas client])
CUR[Cursors]
TOUCH[Touch / stylus]
end
subgraph Edge
WS[WebSocket]
end
subgraph Engine
SVR[Per-board server]
CRDT[CRDT - shapes + properties]
OPSTREAM[[Op stream]]
PRESENCE[[Presence pub/sub]]
SNAP[Snapshots]
end
subgraph Storage
BOARD[(Board state)]
HIST[(History)]
ASSET[(Image / sticker assets + CDN)]
end
subgraph Features
TPL[Templates]
VOTE[Voting / dot voting]
TIMER[Timer]
EMBED[Embedded media / iframes]
EXPORT[Export PNG / PDF]
INF[Infinite canvas, zoom]
end
Clients --> WS --> Engine --> Storage
Features --- Engine
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 CV client;
class CUR,TOUCH,WS,SVR,CRDT,SNAP,TPL,VOTE,TIMER,EMBED,EXPORT,INF service;
class BOARD,HIST,ASSET datastore;
class OPSTREAM,PRESENCE queue;
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#
- Infinite canvas with shapes, text, images, sticky notes.
- Multi-user with cursors.
- Templates, voting, timer, embeds.
- Export, comments.
Non-functional#
- Op echo < 200 ms.
- Many concurrent editors per board.
- Large boards (10k+ elements) must scroll smoothly.
Trade-offs#
- Per-board owner simplifies; failover requires replay.
- Vector elements stream as ops; raster assets via CDN.
- Excalidraw is open-source and CRDT-friendly; Miro is closed.
Refs#
- Excalidraw architecture (open source).
- Miro engineering posts.
- ByteByteGo "Design collaborative whiteboard".
FAQ#
How does an online whiteboard like Miro work?#
Each board runs a realtime engine that owns a scene graph of shapes. Clients send shape ops over WebSocket, the engine merges with CRDT, and broadcasts deltas to all collaborators.
How do you handle 100+ concurrent editors on one board?#
Shard the board into spatial tiles so only viewers of the visible tile receive its updates. Throttle cursor pings to 20Hz and coalesce shape ops in a 50ms window before broadcast.
How are freehand strokes synced in real time?#
Strokes stream as point batches every few frames so other clients see the draw in progress. The final stroke is committed once the pen lifts and the buffered batch is collapsed.
How do whiteboards export to PDF or image?#
A headless renderer reads the scene state, lays it out at the chosen DPI, and emits PNG, SVG, or PDF. Exports run as background jobs because large boards can take seconds.
How is undo and redo implemented?#
Each client keeps a local op history and pushes inverse ops on undo. The remote engine accepts inverse ops like any other op so undo participates in the same CRDT merge.