Skip to content

A chill whiteboard app where you can doodle, sketch ideas, or jam with friends in real time while being on video call. Built for fast collab and smooth vibes.

License

Notifications You must be signed in to change notification settings

Acid-OP/DrawDeck

Repository files navigation

🎨 DrawDeck

A modern, privacy‑first, whiteboard with realtime collab, E2EE, and integrated video chat.

DrawDeck lets you sketch ideas, take notes, and collaborate live. It ships with three usage modes, strong client‑side encryption, and a lean, containerized monorepo you can self‑host in minutes.

✨ Features

  • Three modes

    • Solo β€” just open and draw. End‑to‑end encrypted (E2EE); nothing leaves your device unencrypted.
    • Private (Duo) β€” 1:1 collaboration with peer‑to‑peer WebRTC video/audio and a lightweight WebSocket control channel. E2EE drawing payloads.
    • Group β€” many participants with live shape broadcast over WebSocket. Drawing payloads are encrypted with the room key and relayed by the server (see Security Notes).
  • Privacy first β€” client‑side room keys, ephemeral sessions, zero persistence by default.

  • Fast Canvas β€” a custom canvas engine with selection, shapes, pencil, text, arrows, eraser, and keyboard shortcuts.

  • Rate limiting & queuing β€” IP‑aware server limits + client‑side message queue with backoff to keep rooms smooth.

  • Video calling β€” built‑in WebRTC for Duo rooms.

  • Authentication β€” NextAuth with Google OAuth (optional for Solo, required for some org setups).

  • Modern stack β€” Next.js, TypeScript, Tailwind, shadcn/ui, native WebSocket (no Socket.IO), WebRTC, Docker.

  • Monorepo β€” apps/DrawDeck (frontend), apps/ws (WebSocket), apps/rtc (RTC signaling).


πŸ“¦ Monorepo layout

.
β”œβ”€ apps/
β”‚  β”œβ”€ DrawDeck/         # Next.js frontend (client + minimal server routes)
β”‚  β”œβ”€ ws/               # WebSocket server (rooms, shapes broadcast, rate limit)
β”‚  └─ rtc/              # RTC signaling server (for Duo video)
β”œβ”€ packages/            # Shared packages
β”œβ”€ docker/
β”‚  β”œβ”€ Dockerfile.frontend
β”‚  β”œβ”€ Dockerfile.websocket
β”‚  └─ Dockerfile.rtc
β”œβ”€ turbo.json           # Turborepo pipelines
β”œβ”€ pnpm-workspace.yaml
└─ .github/workflows/   # CI/CD

πŸš€ Quick start (local dev)

Prereqs

  • Node 18+ (Node 20 recommended)
  • pnpm 9+
pnpm install
pnpm dev

By default this will run:

You can also start services individually:

pnpm --filter @app/drawdeck dev     # or: cd apps/DrawDeck && pnpm dev
pnpm --filter @app/ws dev           # or: cd apps/ws && pnpm start
pnpm --filter @app/rtc dev          # or: cd apps/rtc && pnpm start

πŸ”§ Configuration

Environment variables are provided via .env.local in each app (never commit secrets). Keep a public template at .env.example.

Frontend (apps/DrawDeck)

NEXT_PUBLIC_WS_URL=
NEXT_PUBLIC_RTC_URL=
NEXT_PUBLIC_SITE_URL=

# NextAuth (optional locally)
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
NEXTAUTH_URL=
NEXTAUTH_SECRET=

WebSocket (apps/ws)

PORT=
# rate limiting, secrets, etc. (implementation-specific)

RTC signaling (apps/rtc)

PORT=

🧱 Architecture overview (What makes DrawDeck different)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     WebRTC (P2P media)     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Client A  │◀──────────────────────────▢│  Client B  β”‚
β”‚  (Duo)     β”‚                            β”‚  (Duo)     β”‚
β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜        Signaling (HTTP/WS) β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
      β”‚                                         β”‚
      β”‚   Shapes/events (WS, encrypted)         β”‚
      β–Ό                                         β–Ό
           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
           β”‚          WS Server (apps/ws)      β”‚
           β”‚  - Rooms & membership             β”‚
           β”‚  - Broadcast shapes/events        β”‚
           β”‚  - Rate limit & IP cache          β”‚
           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
           β”‚          RTC Signaling            β”‚
           β”‚          (apps/rtc)               β”‚
           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
           β”‚        Next.js Frontend           β”‚
           β”‚        (apps/DrawDeck)            β”‚
           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Next.js 15 Frontend-First β€” runs both client UI and lightweight server routes. Backend services (ws, rtc) are kept minimal.

No Mandatory Auth β€” anyone can instantly start drawing in Solo mode. Auth (Google OAuth via NextAuth) is only needed for collaboration or org setups.

