A real-time collaborative whiteboard with AI-powered shape recognition — built with Next.js, Fabric.js, WebRTC, and TensorFlow.js.
SyncCanvas lets multiple users draw together on a shared canvas in real time, with no server required. Every stroke, shape, and edit is synchronized peer-to-peer using WebRTC. An AI panel analyzes selected objects and predicts their shape using a rule-based recognizer, with a TensorFlow.js pipeline ready to plug in a trained model.
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router, Turbopack) |
| UI | React 19, Tailwind CSS 4, Lucide React |
| Canvas | Fabric.js 7 |
| P2P Networking | PeerJS (WebRTC) |
| State Management | Zustand 5 |
| AI / ML | TensorFlow.js (pipeline ready, no trained model yet) |
| Language | TypeScript 5 |
- Freehand pen and eraser tools
- Rectangle and circle shape tools
- Select, move, scale, and rotate objects
- Adjustable stroke width and color picker
- Host a room and share a Room ID with collaborators
- Join a room by entering the host's Room ID
- Every draw action broadcasts instantly to all connected peers
- Canvas snapshot sent automatically to new peers on join
- CRDT-based conflict resolution with Lamport clocks for consistency
- Select any drawn object and click AI Recognize Shape
- Rule-based recognizer classifies shapes from bounding box geometry
- Supports single objects and multi-stroke selections
- TensorFlow.js pipeline is connected and ready for a trained model
- Node.js 18 or later
- npm 9 or later
npm installnpm run devOpen http://localhost:3000 in your browser.
npm run build
npm start- Open http://localhost:3000 in Browser A.
- Click Create Room (Host). A Room ID appears — copy it.
- Open http://localhost:3000 in Browser B (or a different device on the same network or internet).
- Paste the Room ID into the Join Room field and click Join.
- Draw on either canvas — strokes appear on both screens in real time.
PeerJS uses a public signalling server by default. Both browsers must have internet access for the initial handshake, after which data flows directly peer-to-peer.
The AI panel (bottom-left of the canvas) uses a hybrid recognition architecture.
Analyzes the selected object's bounding box aspect ratio and Fabric.js type to predict the shape:
| Object | Prediction | Confidence |
|---|---|---|
Fabric Rect |
rectangle | 100% |
Fabric Ellipse |
circle | 100% |
| Freehand path, ratio 1.35 – 4.5 | rectangle | 70% |
| Freehand path, ratio 0.65 – 1.35 | circle or oval | 70% |
| Freehand path, ratio 0.22 – 0.65 | vertical rectangle | 70% |
| Freehand path, ratio > 4.5 | line or freehand stroke | 65% |
| Multi-stroke selection, ratio 1.2 – 4.5 | rectangle | 70% |
| Multi-stroke selection, ratio ~1 | square or circle/oval | 70% |
Multi-object selections (hand-drawn rectangles made from separate strokes) are handled by analyzing the combined bounding box of all selected paths.
TensorFlow.js is installed and initialized on the client. The pipeline includes:
initTF()— lazy-loads TensorFlow.js in the browserloadModel(url)— loads aLayersModelfrom any hosted URLobjectToTensor(object, canvas)— rasterizes the selected object to a 28×28 grayscale tensorpredictWithModel(tensor)— runs inference and returns the top predicted class
The AI panel displays the current TensorFlow.js status on every page load:
TensorFlow.js is connected, but no trained model is loaded yet.
When a trained model (e.g. a Quick Draw-style sketch classifier) is available, connecting it requires only calling loadModel(url) — no other changes needed.
synccanvas/
├── app/ # Next.js App Router (layout, page)
├── components/
│ └── whiteboard/
│ ├── Whiteboard.tsx # Main canvas + P2P orchestration
│ ├── Toolbar.tsx # Drawing tools UI
│ ├── ConnectionPanel.tsx # Host / join room UI
│ ├── PeerStatus.tsx # Live connection status
│ └── AIPanel.tsx # AI shape recognition panel
├── lib/
│ ├── canvas/
│ │ ├── aiBridge.ts # Rule-based + TF.js recognition pipeline
│ │ ├── fabricFactory.ts # Fabric canvas initialization
│ │ ├── canvasSerializer.ts # Object serialization for sync
│ │ └── canvasReconciler.ts # CRDT conflict resolution
│ ├── peer/
│ │ ├── PeerService.ts # WebRTC peer management
│ │ └── protocol.ts # Message types and sync envelope
│ └── utils/
│ └── ids.ts # UUID generation
├── store/
│ └── whiteboardStore.ts # Zustand global state
└── types/
└── fabric.d.ts # Fabric type augmentations
- Trained ML model — plug in a Quick Draw or custom sketch classifier via
loadModel()to replace rule-based heuristics with real inference - Cursor presence — show each peer's live cursor position (the protocol already defines
CURSOR_UPDATEmessages) - Undo / redo — per-user history stack synchronized across peers
- Sticky notes and text — Fabric
ITextobjects with full P2P sync - Room persistence — save and restore canvas state via a lightweight backend or localStorage
- Private rooms — end-to-end encryption on the WebRTC data channel
- Export — download the canvas as PNG or SVG
SyncCanvas demonstrates:
- Real-time distributed systems — custom CRDT protocol with Lamport clocks resolves concurrent edits without a central server
- WebRTC networking — mesh topology where every peer broadcasts to all others; new peers receive a full canvas snapshot on join
- AI integration architecture — clean separation between rule-based logic (works today) and a model inference pipeline (ready for tomorrow), following a progressive enhancement pattern
- Modern React patterns —
useReffor imperative Fabric.js integration, Zustand for shared UI state, dynamic imports to keep TensorFlow.js out of the SSR bundle - TypeScript throughout — custom type augmentations for Fabric objects, typed message envelopes, strict build with no suppressions
MIT