Bananas for Scale is a reference site. You look up a thing, get its real measurements, and see them restated in units nobody asked for. The International Space Station orbits at 408 kilometers, or about 2.3 million bananas end to end. A blue whale weighs 150,000 kilograms, or 300,000 fully loaded burritos. Every number is a ratio of two real SI values with the unit swapped.
There are 1,147 things, from a grain of rice to the Andromeda Galaxy, each with whatever measurements apply (length, mass, speed, volume). Against those sit 1,339 units. A golden retriever is 30 kilograms. Nicolas Cage is 84 kilograms. A Subway Footlong is 0.3048 meters. Each unit carries a singular and a plural ("1 golden retriever", "2.5 golden retrievers") and a dimension tag, so mass units only ever pair with mass measurements and length with length.
Picking the right wrong unit
Not every unit works for every measurement. A giraffe that is 0.000003 Eiffel Towers tall is a useless number. For a measurement \(m\) and a unit \(u\), both in SI, the converted value is \(v = m / u\), and I call it comprehensible if \(10^{-30} \leq v \leq 10^{30}\). The window is wide on purpose, so it only throws out the infinities and the zeroes.
Each measurement shows two or three conversions. The first two come from the comprehensible pool. With probability 0.6 there's a third, drawn from outside the window on purpose. Picks come from a seeded PRNG (Mulberry32, seeded with a djb2 hash of the thing's slug and the measurement id), so the same entry shows the same conversions on every load and the build is reproducible. The Remix button draws a new seed and repicks in the browser.
Static all the way down
A SQLite database holds the things, measurements, categories, and units. At build time Next.js reads it, runs the conversion for every measurement of every thing, and writes out 1,147 thing pages and 66 category pages. The database doesn't exist at runtime. The deployed site is a folder of HTML, CSS, JavaScript, and a search index on GitHub Pages.
Search works the same way. A build script writes a JSON index of names, categories, and aliases. The client loads it and runs MiniSearch with prefix and fuzzy matching, and names weighted above aliases and categories. The overlay shows up to eight results grouped by category.
The data problem
Curating 1,147 entries took more time than the code. A blue whale's length is easy to find, the mass of a cumulus cloud less so. Every measurement needed a real SI value from a source I could point at, which meant reading specifications and papers, then redoing the unit conversions by hand to check I wasn't off by a power of ten. Some things have one measurement. Others have five or six: length, mass, wingspan, top speed, cruising altitude.
The site covers things from subatomic particles to the observable universe, so the number formatter has to span the whole range. Above a million it writes "1.2 million" or "340 billion". Between 1 and 999,999 it uses comma separators. Small values get one decimal, and very small ones become "710 millionths" or "5 trillionths".