CanVas

Realtime collaborative rooms pairing a shared block-editor document with a shared Excalidraw canvas — join by room name, no accounts, synced live via Yjs over a Cloudflare Worker.

Services
Product DesignEngineering
Industries
RealtimeDev Tools
Tools
TypeScriptYjsCloudflare WorkersWebSockets
Highlight
A URL is the whole account system.
Live
co-canvas-web.vercel.app

Accounts are the tax you pay before the useful part

Two people want to think about the same thing at the same time. Between them and that stands a sign-up form, an email confirmation, a workspace, an invite, a permission model and a seat licence. Every one of those exists for a real reason and none of them is the thing anyone came to do.

Co-Canvas removes all of it. You type a room name to join it; if it does not exist yet, typing it creates it. The URL is the whole account system — sharing it grants access, presence cursors and all.

The room, from a name
Presence, live
Two surfaces, one state
A room is a name. Two surfaces, one document, no sign-up.

Two surfaces, because one would be a bad version of both

The obvious build is a single canvas that also accepts text. It is obvious and it is wrong, and the README says why in one line: "Text and flows, canvas is spatial — so cramming both into one editor makes a bad version of each."

So a room is a BlockNote document and an Excalidraw canvas, bound together and synchronised as one. Different people can be on different surfaces at the same time, which turns out to be how pairs actually work — one writing while the other draws, both watching the same room.

Each room slug maps to a Cloudflare Durable Object holding the Yjs document, so a room survives with zero connected clients.

Sync
Yjs CRDTsy-partyserverCloudflare Durable Objects
Surfaces
BlockNote documentExcalidraw canvasPresence cursors
Access
No accountsNo passwordsThe URL is the grant
Stack
Next.js on VercelCloudflare Workerpnpm workspaces
CRDTs mean no merge conflict
The document persists in the DO
Anyone with the link

01 / 03

What it cost

Durable Objects are the design, not the deployment. A room that has to survive with nobody in it needs somewhere to live, and the choice between "a database row you load and save" and "an object that simply stays" decides the shape of everything above it. Picking the second made presence, persistence and the room lifecycle one problem rather than three.

The two-surface binding was the fiddly part. Yjs handles the merge; what it does not handle is two editors with different ideas about what a document is, sharing one awareness channel so that a cursor on the canvas and a cursor in the text read as the same person.

Where it does not work yet

The trade is stated plainly rather than hidden: no accounts means no recovery. Lose the room name and the room is gone, because there is no list of your rooms — there is no you. That is the correct trade for a scratchpad and the wrong one for anything you would be upset to lose, and the product should probably say so louder than it does.

The roadmap under docs/ is honest about what is next. This is a working thing rather than a finished one.