An infinite whiteboard you draw on together with friends at the same time, peer to peer in the browser.
Website · Open the app · Leia em português
Press Invite friends and send six words, a QR code or a link (all three carry the same words), then draw on the same endless slate while you talk: chat with replies and reactions, a voice call with strong noise suppression, camera bubbles that float next to each cursor, and screen sharing. Up to 5 people, no account, and no server of ours in the middle. Boards are saved in your browser.
A right click, a long press or the Menu key (Shift+F10 too) opens one keyboard friendly menu: copy, paste, duplicate, delete, front and back, color, group and lock on shapes and strokes; paste, a sticky note, text and zoom to fit on the empty board; reply and react on chat messages; mute for me and go to their cursor on the people in the call.
Everyone on the board can join the call. Mute, turn the camera off, share a screen, and pick the microphone, camera and speaker in the settings (the change applies during the call). Strong noise suppression runs the browser filters plus our own DSP AudioWorklet; the mic test lets you hear yourself after it.
- The inviter gets six words (66 random bits from the BIP39 English list), a QR code and an invite link with a Copy button. All three carry the same words and work once, for ten minutes. The link looks like
.../app/#join=word-word-word-word-word-word: the words sit in the hash, which the browser never sends to any server. Opening it drops the friend straight into the join, and the app clears the words from the address bar. Pasting the whole phrase (or the link) into any word box spreads it one word per box. - Both devices derive a topic and an AES-GCM key from the words (PBKDF2, then HKDF) and meet on several public MQTT brokers at once. The brokers only see a random topic and ciphertext. The WebRTC offer and answer travel sealed with that key.
- Once connected, the friend's screen shows six confirmation words made from the DTLS fingerprints of the connection. The inviter types them back. If someone sat in the middle, the words would not match, and three wrong tries close the connection.
Anyone already on a board can invite the next person. The mesh introduces newcomers to everyone, and forwards through a relay member when two people cannot reach each other directly.
Each element is a map of fields, and each field is a last writer wins register stamped with a Lamport time and the replica id. Deleting is a field too, so undo is just a newer write. Pen strokes are ordered point lists that only their author appends to, so chunks can arrive late or twice and still line up. A late joiner merges the full state in small parts and asks for the pictures it lacks; pictures are named by their SHA-256 and checked on arrival. The unit tests run thousands of random concurrent edits across three replicas over a lossy, reordering network and require identical results.
- Drawing together needs at least one person who has the board to keep it open. There is no server that stores boards.
- Pairing depends on public MQTT brokers that are not ours. If none answers, pairing fails with a clear message.
- Some networks block direct connections. The app tests at start whether the public STUN servers answer. No TURN server is built in; you can add your own in the settings.
- Boards live in your browser's storage. Clearing site data deletes them, so export a board file to keep a copy.
- Pen pressure needs a pen that reports it. With a mouse or a finger, the width follows your speed.
- The eraser removes whole strokes and shapes. Pictures are scaled to 1600 pixels; animated GIFs keep their first frame.
- Reloading the page ends the live session. Invite each other again and the boards merge back.
- Calls are a full mesh: with five people each device sends its voice and camera four times, which needs a decent upload. Screen sharing needs a browser that supports it, so most phones cannot share.
Mabel is the bold one, chalk up and already drawing. Otto is the calm one, crayon close, happy to watch first. See the character sheet.
npm install
npm test # unit tests: CRDT convergence, smoothing, board file, pairing, mesh, QR
npm run lint # ESLint
npm run rules # house rules: no emoji, no long dashes, no product names
npm run e2e # two browser profiles join by link, words and QR code, then draw, chat and call
npm run serve # http://127.0.0.1:8080/doodleduo/The site is plain static files in docs/ with relative paths, served the same way as GitHub Pages. Chat, calls, the mesh and the noise suppression are adapted from howlden. Shared modules live in src/ and are copied to docs/app/core/ with npm run core. Styles use Monsoon and the interface is declared with Voodoo.js.
MIT, made by Alexandre Kohler.
