A native macOS home for your Excalidraw drawings.
Local-first, with folders, version history, live sharing and search that understands what you drew.
Apple Silicon Β· signed and notarized by Apple
Excalidraw is a great canvas and a bad filing cabinet. Every drawing is a file you download, lose, and redraw three weeks later.
BetterDraw wraps the real Excalidraw editor in a macOS app that actually keeps your work: drawings live in a sidebar, save themselves as you draw, sort into folders, keep their own version history, and can be found later by describing what was in them. Everything stays on your disk.
π Import / export .excalidraw |
Real Excalidraw JSON on disk. Round-trips with excalidraw.com. Export PNG, SVG or .excalidraw. |
| βοΈ Region screenshots | Drag a box around part of a diagram, save it or copy it. Vector-rendered at 2560px+. |
| π Live collaboration | One link, no accounts. The relay runs inside your app, not on someone's server. |
| π Search by meaning | Ask for "login and sessions" and find the diagram, even if it's called "Untitled 4". |
| β Folders, stars, archive | Drag to organise, star what matters, archive instead of deleting. |
| π Version history | Automatic snapshots with one-click revert. Auto-saves as you draw. |
Everything is stored locally as plain files. Nothing is uploaded anywhere unless you turn on AI search or start a share session.
Nothing is locked in. Drawings are stored as ordinary .excalidraw JSON, so
anything you make here opens on excalidraw.com and vice versa. Paste a scene
straight from your clipboard, pick a file from disk, or export the current
drawing as PNG, SVG or .excalidraw.
Importing over an existing drawing snapshots the old version first, so a bad paste is always one click from being undone.
Hit Screenshot, drag a box around the part of the diagram you actually want, and save it or drop it straight on the clipboard. Output is rendered through Excalidraw's vector exporter and upscaled to at least 2560px on the long edge, so it stays crisp when it lands in a doc or a PR.
Share starts a WebSocket relay inside the app and opens a cloudflared quick tunnel in front of it. You get a link; whoever opens it lands in a browser-based Excalidraw session editing the same drawing, cursors and all. No account, no sign-up, and no server of ours in the middle β the relay is a thread in your app, and it dies when you stop sharing.
Without cloudflared installed you still get a link that works on your local
network.
Filename search is useless when everything is called "Untitled 4". Point BetterDraw at a Gemini API key and it indexes each drawing in the background β rendering it, reading the text in it, and writing a one-line summary plus tags. Then ask for what you remember ("login and sessions") and get ranked results with a reason for each match.
Indexing runs on idle and when the window loses focus, never while you're searching. Plain substring search over names, summaries and tags works with no key at all.
Drag drawings between folders, star the ones you keep coming back to, and archive the rest instead of deleting them. Archived drawings stay searchable.
- Auto-save β edits are written to disk 2.5s after you stop drawing. There is no save button.
- Version history β snapshots on save (at most one a minute), plus forced ones before an import or a restore, and manual ones on demand. The last 50 are kept per drawing; identical consecutive versions are skipped.
- Native chrome β real macOS vibrancy, transparent titlebar, no Electron.
Apple Silicon (M1 and later) only. Open the DMG, drag BetterDraw to Applications, and launch it β the build is signed and notarized by Apple, so there are no Gatekeeper warnings to work around.
Prerequisites: macOS, Node 20.19+ (or 22.12+), and a Rust toolchain.
git clone https://github.com/sidpremkumar/BetterDraw.git
cd BetterDraw
npm install
./build.shbuild.sh builds the release bundle and copies BetterDraw.app into
/Applications. To build without installing:
npm run tauri buildThe .app lands in src-tauri/target/release/bundle/macos/.
Known issue: the DMG bundling step can fail with a
bundle_dmg.sherror even when the.appitself built fine. The app inbundle/macos/is usable.
Builds are unsigned by default. To sign with your own Apple Developer identity,
set APPLE_SIGNING_IDENTITY before building rather than committing it to
tauri.conf.json.
npm install
npm run tauri devThe whole UI runs in a browser against a fake backend and a fixed set of fake drawings β useful for working on the React side, and for anyone who doesn't want to install Rust:
npm run demoThat serves the app at http://localhost:1421 with src/demo standing in for
every Tauri command, the collaboration relay, and the Gemini API. It's gated
behind VITE_DEMO, so it is compiled out of production builds entirely.
The images in this README are generated, not hand-captured:
npm run screenshots # all of them
npm run screenshots ai-search # just oneThis drives demo mode with Playwright using the Chrome already on your machine
and writes to docs/screenshots/. Add a new one by adding an entry to SHOTS
in scripts/screenshots.mjs.
cd src-tauri && cargo testAI search β open Settings (the gear in the toolbar), paste a Gemini API key. It is stored in the macOS Keychain, not in a config file. The Settings panel tracks token usage and estimated cost, and lets you reindex everything from scratch.
Public share links β install cloudflared:
brew install cloudflaredWithout it, sharing falls back to a local-network-only link.
| What | Where |
|---|---|
| Drawings | ~/Library/Application Support/com.sidpremkumar.betterdraw/drawings/ as .excalidraw files |
| Metadata (names, folders, stars, AI index) | index.json alongside them |
| Version snapshots | per-drawing, in the same directory |
| Gemini API key | macOS Keychain |
Nothing is uploaded anywhere. The only outbound requests are to Gemini, when you have configured a key, and to Cloudflare, while you are actively sharing.
src/ React frontend
components/ Sidebar, Canvas, Toolbar, dialogs, overlays
hooks/ useDrawings, useAutoSave, useCollab, useSearchIndex
services/ Tauri command wrappers, Gemini client
collab/ Sync engine + wire protocol (shared with the guest)
guest/ Browser client people get via a share link
demo/ Fake backend for demo mode and screenshots
src-tauri/ Rust backend
commands/ Drawing, folder, version, export, settings, collab commands
collab/ axum WebSocket relay + cloudflared tunnel supervisor
storage.rs On-disk layout
scripts/screenshots.mjs README screenshot generation
A few decisions worth knowing about before you change things:
- The
<Canvas>is keyed onactiveDrawingId+ a data nonce, so switching or restoring a drawing remounts Excalidraw rather than trying to reconcile state. src/collab/CollabClient.tsis shared verbatim by the host app and the guest web client. Change the protocol in one place and both sides move together.- The sidebar deliberately avoids per-row transitions and transforms. Under the
sidebar's
backdrop-filter, they promote every row to its own compositing layer and visibly lag the app. src/App.cssdefines the z-index scale. Excalidraw portals overlays up to z-index 999999, so all app chrome sits above that, and the window-drag strip sits below anything clickable.
Issues and pull requests are welcome. npm run demo is the fastest way to try
a frontend change; cargo test in src-tauri covers the relay.
Built on Excalidraw and Tauri. BetterDraw is not affiliated with either project.






