Flagship · November 2025
Ledger Loom
A double-entry budgeting engine that draws its own balance sheets as living woven diagrams.
Why it matters
Personal-finance tools either bury you in spreadsheets or hide the math behind cheerful dashboards that lie by omission. I wanted something honest: every dollar visibly belongs somewhere, and the picture updates as you reconcile.
What I built
A browser-local double-entry ledger — no account ever silently nets to zero — with an SVG "loom" view where accounts are warp threads, transactions are weft, and the weave thickens where money actually moves. I wrote the reconciliation flow, an importer for OFX/CSV, and a deterministic SVG layout engine that animates only on data change, never on idle.
What I learned
Double-entry is the right primitive even for a "personal" tool — it makes whole classes of bug impossible to hide. Hand-authoring an SVG layout engine taught me more about coordinate systems than any tutorial. And "delightful" animation is a liability the moment it fires when nothing has changed.
Ledger Loom started as a spite project. I’d just spent twenty minutes arguing with a budgeting app that insisted I had $4.00 more than I did, with no way to find out why. The number was authoritative and unaccountable at the same time — the worst combination. So I rebuilt the thing I wanted: a ledger where the math is the interface, not a varnish over it.
The “loom” view is the part people remember, but the part I’m proud of is underneath it. Every transaction is two postings that must sum to zero. The renderer never invents a layout — it reads the ledger and draws exactly what’s there, which means the diagram is a proof, not a decoration. When a thread frays in the weave, that’s a real imbalance you can click into.
It runs entirely in the browser. Your numbers never leave the machine, the importer is forgiving about the genuinely cursed CSV exports banks produce, and the whole thing works on a plane with the Wi-Fi off. That last constraint shaped more decisions than any visual one.