Vibe code your own Figma
Vibe code your own Figma as a single-player drawing tool. The multiplayer document and the WASM renderer are what you pay for. Here is the public split.
You can vibe code your own Figma as a drawing tool. Rectangles, text, a frame the size of a phone. The catalog calls Figma a small vector canvas for wireframes, minus multiplayer everything.
You pay $16–$55 a person a month. The card says that price scales with the team. The verdict is “Single-player on purpose”.
Yesterday I wrote about an open source Statuspage alternative. That was a regular backlog post. This one is a teardown: what is public about the architecture, which half you can vibe-code, and which half you should leave alone.
Vibe code your own Figma as a canvas
The Figma page splits the work into three rows.
The first row is 4 days. Shapes, text, select, move, undo on a canvas. That is a scene graph and a pointer. You can vibe-code this.
The second row is 2 weeks and up. Frames, reusable components, autosave, PNG and JSON export. Still one person at a desk.
The third row is 3 months and up. Multiplayer engine, plugins, vector booleans, developer handoff. Skip that row.
If you tell an agent to “clone Figma”, 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 4 days.
const file = {
id: 'wireframe-checkout',
pages: [
{
id: 'page-home',
nodes: [
{ id: 'frame-mobile', type: 'frame', name: 'iPhone 14', x: 80, y: 40, width: 390, height: 844 },
{ id: 'rect-header', type: 'rectangle', parentId: 'frame-mobile', x: 0, y: 0, width: 390, height: 72, fill: '#171713' },
{ id: 'text-title', type: 'text', parentId: 'frame-mobile', x: 20, y: 24, content: 'Checkout' },
],
},
],
}
A page, a frame, a few nodes with stable ids. Groups can wait until you have dragged a stack of layers. Components wait until you have copied the same button twice. Export waits until someone asks for a PNG.
The audience line on the page is a product builder sketching low-fidelity web and mobile interfaces alone. If that is you, a local canvas is enough. If three people already live in the same Figma file, keep paying. The file is the source of truth, the same way Notion is for the notes.
How does Figma work: a tree of nodes
Figma’s REST API lets you fetch a file as JSON from https://api.figma.com. GET /v1/files/:key returns the document. Every layer is a node. Official types include DOCUMENT, CANVAS, FRAME, TEXT, and RECTANGLE.
That is the same tree Evan Wallace described in How Figma’s multiplayer technology works (October 16, 2019). A root, pages under it, objects under each page. Each object has an id and a bag of properties. He wrote it as Map<ObjectID, Map<Property, Value>>. Adding a feature often means adding a property.
The REST API is mostly a reader. You inspect nodes, export images, list versions, post comments. Figma’s API comparison is explicit: the Plugin API edits the file you have open, the REST API reads files from a script that is not sitting in the editor.
A vibe-coded canvas needs neither. One person, one tab, SVG, save to IndexedDB. Copy the product rule, not the endpoints: stable node ids, a parent pointer on the child, no executable values in imported JSON. The mini-prompt for import treats SVG and design JSON as hostile. Scripts, external fetches, NaN coordinates, a million-node bomb. Drop those on the floor.
Drawing is easy. Their renderer is not.
Wallace wrote Building a professional design tool on the web in December 2015, before the public launch. He said they built a browser inside a browser.
The editor is C++, compiled first with Emscripten to asm.js, later to WebAssembly (June 8, 2017). They measure load time as app init plus file download plus the first full render. After the WASM switch they said that number improved by more than 3x, across document sizes.
HTML, SVG, and the 2D canvas API were too slow to zoom and inconsistent for text. They wrote a tile-based WebGL renderer with their own compositor and text layout. All of that is in the 2015 post.
A 4-day v1 should use the browser. SVG for rectangles and text is fine. Canva on this site does the same thing for locked templates. You will not match Figma’s zoom on a huge file, and you do not have one.
Vector boolean operations sit on the skip row for a reason. Union and subtract on real paths is a geometry library. I have not seen Figma publish their boolean kernel. Treat “we will just clip two paths” as an educated guess that will eat the week.
The file is a replica
I am summarizing what Wallace wrote in 2019, not reconstructing their protocol. Some server details are from 2018 and 2019. Figma has not published a later spec I can find, so treat the process model as possibly dated and the rules as the part that still matters.
Clients talk to a cluster of servers over WebSockets. Open a document and you download a copy. After that, updates go both ways on the socket. Go offline and you keep editing. Come back, download a fresh copy, reapply the offline edits, reconnect. The hard work is updates on a document that is already open.
They skipped operational transforms. OTs are built for long text. Figma is a tree of properties. They looked at CRDTs, then declined to run a true CRDT, because CRDTs assume no central authority and Figma has one. The server decides the order of events.
A conflict is the same property on the same object. Fill on two different rectangles does not conflict. Fill and x on one rectangle do not conflict. The same fill on the same rectangle: last write to the server wins. No client timestamps. The server is the clock.
They are fine with last-write-wins on text. If the string is B and one person types AB while another types BC, the file ends as AB or BC, never ABC. Wallace said that out loud. Figma is a design tool, not a text editor.
The client applies your change immediately. Incoming server changes that collide with an unacknowledged local write are dropped, so the rectangle does not flicker back to the old fill while your write is in flight.
function applyRemote(local, change) {
const pending = local.unacked[change.objectId]
if (pending && pending.property === change.property) return local
const current = local.objects[change.objectId]
return {
...local,
objects: {
...local.objects,
[change.objectId]: { ...current, [change.property]: change.value },
},
}
}
That is a sketch of the idea, not Figma’s code. Catch the unacked write. Ignore the stale fill. Replace one property. Offline replay and a laptop that has been closed for a month belong on the 3-month row.
Linear made the same split. The tracker is a weekend. The sync engine is the other product. Notion is the same story with blocks.
Parenting and order are the nasty cases
Reparent by writing a parent id on the child. Do not delete and recreate the node. Identity has to survive a drag onto another frame, or a concurrent fill on that node disappears.
Those parent links are just edges. Two clients can parent A under B and B under A at the same time. The server rejects a parent write that would cycle. A client can still hold an unacked write that will be rejected, so the node can vanish from the tree for a moment. They accepted that. Rare, temporary, not worth a smarter client.
Child order uses fractional indexing. Each child has a position in (0, 1). Sort by that number. Insert between two siblings by averaging. Parent and position update as one property, so a node never keeps the old index after it changes parent.
They do not store those positions as 64-bit doubles. Doubles run out of precision if you keep inserting between the same pair. The public write-up says they store arbitrary-precision fractions as strings in a base-95 alphabet, and the server assigns a unique position if two inserts land on the same spot.
function insertBetween(left, right) {
return (left + right) / 2
}
insertBetween(0.25, 0.5)
//0.375
Use real numbers in a 4-day v1 if you are the only editor. The day a second cursor exists is the day I would stop and keep paying Figma.
Undo is local. The sync engine is not.
Undo in multiplayer has no obvious meaning. Wallace’s test: undo a lot, copy something, redo back to the present. The document must not change. Redo is not “put my pixels back on top of what they did next.” Figma rewrites redo history at undo time, and undo history at redo time, so that loop stays clean.
Single-player undo is the 4-day problem you should actually solve. The mini-prompt is “Every edit carries its own antidote.” A move stores the old coordinates. A delete stores the subtree, the parent, and the layer index. A snap-assisted drag is one command. Group-move-undo has to put every child on the same pixel it left.
Comments look like part of the file. They are not. Wallace said comments, users, teams, and projects live in Postgres and sync on a different path. I would not build comments in v1 anyway. The non-goal list already drops them.
The first multiplayer server was TypeScript. In Rust in production at Figma (May 2, 2018) they moved the hot path into a Rust child process per document after a huge-file encode blocked a whole worker. Serialization got over 10x faster. That write-up is eight years old, so I will not claim they still spawn one process per file.
What I would actually rebuild
I would rebuild Figma for one person who currently draws boxes in Canva or a slide deck. Frames, rectangles, text, select, move, undo. Stable ids. Autosave that does not clear the dirty flag before the write commits. PNG export of a frame.
Skip multiplayer, plugins, boolean path operations, and a design-system library that publishes across files.
Local components are the 2-week feature I might add later. The mini-prompt is “Components that propagate without clobbering.” An instance is a reference plus a sparse override map. Change the master’s fill, keep the label you overrode. Reject a component that contains itself.
Miro is a different job. Stickies, votes, a workshop timer. Twelve hours for the first row. If the rent is a retrospective, build that.
I would not rebuild Figma for a company that already treats the shared file as the system of record. You will spend the 3-month row and still open the paid app on Monday.
Start with the first row
Go to Build your own Figma. Copy the AHA prompt, or the stack you already write.
Tell the agent the first estimate only: shapes, text, select, move, undo. Four days.
If three people already have the same file open, keep paying.