Excalidraw: The Open-Source Whiteboard That Redefined How Developers Draw

Excalidraw: The Open-Source Whiteboard That Redefined How Developers Draw

Excalidraw: The Hand-Drawn Whiteboard That Became a Developer Obsession

The best way to explain a complex system architecture is to draw it. The best way to draw it is with a tool that feels like paper, thinks like software, and doesn't ask you to create an account.

1. The Whiteboard Problem

Every developer knows the feeling. You're in a meeting, trying to explain a microservices architecture, a data flow, or a UI layout. Words fail. You reach for a whiteboard marker.

Then the meeting ends and someone needs to take a photo of the whiteboard with their phone. The photo is blurry. The text is illegible. The diagram is someone's laptop wallpaper, never to be seen again.

The digital alternatives exist, but they all have problems:

  • Miro: Powerful, but $8/user/month, and the sheer number of features is overwhelming for a quick sketch
  • Figma: Excellent for design, but requires accounts, project setup, and permission management
  • Draw.io: Precise, formal — but that's exactly the problem. Sometimes you want your diagram to look like a sketch, not a published document
  • Pen and paper: Works great, but doesn't survive the meeting
None of these tools capture the essence of a whiteboard session: low friction, no commitment, temporary, collaborative, and focused on ideas rather than polish.

Excalidraw's insight was simple: make the digital whiteboard feel like a sketch, and everything else follows.


2. The Hand-Drawn Aesthetic: Why It Matters

Excalidraw's most distinctive feature — the hand-drawn, "rough" style — is not a cosmetic choice. It's a deliberate design decision based on psychology.

Research shows that informal visual styles reduce defensive criticism. When people look at a polished, pixel-perfect diagram, they instinctively focus on the aesthetics: "that arrow isn't aligned," "the font is wrong," "the colors don't match." When they look at a sketch-like diagram, the focus shifts to the content: "does this flow make sense?" "is this component in the right place?"

The hand-drawn style communicates, subconsciously, that this is a work in progress. It invites collaboration. It says: "this is just an idea — feel free to change it."

This is the opposite of what tools like Visio or Lucidchart do. They produce diagrams that look "finished" — which discourages the very iteration that whiteboard sessions are meant to enable.


3. Technical Architecture: The Complexity Behind the Simplicity

3.1 Tech Stack

| Layer | Technology | Purpose |
|-------|-----------|---------|
| UI framework | React 19 + TypeScript | Component model, type safety |
| Canvas rendering | HTML5 Canvas API | Dual-canvas architecture |
| Hand-drawn effect | Rough.js | SVG path randomization |
| Build tool | Vite | Fast dev server, optimized builds |
| Testing | Vitest | Unit and integration tests |
| Package management | Yarn Workspaces | Monorepo structure |
| Collaboration | WebSocket + custom CRDT | Real-time multi-user editing |
| Collaboration server | excalidraw-room | Lightweight WebSocket relay |
| npm package | @excalidraw/excalidraw | Embeddable React component |
| License | MIT | Fully permissive, commercial use allowed |

3.2 Monorepo Structure

excalidraw/
├── excalidraw-app/          # Main application (excalidraw.com)
├── packages/
│   └── excalidraw/          # Core npm package
│       ├── element/         # Shape element library
│       ├── math/            # Geometry calculations
│       └── utils/           # Utility functions
├── examples/                # Integration examples
│   ├── with-nextjs/
│   └── with-script-in-browser/
├── dev-docs/                # Developer documentation (Docusaurus)
├── firebase-project/        # Firebase collaboration backend
└── Dockerfile

3.3 The Dual-Canvas Rendering Architecture

Excalidraw uses a dual-canvas overlay architecture that's both elegant and performant:

// Simplified structure
const staticCanvas = document.createElement('canvas');
staticCanvas.style.cssText = 'position:absolute;top:0;left:0;z-index:1';
container.appendChild(staticCanvas);

