A web app for practicing Fermi estimation: the art of getting within an order of magnitude of any quantity by breaking it into a chain of guessable factors. Named for Enrico Fermi, who famously estimated the number of piano tuners in Chicago.
Unlike a typical "guess the number" quiz, Fermi scores your reasoning, not just your final answer — it tells you which factor in your chain sent you off course, and trains your calibration (how well your confidence matches reality) over time.
No build step. No dependencies. Open the file and play.
# Option A — just open it
open index.html # macOS (or double-click the file)
# Option B — run the tiny dev server (better; enables localStorage cleanly)
python3 server.py # then visit http://localhost:8000
# or
npm startRun the tests:
npm test # engine + units tests + headless UI smoke test
# or
node test.js
node test_units.js
node smoke.jsBuild your estimate as a chain of factors that combine with real operators
(× ÷ + −), each carrying an optional unit. The app keeps a live running
total (with units) and compares your chain — factor by factor — against a
reference decomposition. It highlights the single biggest divergence:
"Your factor 'fraction owning a piano' (5 × 10⁻¹) was about 10× too high versus the reference (5 × 10⁻²). Fixing just this step would move your answer by ~1.0 orders of magnitude."
Worksheet rows are colour-coded green / amber / red by how far each factor is from the reference.
Operations & grouping without brackets. Rows multiply/divide by default.
When you need a sum-then-multiply like (a + b) × c, add a group: a tidy
sub-calculation whose rows all share one operator (sum / difference / product /
quotient). The group's result drops into the main chain as a single factor — so
you get real grouping with no bracket soup. (One level of nesting; the
calculator handles arbitrarily complex ( ) expressions for power users.)
Units via dropdowns (magnitude × unit). Each factor has two dropdowns: a
magnitude (×1, thousand, million, billion, percent, kilo, milli, micro…)
and a unit (grouped: length, area, volume, mass, time, speed, count, …),
plus an “Other…” escape for anything exotic (BTU, parsec). Picking
magnitude = million on a 20 makes the factor 2×10⁷ — no more silently
dropping the word “million”. The engine tracks SI dimensions, converts where
needed (a chain that works out to metres is compared against an answer stated in
km), and shows an advisory warning if your factors combine to the wrong
dimension — it never blocks you from submitting. Casing is forgiving for words
(Million, KM→km) but strict for ambiguous symbols (m=metre stays
distinct from M=mega, t=tonne from T=tera).
Instead of a point estimate, give a 90% confidence interval. You're scored with a log-scale interval score: tight intervals that still contain the truth score best; overconfident-and-wrong scores worst. Your hit rate is tracked on a calibration curve in Stats — a well-calibrated estimator contains the answer in ~90% of their intervals.
A floating 🧮 dock that understands estimation-friendly notation and units:
- Math:
3e6,3×10^6,3 x 10^6,5k,2.5M,2B,pi,sqrt(),log()(base 10),ln(), and ordinary+ − × ÷ ( ). - Convert:
12 ft to m,3 m^3 in L,100 km/h to m/s,1 mile -> km. - Unit products:
12 ft * 8 ft * 9 ft→ result in m³ (with the dimension shown).
Results show both scientific and human-readable forms, with a reusable tape. A 📚 button drops reference values straight into the calculator.
Stuck on "how big is a marble?" or "what's the population of India?" Hit 📚 Lookups (on the worksheet or the calculator) for a searchable library of ~90 common reference quantities — object sizes, room/building dimensions, the human body, populations, time, speeds, physics constants, money. Click any entry to insert it as a worksheet factor (value and unit) or into the calculator.
Practice mode serves an unlimited stream of generated problems from parameterized templates (packing, per-capita services, daily consumption, line-ups), each with its own reference chain and hints. The generator is seedable, which powers…
Daily mode gives everyone the same problem each day (deterministically generated from the date). After solving, copy a Wordle-style result:
Daily Fermi 2026-06-19 🟢
Within 0.4 orders of the answer
Hints used: 💡
Streak: 5🔥
Every problem reveals a full worked decomposition with notes and a source
(Fermi, Weinstein's Guesstimation, Mahajan, Santos…). Daily wins build a
🔥 streak; Stats tracks accuracy, calibration, best streak and recent
results. All progress persists in localStorage.
| File | Purpose |
|---|---|
index.html |
App shell (loads the scripts in order). |
styles.css |
Dark, mobile-first theme. |
units.js |
Dimensional-analysis engine: parse/convert units, combine dimensions. |
references.js |
Searchable library of ~90 common reference quantities (the cheat-sheet). |
problems.js |
Hand-curated bank of classic problems with reference steps. |
generator.js |
Seedable procedural generator (templates → problems). |
engine.js |
Pure logic: OOM math, unit-aware grouped chain scoring, calibration, storage, share text. |
app.js |
UI controller wiring all views and modes (no framework). |
server.py |
Zero-dependency static dev server. |
test.js |
Unit tests for the engine + generator. |
test_units.js |
Unit tests for units, conversions, grouped chains, and lookups. |
smoke.js |
Headless DOM harness that drives app.js to catch runtime errors. |
Each problem (static or generated) has the same shape. The key part is steps,
where every input factor is tagged with an op:
{
id, title, question, unit, category, difficulty, answer, tolerance,
hints: [ "…", "…", "…" ],
steps: [
{ label: "Population of Chicago", value: 3e6, unit: "people", op: "mul" },
{ label: "People per household", value: 3, unit: "", op: "div" },
{ label: "Households", value: 1e6, unit: "", op: "sub" }, // subtotal
{ label: "Fraction owning a piano", value: 0.05, unit: "", op: "mul" },
// …
{ label: "Piano tuners", value: 125, unit: "tuners", op: "sub" }, // final
],
sources: [ "Enrico Fermi (classic), University of Chicago" ],
}op: "mul"— an input factor multiplied into the chain.op: "div"— an input factor divided into the chain.op: "sub"— a subtotal / intermediate (shown to the reader, excluded from the factor product).
The reference answer is reconstructed by combining the mul/div inputs, which
is also what the unit tests assert (engine.referenceProduct).
Append an object to the array in problems.js. Make sure the mul/div inputs
multiply out to your stated answer (the test suite checks this). Then:
node test.js # confirms every reference chain reproduces its answerThe books these problems are drawn from, if you want more:
- Guesstimation & Guesstimation 2.0 — Lawrence Weinstein
- Street-Fighting Mathematics & The Art of Insight in Science and Engineering — Sanjoy Mahajan (free PDFs from MIT)
- How Many Licks? — Aaron Santos
- The calculator evaluates expressions in a whitelisted scope (only known math names; rejects anything else) — fine for local/single-user use. If you deploy this multi-tenant, consider a dedicated expression parser.
smoke.jsships a tiny purpose-built DOM (not a real browser); it exists to catch runtime wiring errors in CI without a heavyweight dependency.- State lives in
localStorage, so progress is per-browser.
MIT licensed. Have fun estimating.