Build a Miro alternative
Build a Miro alternative as sticky notes on a workshop board. Live cursors, an infinite canvas, and hundreds of editors are the other product.
You can build a Miro alternative as sticky notes on a workshop board. The catalog calls Miro that loop, notes and voting dots on one infinite-ish canvas.
You pay $10–$20 a person a month. The card says that price scales with the team. The verdict is “For workshops, absolutely”.
Last time I wrote about building a Toggl alternative. That was a regular backlog post. This one is a teardown.
Build a Miro alternative as a workshop board
The Miro page splits the work into three rows.
The first row is 12 hours. A canvas, sticky notes, pan and zoom. That is a board, colored notes with x and y, and a transform on the page. You can vibe-code this.
The second row is 1 week. Live cursors, dot voting, frames, exports. Still one workshop. Named people, a timer, a PNG of the finished board.
The third row is 3 months and up. Freehand drawing, diagrams, hundreds of simultaneous editors. Skip that row.
If you tell an agent to “clone Miro”, it aims at the third row. The useful v1 is the first one.
Here is the shape of the data. Nothing else has to exist at 12 hours.
const board = {
id: 'board-retro-oct',
name: 'October retro',
}
const note = {
id: 'note-keep-deploy',
boardId: 'board-retro-oct',
type: 'sticky_note',
content: 'Keep the Friday deploy window',
fillColor: 'light_yellow',
x: 240,
y: -80,
parentId: null,
}
A board, notes, later a vote row per person per note. Frames wait until you have two sections that should not mix. Cursors wait until a second facilitator is in the room. Export waits until someone asks for the photo.
The audience line on the page is a facilitator running remote retrospectives and planning workshops. If that is you, one board of notes is enough.
The same v1 also replaces Mural, FigJam, and Lucidspark if what you wanted was “people put notes, then we vote”. Figma is the wireframe cousin. I already wrote the Figma teardown. Lucidchart is the boxes-and-arrows cousin at $9–$11 a person. Canva is locked templates, not a workshop.
How does Miro work
Miro publishes a REST API, a Web SDK, and a few engineering posts about the board process. I am summarizing those pages, not reconstructing today’s cluster.
The boards guide is the public picture of the canvas. Boards are zoomable and infinite. You pan and scroll as long as you want. Item x and y mark the center of the item, relative to the center of the board. That center is x: 0, y: 0. Create a note with no position and it lands on the origin.
A child inside a frame uses a different origin. Its x and y mark the child’s center relative to the top-left of the parent. Move the frame and the children keep their local layout. The REST create-sticky-note endpoint is POST /v2/boards/{board_id}/sticky_notes. You send data.content, an optional shape of square or rectangle, a fillColor from a fixed list, and a parent.id when the note sits in a frame. A frame can hold at most 5,000 children. Pass null to attach the note to the canvas.
A 12-hour v1 copies that shape, not the endpoints. One board. Notes with center coordinates. No frames yet.
function moveNote(note, x, y) {
return {
...note,
x,
y,
}
}
Validate x and y on the server. The catalog guardrail already says that. Cap how many notes a board can hold.
Listing objects is cursor pagination. The get-items call is GET /v2/boards/{board_id}/items. You can filter type to sticky_note, frame, shape, image, card, and a few others. Limit sits between 10 and 50. A v1 can SELECT the notes for one board. No cursor until you have more than a page.
Rate limits are per user per application, measured in credits. Level 2, which is the sticky-note write, costs 100 credits and allows 1,000 requests a minute. The global cap is 100,000 credits a minute. Over that, you get 429 with code: "tooManyRequests". A v1 does not need their credit table. One write per drag-end is enough. Do not persist every pointer move.
The Web SDK events guide splits what you can listen for. Board UI events cover drop and selection. Timer events cover start and stop. Collaboration and session events cover “someone joined”. Realtime events are ones you send to the other tabs. Experimental item-update events exist, and they may change. Treat “the hosted product uses this same event list internally” as an educated guess. The public SDK is for apps on top of a board, not a dump of Miro’s own protocol.
They tried board webhooks and then pulled them. The December 2025 changelog retired /v2-experimental/webhooks/board_subscriptions. Miro said the need for board activity is real, and that this architecture was not reliable enough to keep. A v1 should persist first, then broadcast on the same request. Do not wait for a webhook you do not have.
How Miro keeps a board live
The skip row starts when two people drag the same note.
A May 2020 concurrency post is the public picture of the board process. They said about 100 production servers, 6,000 HTTP API requests a second, and more than 50,000 WebSocket commands a second. When someone opens a whiteboard, a state is created on one server. Hazelcast stores which server owns that board. New connections go to that owner. An acceptor thread binds the socket to the board state. A network thread reads packets. A business thread runs commands in order, so the application code does not lock every write.
A March 2020 database post adds the persistence numbers from the same period. They wrote every change to a database, about 20,000 requests a second. Redis sat at 80,000 to 100,000 writes a second at peak. The backend was a stateful Java monolith with a persistent WebSocket per client. Treat “the 2026 cluster still pins a busy board to one owner process” as an educated guess. The product rule is public: one place serializes the board, then it writes, then it fans out.
They are still talking about those sockets. A KubeCon Europe 2025 talk is about long-lived TCP connections in Kubernetes: rebalancing, draining, graceful shutdown. The canvas stayed stateful. The hosting model moved.
A v1 does not need Hazelcast or a Java cluster. One Durable Object per board is the catalog stack. Members of board-retro-oct connect there. Persist the note, then broadcast the same payload.
async function applyMove(board, { noteId, x, y, clientKey }) {
if (board.seen.has(clientKey)) return board.notes[noteId]
const note = moveNote(board.notes[noteId], x, y)
board.notes[noteId] = note
board.seen.add(clientKey)
await board.save(note)
await board.broadcast({ type: 'note.moved', note })
return note
}
Keep clientKey stable. If the socket drops mid-drag, a retry does not write the same move twice.
The collaboration namespace is experimental even on their SDK. Sessions, follow-the-facilitator, remote viewport control. Only one app can run concurrent sessions on a board. A person can sit in one session at a time. That is the hard half. Presence, attention, and a room that stays correct when a packed retro joins.
Freehand drawing and connectors are the rest of the skip row. Lucidchart is the product if the rent is attached arrows. The catalog non-goal already names arbitrary drawing, diagrams, embeds, a template marketplace, and hundreds of simultaneous editors.
Dot voting is a table, not a canvas problem. One row per participant per note. The Canny page already does one vote per person on a status column. Reuse that rule. Do not invent a second vote when they click again.
A Miro alternative for one workshop
I would rebuild Miro for the retro I already run on Friday. Four frames if I need them later: Keep, Drop, Try, Questions. Yellow notes. A 25-minute timer. Three votes each. Export a PNG when we stop.
Cursors come after a second person talks over a note I cannot see. Frames come after notes from two topics sit on top of each other. I would not draw a diagram. I would not host a company all-hands on this board.
Host it next to the product, on Node.js and Astro, with Alpine on the canvas and one Durable Object per board, because that is the stack on the card. Use TanStack Start if React is your weekday language. The hosting post already says Durable Objects wait for a coordinated room, which is a workshop board. Persist in D1. Cap the object count.
If the rent is a wireframe, that is Figma, 4 days for shapes on a canvas. If the rent is a finished graphic, that is Canva, 3 days for locked templates. If the rent is a flowchart, that is Lucidchart, 10 hours for boxes and attached connectors.
Start with the first row
Go to Build your own Miro. Copy the Astro prompt, or TanStack Start if that is what you write.
Tell the agent the first estimate only: canvas, sticky notes, pan and zoom. Twelve hours.
If the company already lives in a shared infinite board and a template library, keep paying.