const interactiveCanvas = document.createElement('canvas');
interactiveCanvas.style.cssText = 'position:absolute;top:0;left:0;z-index:2';
container.appendChild(interactiveCanvas);

  • Static canvas (z-index 1): Renders all shape elements. Leverages caching to avoid redrawing unchanged elements.
  • Interactive canvas (z-index 2): Renders transient content — cursor, selection box, drag preview, resizing handles. Changes every frame during interaction.
This separation is critical for performance. During a drag operation, only the interactive canvas needs to repaint (60fps). The static canvas stays untouched, preserving its cache.

3.4 How the Hand-Drawn Effect Works: Rough.js

The magic behind Excalidraw's sketch style is Rough.js, a lightweight library that generates "imperfect" graphics.

Core principle: Every geometric shape is decomposed into small line segments, each with a random offset drawn from a Gaussian distribution. The result is a line that looks hand-drawn — slightly wobbly, never perfectly straight.

import { rough } from 'roughjs/bundled/rough.es5.js';

const rc = rough.canvas(canvas);
rc.rectangle(10, 10, 200, 100, {
stroke: '#000',
strokeWidth: 2,
roughness: 2.5, // 0 = precise, 1 = default, 2+ = messy
fillStyle: 'hachure', // solid | hachure | cross-hatch
hachureGap: 5,
});

Key parameters:

| Parameter | Effect | Typical value |
|-----------|--------|---------------|
| roughness | Line wobble intensity (0-10) | 1.8-2.8 |
| bowing | Line curvature | 1.0-1.5 |
| strokeWidth | Line thickness | 1-4 |
| fillStyle | Fill pattern | hachure / cross-hatch / solid |

The rendering pipeline:

1. renderScene() clears or fills the background
2. Sets zoom/scroll transforms on the canvas context
3. Iterates through visible elements, checking cache validity
4. For each element: calls generateElementCanvas() to render onto a small, independent canvas
5. On the small canvas: calls RoughCanvas.draw() to generate the Rough.js Drawable
6. Copies the cached canvas to the main canvas via context.drawImage()

3.5 The Element Cache System

Performance on complex diagrams (100+ elements) comes from an aggressive caching strategy. Each element is rendered to an independent small canvas and cached in a WeakMap.

Cache invalidation triggers:

  • Zoom level changes (unless shouldCacheIgnoreZoom is true)
  • Theme switch (light/dark mode)
  • Bound text version changes
  • Image crop property changes
  • Parent frame opacity changes
  • Element angle changes (for bound text elements)

Additionally, ShapeCache pre-generates Rough.js Drawable objects, avoiding path recalculation on every render. The cache key includes element type, dimensions, stroke/fill properties, roughness, and all other visual attributes.

Browser canvas limits:

  • Maximum area: 16,777,216 pixels (Safari mobile limit)
  • Maximum width/height: 32,767 pixels
  • Canvas automatically reduces scale when limits are exceeded

3.6 Real-Time Collaboration: CRDT Without the Complexity

This is the most technically impressive part of Excalidraw. Instead of using a standard CRDT library (like Yjs), the team built a simplified CRDT that's "good enough" for whiteboard use cases.

Communication layer: WebSocket

Excalidraw uses WebSocket for real-time collaboration because:

  • Full-duplex, persistent, low-overhead bidirectional channel
  • End-to-end latency typically 50-200ms
  • No HTTP handshake overhead per message

The collaboration server (excalidraw-room) is deliberately minimal — it only relays messages and doesn't participate in conflict resolution.

Data model: Every element is a JSON object

interface ExcalidrawElement {
  id: string;            // Globally unique ID
  type: string;          // rectangle | arrow | text | ...
  x: number;
  y: number;
  width: number;
  height: number;
  angle: number;
  strokeColor: string;
  backgroundColor: string;
  fillStyle: string;
  strokeWidth: number;
  roughness: number;
  opacity: number;
  groupIds: string[];
  seed: number;
  version: number;       // Monotonic version number
  versionNonce: number;  // Random value for tie-breaking
  isDeleted: boolean;    // Tombstone flag
  boundElements: string[];
  updated: number;       // Timestamp
}

The merge algorithm (three steps):

Step 1: State merge (union)

When Peer A receives Peer B's update, it takes the union of local and remote element IDs, producing a new array.

Step 2: Tombstone deletion

