A real-time collaborative drawing board. Create a room, share the link, and sketch together — rectangles, circles, triangles, freehand pencil, eraser, colors, stroke widths, and undo / redo, all synced over WebSockets and persisted to Postgres.
- Frontend — Next.js 15 (App Router) + TypeScript + Tailwind v4 + shadcn/ui, on a
<canvas> - HTTP backend — Bun + Express, JWT auth (signup / signin / rooms)
- WebSocket backend — Bun +
ws, handlesjoin_room,drawing,erase_shape - Database — Postgres via Prisma (
User,Room,Chatwith ashape: Json?column) - Monorepo — Turborepo, Bun workspaces
apps/
web/ Next.js frontend
http-backend/ Express HTTP API (port 3001)
web-socket-backend/ WebSocket server (port 8081)
packages/
db/ Prisma schema + client
common/ Shared types (zod) + env config
backend-common/ Shared JWT secret loader
ui/ eslint-config/ typescript-config/
docker/
docker.backend Railway image for http-backend
docker.websocket Railway image for web-socket-backend
Prereqs: Bun ≥ 1.2, a Postgres database (a free Neon project works great).
# 1. clone + install
git clone https://github.com/Ansh-699/Excalidraw
cd Excalidraw
bun install
# 2. configure env
cp .env.example .env
# edit .env — at minimum fill in DATABASE_URL and JWT_SECRET
# 3. generate prisma client + apply migrations
bun run generate:db
cd packages/db && bunx prisma migrate deploy && cd ../..
# 4. run everything
bun run devOpens the frontend on http://localhost:3003, HTTP backend on 3001, WS on 8081.
Run individual services:
bun run start:frontend # only the Next.js app
bun run start:backend # only the HTTP API
bun run start:ws # only the WebSocket serverShipped as:
- Vercel —
apps/web(Next.js frontend) - Railway —
apps/http-backendandapps/web-socket-backend(two services, separate Dockerfiles indocker/) - Neon — managed Postgres
See the top-level .env.example for the full list of environment variables. Set them in Vercel and Railway via each dashboard; never commit a filled-in .env.
Rough steps:
- Create a Neon project, copy the connection string into
DATABASE_URL. - On Railway, create two services from this repo — one pointing at
docker/docker.backend, one atdocker/docker.websocket. SetDATABASE_URL,JWT_SECRET, andCORS_ORIGINSon both. - On Vercel, import the repo. Root directory stays at
/; Vercel readsvercel.json. AddNEXT_PUBLIC_BACKEND_URLandNEXT_PUBLIC_WEBSOCKET_URLpointing at your Railway public URLs. - Push to
main. Vercel and Railway redeploy automatically; GitHub Actions runscheck-types+buildon PRs.
| Shortcut | Action |
|---|---|
Ctrl/Cmd + Z |
Undo your last shape (broadcast to room) |
Ctrl/Cmd + Shift + Z / Ctrl + Y |
Redo |
MIT