FSD Frontend System Design

Part VI β€” The Blueprint Β· Lesson 11.9

HLD – Diagram Tools (Excalidraw)

In one lineA canvas render loop plus an operation log β€” undo, collaboration, and persistence all come from the log.

Where you've seen itDragging a shape smoothly while someone else edits the same diagram in real time.

  • Time20 min
  • DiagramCanvas layer model + operation log driving undo and sync
  • Chapter11 Β· High Level Design

Diagram

diagram
1–2 Β· SCOPE AND SCALE

  in scope   draw shapes Β· select/move/resize Β· undo Β· export Β· collab
  out        version history UI Β· comments Β· templates Β· auth
  scale      ~5k elements per board Β· 60fps interaction required
             2–10 concurrent editors
diagram
3 Β· WHY CANVAS, NOT DOM

  5,000 shapes as DOM nodes   β†’ layout + paint per frame, ~10fps
  5,000 shapes on <canvas>    β†’ one draw call loop, 60fps

  LAYER MODEL β€” separate canvases so you redraw the smallest thing

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ interaction layer  cursors,      β”‚  redraw every frame
  β”‚                    selection box β”‚
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  β”‚ active layer       shape being   β”‚  redraw during drag
  β”‚                    dragged       β”‚
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  β”‚ static layer       everything    β”‚  redraw only on commit
  β”‚                    else          β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

  Dragging one shape must never redraw the other 4,999.
diagram
6a Β· DEEP DIVE β€” THE OPERATION LOG POWERS EVERYTHING

  every change becomes an operation
    { id:'op91', type:'move', elementId:'e7',
      from:{x:10,y:20}, to:{x:80,y:60}, author:'u1', seq:412 }

           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
           β”‚ operation logβ”‚
           β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β–Ό         β–Ό         β–Ό
      UNDO      SYNC     PERSIST
   invert &   broadcast  append-only,
   re-apply   to peers   replay to
                         rebuild state

  One structure, three features. Storing only final state gives
  you none of them.
diagram
6b Β· DEEP DIVE β€” COLLABORATION AND SPATIAL LOOKUP

  collab    WebSocket (2.4) Β· per-element last-write-wins is
            adequate here (unlike text β€” see 11.11)
            cursors broadcast at ~20Hz, throttled, never persisted

  hit-test  naive: check 5,000 shapes per mousemove  βœ—
            spatial index (quadtree/R-tree): check ~10  βœ“
            rebuild the index on commit, not during drag

How it works

  1. Canvas with layered surfaces, redrawing only the layer that changed.
  2. Model elements as plain data (type, x, y, w, h, style, version) β€” rendering is a pure function of that array.
  3. Every mutation is an operation appended to a log; undo inverts and re-applies.
  4. Spatial index for hit testing and viewport culling β€” draw only what's on screen.
  5. requestAnimationFrame render loop, coalescing pointer events so a fast mouse doesn't trigger extra frames.

Trade-offs

ChoiceCost
CanvasFast; you rebuild accessibility and text selection
SVG/DOMFree a11y and hit testing; dies past ~1k nodes
Operation logUndo + sync + persistence; more memory
Last-write-winsSimple; occasional lost edit on the same shape

Accessibility (8.1): canvas is opaque to screen readers. Maintain a parallel DOM list of elements, keyboard shortcuts for select/move, and text alternatives β€” this is the honest weakness of canvas apps and worth naming yourself.

Interview angle

"Dragging gets slow with 5,000 shapes. What's wrong?"

  • You're probably redrawing the entire canvas per pointer event β€” split into static and active layers so a drag redraws one shape.
  • Hit testing is likely linear per mousemove; add a quadtree and cull to the viewport.
  • Coalesce pointer events into one requestAnimationFrame tick; browsers deliver far more of them than you can render.

Recap

  • Layered canvases: redraw the smallest surface that changed.
  • The operation log gives you undo, collaboration, and persistence from one structure.
  • Spatial indexing turns hit testing from O(n) into O(log n).