Local-Only Solo Mode β€” drawings are stored locally in the browser and never touch a server.

Peer-to-Peer Collaboration β€” Duo sessions use WebRTC for direct audio/video and E2EE drawing sync.

Group Mode with Secure Broadcast β€” multiple participants sync shapes over a WebSocket layer; payloads are encrypted with a room key.

Ephemeral by Design β€” no canvas history is persisted unless you extend it. All sessions are temporary.

Encrypted Everywhere β€” all drawing data is encrypted client-side with room keys; servers only relay ciphertext.

Hook-based WebSocket Client β€” a clean React abstraction for connecting, syncing shapes, and handling backpressure.

Rate Limiting + Queueing β€” IP-aware rate limiting server-side; client has a message queue with retry/backoff.

Modes

  • Solo β€” local only; drawing payloads encrypted client‑side; nothing readable server‑side.
  • Private (Duo) β€” WebRTC for media (P2P), WebSocket for control/shapes; room key E2EE.
  • Group β€” many participants over WS; shapes encrypted with room key and relayed by server. See notes below.

Canvas core

  • Primitive types: rectangle, diamond, circle, arrow, line, pencil, text, eraser, select, pan/hand.
  • Keyboard shortcuts: 1..0 map to tools (hand/select/shape…)
  • Message batching & prioritization when broadcasting.

πŸ” Security & privacy notes

  • Room keys are generated/handled client‑side and used to encrypt shape payloads.
  • Solo & Duo are end‑to‑end encrypted: payloads are unreadable by the server.
  • Group: payloads are encrypted with the room key and relayed by the WS server. If the server never sees decrypted content, this is room‑key encryption with relay. Depending on your threat model, treat this as E2EE if keys never leave clients; otherwise as transport+payload encryption.
  • No persistence by default β€” sessions are ephemeral; add storage if you need history.
  • Auth β€” NextAuth + Google OAuth when you want identity; Solo can be anonymous.

🚦 Rate limiting & backpressure

  • Server‑side: IP‑aware rate limiting; the server can return rate_limit_exceeded with retryAfter.
  • Client‑side: a token bucket (default ~50 msgs/min per client, configurable) + a priority queue. When over limit, DrawDeck queues messages and drains gradually (every ~2s) to avoid bursts.
  • Connection attempts: exponential backoff with temporary blocks after repeated failures.

🐳 Docker

Build images (from repo root):

# Frontend
docker build -f docker/Dockerfile.frontend -t acidop/drawdeck-frontend:dev .

# WebSocket
docker build -f docker/Dockerfile.websocket -t acidop/drawdeck-ws:dev .

# RTC
docker build -f docker/Dockerfile.rtc -t acidop/drawdeck-rtc:dev .

Run containers:

# Frontend (port 3000)
docker run -d --name drawdeck-frontend -p port-machine:port-locally \
  -e NEXT_PUBLIC_WS_URL=ws://your-host:port-ws \
  -e NEXT_PUBLIC_RTC_URL=http://your-host:port-rtc \
  -e NEXT_PUBLIC_SITE_URL=http://your-host:port \
  -e GOOGLE_CLIENT_ID=... -e GOOGLE_CLIENT_SECRET=... \
  -e NEXTAUTH_URL=http://your-host:port -e NEXTAUTH_SECRET=... \
  acidop/drawdeck-frontend:dev

# WebSocket (port )
docker run -d --name drawdeck-ws -p machine-port:port-ws acidop/drawdeck-ws:dev

# RTC (port 8081)
docker run -d --name drawdeck-rtc -p machine-port:port-rtc acidop/drawdeck-rtc:dev

βš™οΈ CI/CD (GitHub Actions)

  • Each service has its own workflow (frontend, ws, rtc).
  • Path filters ensure we only build/deploy when files in that app change.
  • Images are tagged with commit SHA (and optionally latest).
  • VM deploy script stops the old container, pulls the new image, runs it, and prunes old images.

πŸ§ͺ Scripts

Common root scripts:

  • pnpm build β€” Turborepo build for all apps
  • pnpm dev β€” start dev servers
  • pnpm lint β€” lint all packages
  • pnpm check-types β€” typecheck
  • pnpm start:client β€” start frontend (production)
  • pnpm start:ws β€” start WebSocket server
  • pnpm start:rtc β€” start RTC signaling server

🀝 Contributing

PRs and issues welcome! Please:

  1. Fork & branch from main.
  2. pnpm install and run pnpm dev to reproduce.
  3. Add or update tests if applicable.
  4. Open a PR with a clear description and screenshots/gifs.

πŸ“œ License

MIT. See LICENSE.


Acknowledgements

About

A chill whiteboard app where you can doodle, sketch ideas, or jam with friends in real time while being on video call. Built for fast collab and smooth vibes.

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages