Build Whimsical
YESreplaces $12/mosaves $144/yrback to the verdict
An SVG canvas with pan and zoom, boxes you can type into, connectors that re-route when you move a node, one-key auto-layout for tree and flow diagrams, and PNG and SVG export that is pixel-honest at any zoom level. Documents live in the browser and export to a file you own.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs Vite and the build. Nothing ships to a server, but the tooling needs it.
Get it Download the LTS build from nodejs.org, or install with nvm. open ↗
Verify
node --version prints v22 or higher - installfree
Why This is a browser application, and the SVG inspector is how you will debug geometry.
Get it Chrome, Firefox or Safari, all current. Test in at least two, because SVG text metrics differ subtly.
Verify
You can inspect an SVG element and see its computed bounding box - installfree
Why The export check in Phase 5 is meaningless if the only thing you open the file in is the browser that made it.
Get it Inkscape is free and strict, which makes it a good test. A design tool you already have works too. open ↗
Verify
You can open an SVG file and see whether its fonts survived - decidefree with an open licence
Why Embedding a font in an exported file is redistribution, and not every licence permits it.
Get it Pick an open licensed family such as Inter or Source Sans, and keep the licence file alongside it. Or convert text to paths on export and avoid the question.
Verify
The licence permits embedding, or your export outlines text instead
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- docs(id, title, created_at, updated_at) - nodes(id, doc_id, kind, text, x, y, w, h, fill, stroke) · kind is box, ellipse, diamond or sticky - edges(id, doc_id, from_node, to_node, from_port, to_port, label, waypoints_json, style) - Ports are named anchors on a node, not absolute coordinates, so moving a node cannot detach an edge. - Selection, hover and the current tool are ephemeral view state and never persisted.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
VITE_APP_TITLE | optional | Diagrams | Shown in the page title. Cosmetic. |
VITE_SHARE_ENDPOINT | optional | https://share.example.com/upload | Product phase only. Where shared documents are uploaded when they are too large for a URL. Leave unset to disable sharing. |
VITE_MAX_SHARE_URL_BYTES | optional | 8000 | Above this the share switches from a link to an upload, because very long URLs break in real clients. |
The build, in order
Canvas, pan, zoom and a node
You can create a box, move it, type in it, and the geometry is correct at any zoom.
One svg element, one root group, and a viewBox. Write screenToCanvas and canvasToScreen as the only coordinate conversion in the codebase.
Files
src/canvas.tsterminalnpm create vite@latest . -- --template vanilla-ts
Pan on space-drag or middle-drag, zoom on wheel anchored at the cursor position so the point under the pointer stays put. That anchoring is what makes zoom feel right.
Files
src/viewport.tsClick to create, drag to move with pointer events, double click to edit text using a foreignObject or an overlaid input positioned through canvasToScreen.
Files
src/node.ts
done when · tick each as it passeswatch out- Zooming with a CSS transform is easier for about an hour, and then every export, every hit test and every text position is off by the scale factor. Drive pan and zoom through the viewBox from the start so what you see is literally the SVG you will export.
Connectors that stay attached
Edges connect to nodes rather than to coordinates, and moving a node re-routes rather than breaking.
Four ports per node to start. When rendering, choose the port pair that gives the shortest sensible route rather than always using the same sides.
Files
src/ports.tsA path that exits the port at right angles, turns at most twice for the common case, and rounds the corners slightly.
Files
src/route.tsDeleting a node cascades to its edges. Write the test that deletes a node and asserts no orphan edge remains.
Files
src/doc.ts
done when · tick each as it passeswatch out- Storing an edge as a pair of coordinates works until the first time someone moves a node, and then every diagram in every saved document is subtly wrong. Store the relationship and derive the geometry.
Automatic layout
One key press turns a tangle into a readable diagram.
Nodes with widths and heights, edges with sources and targets. Its input shape is close to yours, which is why it is worth using.
Files
src/layout.tsterminalnpm install elkjs
Compute all positions, then commit them in a single history entry so one undo restores the previous arrangement.
Files
src/history.tsAfter layout, assert no two node rectangles intersect. This catches configuration mistakes that look almost right.
Files
src/layout.test.ts
done when · tick each as it passeswatch out- Running layout automatically after every edit makes the canvas feel possessed, because a node you just dragged jumps somewhere else. Make it an explicit action with an undo step.
Text that exports the way it looks
Wrapped labels look identical in the browser and in the exported file.
Use a hidden SVG text element and getComputedTextLength, or a canvas measureText with the identical font stack. Guessing from character counts fails on proportional fonts.
Files
src/text.tsBreak on word boundaries, position each tspan with explicit x and dy, and return the total height.
Files
src/text.tsNode height is derived from the laid-out text height plus padding, so a long label is never clipped.
Files
src/node.ts
done when · tick each as it passeswatch out- A foreignObject with an HTML div wraps text beautifully in the browser and is ignored or mangled by most SVG consumers, including Inkscape and many design tools. If you use foreignObject for editing, convert to positioned tspans for rendering and export.
Export and local documents
Diagrams leave the application as files that open correctly somewhere else.
Compute the bounding box of all content, set the viewBox to it, inline the styles, and embed or outline the font.
Files
src/export-svg.tsDraw the serialised SVG into a canvas at the requested scale and export a blob. Verify the pixel dimensions rather than trusting the scale parameter.
Files
src/export-png.tsIndexedDB for the working set, plus save to file and open from file so a document survives a cleared browser.
Files
src/store.ts
done when · tick each as it passeswatch out- An exported SVG that references a web font renders with a fallback font on any machine that lacks it, which usually means every machine but yours. Embed the font as a data URL or convert text to paths, and check by opening the file in a different application.
Sharing, and being honest about collaborationproduct builder
Other people can see a diagram, and you have not pretended to build multiplayer.
A compressed encoding of the document. If it exceeds a sane URL length, upload it instead of producing a link nobody can paste.
Files
src/share.tsSame rendering code, no editing affordances, no keyboard shortcuts that mutate.
Files
viewer.htmlOne sentence in the interface and one in the README. Silence here reads as a promise you did not make.
Files
README.md
done when · tick each as it passeswatch out- Real-time collaborative editing needs conflict-free replicated data types or operational transforms plus presence and reconnection handling. A naive last-write-wins sync over a socket looks like it works in a demo with one person and destroys work with two.
That is the whole plan for Whimsical. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Real-time collaboration and presence, which is why teams buy the paid plan.
- The template library and the wireframe component kits.
- Document embedding and workspace integrations.
- Comment threads and review workflows on a diagram.
- templates
- multiplayer polish
- docs integration
- wireframe components
- comments
- sharing permissions
- Sticky notes and freehand annotation, which need no new architecture
- Diagram as code import, so a text description generates a first draft
- A continuous integration check that renders fixture documents and diffs the output
Need the files? The project pack on the verdict page hands your agent the whole brief · more diagrams.