A simple union would make deletions impossible — the remote peer would immediately re-add the deleted element. Solution: elements are never actually removed. Instead, isDeleted is set to true, and the runtime filters them out. Actual cleanup happens only when the scene is saved to persistent storage.

Step 3: Version + Nonce conflict resolution

  • Each edit increments version
  • On merge, the higher version wins
  • If version is equal (concurrent edits), compare versionNoncelower nonce wins, ensuring deterministic convergence
function shouldAcceptRemoteElement(local, remote, appState): boolean {
  if (!local) return true;  // New element, always accept
  // Don't overwrite an element the user is actively editing
  if (appState.editingElement?.id === local.id) return false;
  if (remote.version > local.version) return true;
  if (remote.version < local.version) return false;
  // Same version: use nonce for tie-breaking
  return remote.versionNonce < local.versionNonce;
}

This is essentially a CRDT (Conflict-Free Replicated Data Type) — allowing concurrent writes that automatically merge via deterministic rules, without locks or sequencing.

Sync optimization:

  • Cursor position: Reported every 33ms (~30fps)
  • Element changes: Debounced at 100ms to batch rapid edits
  • Delta sync: Only changed elements are transmitted, not the full scene
  • Full snapshot: Every 20 seconds, a complete state snapshot is pushed to repair any divergence
  • Reconnection: Automatically reconnects, queues unsent operations, and batch-submits on recovery

End-to-End Encryption (E2EE):

Excalidraw enables E2EE by default. Only participants with the room link and password can decrypt the content. The collaboration server itself cannot read the data. This is critical for sensitive use cases — architecture diagrams, business plans, or any confidential information.


4. Deployment: Self-Hosting and Integration

4.1 Docker One-Liner

docker run --rm -dit -p 5000:80 excalidraw/excalidraw:latest

→ http://localhost:5000

Important: The official Docker image contains no analytics or tracking libraries.

4.2 Docker Compose with Collaboration Support

version: "3.8"

services:
excalidraw:
image: excalidraw/excalidraw:latest
container_name: excalidraw
restart: unless-stopped
ports:
- "3030:80"
environment:
- REACT_APP_DISABLE_TRACKING=true
- REACT_APP_WS_SERVER_URL=https://collab.yourdomain.com
depends_on:
- excalidraw-room

excalidraw-room:
image: excalidraw/excalidraw-room:latest
container_name: excalidraw-room
restart: unless-stopped
ports:
- "3031:80"

4.3 React Integration (npm package)

Excalidraw's most powerful feature is its embeddable React component. You can integrate a full whiteboard into any application with a few lines:

import { Excalidraw } from "@excalidraw/excalidraw";
import "@excalidraw/excalidraw/index.css";

function App() {
return (
<div style={{ height: "100vh" }}>
<Excalidraw />
</div>
);
}

Programmatic control via excalidrawAPI:

function App() {
  const [api, setApi] = useState(null);

const updateScene = () => {
api?.updateScene({
elements: [
{
type: "text",
x: 100,
y: 100,
text: "Hello from API!",
fontSize: 20,
},
],
});
};

return (
<>
<button onClick={updateScene}>Add Text</button>
<div style={{ height: "500px" }}>
<Excalidraw excalidrawAPI={(api) => setApi(api)} />
</div>
</>
);
}

Key Props:

| Prop | Type | Purpose |
|------|------|---------|
| initialData | ExcalidrawInitialDataState | Initial scene data |
| onChange | (elements, appState, files) => void | Content change callback |
| excalidrawAPI | (api) => void | API reference for programmatic control |
| UIOptions | object | UI configuration (enable/disable features) |
| viewModeEnabled | boolean | Read-only mode |
| zenModeEnabled | boolean | Zen mode (hide UI) |
| gridModeEnabled | boolean | Grid mode |

Export API:

import { exportToBlob, exportToSvg, exportToClipboard, serializeAsJSON } from "@excalidraw/excalidraw";

// Export as PNG
const blob = await exportToBlob({
elements: api.getSceneElements(),
appState: api.getAppState(),
files: api.getFiles(),
mimeType: "image/png",
quality: 0.92,
});

