A modern, persistent, collaboration-first whiteboard with real-time cursors, E2EE, and integrated video chat.
SyncSpace lets you sketch ideas, take notes, and collaborate live. It ships with three usage modes, robust state management via Redis, strong clientβside encryption, and a lean, containerized monorepo you can selfβhost in minutes.
-
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 β Infinite participants with live shape broadcast over WebSocket. Shapes and room state are persisted in Redis (24h TTL) for resilience.
-
Real-Time Collaboration
- Live Cursors β See exactly where others are pointing with smooth, color-coded cursors.
- Persistent Rooms β Room state (shapes, metadata) is saved in Redis, allowing users to refresh or rejoin without losing work.
- Smart Sync β Initial state synchronization ensures new users get the complete picture instantly.
-
Privacy First β Clientβside room keys and ephemeral sessions (auto-cleanup after inactivity).
-
Fast Canvas β 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 15, TypeScript, Tailwind, shadcn/ui, native WebSocket, Redis, WebRTC, Docker.
-
Monorepo β
apps/DrawDeck(frontend),apps/ws(WebSocket),apps/rtc(RTC signaling).
.
ββ apps/
β ββ DrawDeck/ # Next.js frontend (client + minimal server routes)
β ββ ws/ # WebSocket server (rooms, shapes, cursors, Redis persistence)
β ββ rtc/ # RTC signaling server (for Duo video)
ββ packages/ # Shared packages
ββ docker/
β ββ Dockerfile.frontend
β ββ Dockerfile.websocket
β ββ Dockerfile.rtc
ββ docker-compose.yml # Orchestration for all services + Redis
ββ turbo.json # Turborepo pipelines
ββ pnpm-workspace.yaml
ββ .github/workflows/ # CI/CD
Prerequisites
- Docker & Docker Compose
- Node 18+ (Node 20 recommended)
- pnpm 9+
This starts the full stack: Frontend, WebSocket Server, RTC Server, and Redis.
docker compose up --buildIf you prefer running services individually (requires local Redis instance):
# 1. Start Redis (required for WS)
docker run -d -p 6379:6379 redis:alpine
# 2. Install dependencies
pnpm install
# 3. Start development servers
pnpm devOr start individually:
pnpm --filter @app/drawdeck dev pnpm --filter @app/ws dev pnpm --filter @app/rtc dev
Environment variables are provided via .env.local in each app.
NEXT_PUBLIC_WS_URL=ws://localhost:8080
NEXT_PUBLIC_RTC_URL=http://localhost:8081
NEXT_PUBLIC_SITE_URL=http://localhost:3000
# NextAuth (Optional for local dev)
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=PORT=8080
REDIS_URL=redis://localhost:6379ββββββββββββββ WebRTC (P2P media) ββββββββββββββ
β Client A βββββββββββββββββββββββββββββΆβ Client B β
β (Duo) β β (Duo) β
βββββββ¬βββββββ βββββββ¬βββββββ
β β
β Shapes/Cursors (WS) β
βΌ βΌ
βββββββββββββββββββββββββββββββββββββ
β WS Server (apps/ws) β
β - Active connections (Memory) β
β - Broadcast logic β
β - Rate Limiting β
βββββββββββββββββ¬ββββββββββββββββββββ
β
β (Persistence)
βΌ
βββββββββββββββββββββββββββββββββββββ
β Redis β
β - Room Meta (Secret, type) β
β - Shapes List (Persistence) β
β - Session expiry (24h) β
βββββββββββββββββββββββββββββββββββββ
- Frontend-First: Next.js 15 handling UI, auth, and rendering.
- WebSocket Layer: Handles real-time shape updates and cursor movements. State is now decoupled from the WS server process, living in Redis.
- Redis Persistence: Rooms and shapes are stored in Redis with a 24-hour TTL, ensuring work is saved even if the server restarts or users disconnect temporarily.
- Real-Time Cursors: High-frequency cursor updates are broadcast via WebSocket (throttled) for a lively "multiplayer" feel.
- Duo Mode: Uses a separate RTC Signaling server (
apps/rtc) to establish P2P WebRTC calls for video/audio.
- Room Access: Protected by a 24-hour persistent secret.
- Encryption: payloads are encrypted with the room key.
- Solo/Duo: Strictly E2EE (Server acts as blind relay).
- Group: Server relays active payloads. Persistence in Redis is currently valid JSON shapes, but payloads can remain encrypted if the client enforces it (current implementation validates keys on server for access control).
- Ephemeral Nature: Redis keys expire after 24 hours of inactivity. No long-term database storage means no data liabilities.
# Build Frontend
docker build -f docker/Dockerfile.frontend -t acidop/drawdeck-frontend:latest .
# Build WebSocket
docker build -f docker/Dockerfile.websocket -t acidop/drawdeck-ws:latest .
# Build RTC
docker build -f docker/Dockerfile.rtc -t acidop/drawdeck-rtc:latest .PRs and issues welcome!
- Fork & branch from
main. pnpm installdocker compose up --buildto test.- Open a PR.
MIT. See LICENSE.