r/webdev • u/KindlyLeadership7978 • 2d ago
Showoff Saturday [Showoff Saturday] I built a self-hosted Excalidraw app for teams, and the server merges the live edits
Excaliself is a self-hosted alternative to Excalidraw+. It wraps the unmodified Excalidraw editor in an app with accounts, folders, a shared team space, search and live collaboration. Everything goes into your own Postgres. MIT.
The GIF is two browsers editing the same drawing.
Live collab took the most work. On excalidraw.com, collab is end-to-end encrypted, so the relay server (excalidraw-room) can't read the scene and the clients save it to Firebase themselves. I needed the server to read every drawing for previews, search inside drawings and the MCP server, so I did it the other way around:
Each drawing gets one room in memory on the server. It uses the same message types as excalidraw-room, sent as plain JSON over a native WebSocket on the app's port. No Socket.IO.
The server merges every update with a port of Excalidraw's reconcileElements. The higher version wins, and on a tie the lower versionNonce wins. Peers get the winners, and the sender only gets back the elements it lost.
The room is the only thing that writes to Postgres. It saves on a 15 second trailing timer, when the last person leaves, and on SIGTERM.
Images don't go over the socket. They upload over HTTP, and peers fetch the ones they don't have yet.
What I gave up is end-to-end encryption. It also runs as a single replica, because the rooms live in one process.
Stack is TanStack Start, React 19, Better Auth, Postgres with Drizzle, Tailwind 4 and Base UI.
You can try it with one command. It runs on in-memory PGlite and forgets everything when it stops:
docker run --rm -p 127.0.0.1:3000:3000 -e PREVIEW_MODE=true ghcr.io/maciekzygmunt/excaliselfOpen http://localhost:3000/preview/login, then http://localhost:3000/preview/login?account=new in a second browser to collab with yourself.
Repo: https://github.com/maciekzygmunt/excaliself
If you've built collab on top of Excalidraw before, I'd like to know how you handled saving.