// Export as SVG (with embedded scene data for re-editing)
const svg = await exportToSvg({
elements, appState: { ...appState, exportEmbedScene: true }, files
});

// Copy to clipboard
await exportToClipboard({ elements, appState, files, type: "png" });

4.4 Next.js Integration (Dynamic Import Required)

Excalidraw doesn't support SSR, so Next.js requires dynamic import:

// excalidraw-wrapper.tsx
"use client";
import { Excalidraw } from "@excalidraw/excalidraw";
import "@excalidraw/excalidraw/index.css";

const ExcalidrawWrapper = () => (
<div style={{ height: "100vh" }}>
<Excalidraw />
</div>
);
export default ExcalidrawWrapper;

// page.tsx
import dynamic from "next/dynamic";
const ExcalidrawWrapper = dynamic(
async () => (await import("../excalidraw-wrapper")).default,
{ ssr: false }
);
export default function Page() { return <ExcalidrawWrapper />; }


5. Excalidraw Plus: The Freemium Model

Excalidraw follows an open-core + paid subscription model:

| Feature | Free | Plus ($6/mo/user) |
|---------|:----:|:------------------:|
| Infinite canvas | ✅ | ✅ |
| Full editor | ✅ | ✅ |
| Scenes | 1 | Unlimited + folders |
| Cloud storage | ❌ | ✅ |
| Auto-sync to server | ❌ | ✅ |
| Collaboration links | ✅ | ✅ |
| Read-only access | ✅ | ✅ |
| Voice calls & screen share | ❌ | ✅ |
| Comments | ❌ | ✅ |
| Live presentations | ❌ | ✅ |
| PDF & PPTX export | ❌ | ✅ |
| Generative AI | Limited | Extended (100 req/day) |
| Team workspace | ❌ | ✅ |
| End-to-end encryption | ✅ (local) | ✅ |

Plus offers a 14-day free trial.


6. Comparison with Alternatives

| Tool | Price | Open source | Hand-drawn | Collaboration | Best for |
|------|-------|:-----------:|:-----------:|:-------------:|----------|
| Excalidraw | Free / $6/mo | ✅ MIT | ✅ | Real-time E2EE | Technical sketches, architecture |
| Miro | Free / $8/user/mo | ❌ | ❌ | Real-time | Enterprise workshops |
| FigJam | Free / $3/seat/mo | ❌ | ❌ | Real-time | Figma ecosystem teams |
| tldraw | Free / SDK license | ✅ | ❌ | Real-time | Embedding whiteboard in apps |
| Draw.io | Free | ✅ | ❌ | Limited | UML, flowcharts, precise diagrams |
| Lucidchart | Free / $9/mo | ❌ | ❌ | Real-time | Enterprise process diagrams |

Key differentiators:

  • vs. Miro: Excalidraw is lighter — no registration, no login, just open and draw. Miro wins on template library and integrations. Excalidraw wins on speed, privacy, and the hand-drawn aesthetic.
  • vs. FigJam: FigJam's advantage is tight Figma integration. If you use Figma for design, FigJam is the natural choice. But FigJam lacks hand-drawn style and E2EE.
  • vs. tldraw: Both are open-source whiteboards. tldraw is more of a developer SDK — it emphasizes "embedding whiteboard into any application." Excalidraw is more of a finished product. tldraw is smaller and faster; Excalidraw is more complete and polished.
  • vs. Draw.io: Draw.io is for precise, formal diagrams (UML, BPMN, network topology). Excalidraw's hand-drawn style is inappropriate for "final" diagrams but far more effective for early-stage discussion.

7. Notable Users and Integrations

Excalidraw is embedded in some of the most popular developer tools:

  • Obsidian: Excalidraw plugin for notes and knowledge graphs
  • Notion: Embedded whiteboard blocks
  • VS Code: Extensions for drawing inline diagrams
  • CodeSandbox: Collaborative whiteboard for code reviews
  • Replit: Whiteboard for pair programming sessions
  • Google Cloud: Internal architecture diagramming
  • HackerRank: Whiteboard for technical interviews
