Alex Garbiak
← Notes

A FIRE calculator with a hand-rolled chart and no server

astrosvgfinance

The FIRE calculator projects a portfolio forward from your current age and savings rate to a “financial independence” number, and draws the result as a chart with UK pension-age reference lines. Like the FIRE tools it’s sitting next to in the Lab, everything — inputs, projections, saved scenarios — stays in localStorage. Nothing about your finances leaves the browser.

Compounding monthly, not dividing by twelve

The obvious way to turn “5% a year” into a monthly step is annualRate / 12, but that understates actual monthly growth because it ignores compounding within the year. The projection instead converts once, up front: Math.pow(1 + netAnnualRate / 100, 1 / 12) - 1, where netAnnualRate is growth minus fees. That monthly rate compounded twelve times reproduces the stated annual return exactly, so a 5% growth / 0.2% fee scenario actually nets 4.8% a year rather than something close to it.

The projection itself is a plain loop: for each year from your current age to a fixed end age (100), apply the monthly rate twelve times and add the monthly contribution each time, recording one point per year for the chart. The FI number is targetAnnualSpend / (safeWithdrawalRate / 100) — the standard “4% rule” shape, but generalized to whatever withdrawal rate the user sets rather than hardcoding 4. FI age is just the first year in the loop where the running balance crosses that number.

Scenarios that survive a corrupted localStorage

Up to five named scenarios live in one localStorage key, and switching between them swaps the whole form. Renaming happens inline: double-click a scenario pill and its button is replaced with a text input, committed on blur or Enter — no separate rename dialog.

Loading that data back in doesn’t just trust it. isValidInputs() checks every one of the eight expected numeric fields is actually a finite number before a stored scenario is treated as usable; anything that fails — a hand-edited localStorage value, a future version of this tool storing an incompatible shape — gets filtered out rather than crashing the page or silently rendering NaN fields. If every stored scenario fails that check, or the value is missing entirely, the code falls back to a single fresh “Base case” scenario with sensible defaults. Scenario names, unlike the reserving tutorial’s author-written step content, are genuine user input and get run through escapeHtml() before they’re interpolated into the pills’ innerHTML.

Drawing the chart by hand

There’s no charting library here — the <svg> is a fixed 640×340 viewBox, and every element in it (grid lines, the balance line and its filled area, axis labels, the FI target line, the pension-age markers) is a template string built from the same projection points, then dropped in with one innerHTML assignment per render.

A few details that came out of actually doing this by hand rather than configuring a library:

  • The y-axis headroom is maxBalance * 1.08, so the line never touches the top edge of the chart.
  • X-axis tick spacing adapts to the span being plotted — every 5 years for a short projection, 10 or 20 for a longer one — so a 15-year run to retirement and a 70-year run to age 100 both get a readable number of labels instead of either a cluttered axis or three widely-spaced ticks.
  • The UK pension-age (57) and State Pension-age (67) reference lines are filtered to the currently plotted age range before being drawn, so a scenario that starts at age 60 doesn’t render a stray “Pension access (57)” label off the left edge of the chart.

Ideas for later

  • Real UK tax treatment. The chart already reserves visual space for pension/State Pension ages; actually modeling ISA allowances, pension tax relief, and drawdown tax would turn this from an illustration into something closer to genuine planning — a much bigger scope increase than it sounds, and the disclaimer banner exists precisely because none of that is modeled yet.
  • Variable returns. A constant real return is the whole simplification this tool is built on; a sequence-of-returns-risk mode (a bad early decade, Monte Carlo bands) would show why FIRE plans built on one smooth line are optimistic.
  • Export/import a scenario as JSON, so a plan can be shared or backed up outside of one browser’s localStorage.