It has become the de facto "drawing layer" for the developer ecosystem — not as a standalone product, but as an embeddable component that appears wherever technical discussions happen.

8. Limitations and Trade-offs

Honestly, Excalidraw is not for every use case:

1. No persistent team workspace (free): Data is saved in browser localStorage by default. Close the wrong tab and lose your work. Plus or self-hosting is required for persistence.

2. No structured data: No cards, documents, or timeline concepts. Everything is "drawn" — you can't search for text within shapes.

3. No built-in AI (free): In 2026, this is a deliberate choice. AI features are limited in the free tier.

4. Not for precise diagrams: No alignment grid, no measurement tools. For formal UML, use Draw.io.

5. Self-hosted collaboration is limited: The official Docker image doesn't support collaboration out of the box. You need the excalidraw-room server and proper WebSocket proxy configuration.

6. Multi-user Undo/Redo is unsolved: The official blog admits this is an open problem. When a remote peer's edit is received, the local undo stack is cleared — a compromise that works but isn't ideal.

These limitations stem from a core design principle: Excalidraw is a sketchpad, not a project management tool. It deliberately maintains a "throwaway" feel.


9. Who Should Use Excalidraw?

9.1 Ideal Use Cases

Software architecture diagrams: Draw microservices, data flows, and system topology in minutes
Wireframing and prototyping: Quick UI sketches that communicate layout and interaction
Teaching and education: Algorithm flowcharts, concept maps, and knowledge structures
Remote whiteboarding: Real-time collaboration with E2EE for sensitive discussions
Technical documentation: Embedded diagrams for blog posts, README files, and documentation
Mind mapping: Organize complex ideas on an infinite canvas

9.2 Where It Falls Short

Formal publication: Diagrams that need to look "final" — use Draw.io or Lucidchart
Enterprise workshops: If you need templates, integrations, and facilitation tools, use Miro
Large-scale project management: No task tracking, no deadlines, no team assignments
Mobile diagramming: The web app works on mobile but is not optimized for it

9.3 Decision Framework

Choose Excalidraw if:

  • You want to draw something quickly and share it
  • You value the hand-drawn aesthetic for its psychological benefits
  • You need to embed a whiteboard into your own application
  • You want E2EE for sensitive diagrams
  • MIT license matters to you

Choose Miro/FigJam if:
  • You need enterprise-grade templates and integrations
  • You're running facilitated workshops with 50+ participants
  • You already use Figma for design work

Choose Draw.io if:
  • You need precise, formal diagrams (UML, BPMN, network topology)
  • The diagram needs to look "finished" and publication-ready


10. My Verdict

Excalidraw's success can be distilled to three things:

1. Technical restraint: No complex OT algorithm — just "version + nonce + tombstone" for collaboration. No custom rendering engine — just Rough.js for hand-drawn effects. Dual-canvas + WeakMap caching for performance. It's "good enough" engineering that produces excellent results.

2. Product focus: It does one thing — "turn hand-drawn sketches into a communication tool" — and does it exceptionally well. No project management, no rich text, no formal diagrams. These "not doing" decisions are what make it lightweight and effective.

3. Open-source authenticity: MIT license, no tracking, self-hostable, embeddable npm package, complete API. The Plus subscription adds value without crippling the free version. It's one of the most genuine open-source business models I've seen.

Bottom line:

If you're a developer who draws diagrams, Excalidraw is the tool you probably already use. If you're building a product that needs a whiteboard component, @excalidraw/excalidraw is the most mature open-source embedding option available. If you're a team looking for a "low-friction visual communication space," open excalidraw.com, share the link, and start drawing.

It's not a tool for everything. But for what it does — turning ideas into sketches, instantly — nothing else comes close.

Try it:

  • Online: https://excalidraw.com
  • GitHub: https://github.com/excalidraw/excalidraw
  • npm: npm install @excalidraw/excalidraw
  • Docker: docker run -p 5000:80 excalidraw/excalidraw:latest


What's your go-to tool for drawing diagrams? Ever tried embedding Excalidraw into your own app? Share your setup.

Comments (0)

No comments yet. Be the first to comment!

Leave a Comment