I wanted to build a small thing. One page. It answers a question a child could ask: how big is a billion dollars, physically, if you printed it all in $100 bills and put it somewhere.
I thought it would take a weekend. It did not.
The question is simple. The answer is simple too, actually. One billion dollars is 10 million $100 bills. They weigh 10 tonnes and cover about 103,000 square metres laid flat. Two divisions and two multiplications. Done.
But "here is a number" is boring. I wanted people to feel the size. And the moment you try to make a number feel physical, you stop building a calculator and start building four different things at once. This is the story of those four things.
Problem one: the whole money model is four numbers
Before any code, I needed the ground truth. How big is a $100 bill, really?
I reduced the model to four physical inputs, based on measurements and estimates published by the Bureau of Engraving and Printing:
about 156 by 66.3 millimetres (the face)
about 0.1094 millimetres thick
about 1 gram per note
Every money measurement on the page starts with these four inputs. Area laid flat, height of a stack, weight on a scale, length end to end. All of it is those numbers multiplied by "how many notes do you have." The city, landmark and vehicle comparisons use separate reference data, but the money itself is just these.
This was the first lesson. When your core numbers rest on a few physical constants, those constants need to live in exactly one place in the code. Not copied into the road renderer, and again into the scale table, and again into the FAQ text. One source of truth, imported everywhere. Because the day you refine the thickness value, you want to change it once, not hunt it across the codebase.
Sounds obvious. I still got it wrong the first time. Two parts of the code used slightly different thickness values, so two stack-height readings on the same page disagreed by a few percent. I could not figure out why for an hour.
Problem two: a road made of money you can actually drive on
Here is where the weekend died.
A static number does not land. "10 million notes" means nothing. So I built a road. Every tile on the road is one $100 bill. You pick a vehicle, a sports car, a tractor, an F-35 jet, and you drive along the money using the arrow keys. The distance you cover is real: put the notes end to end and one metre of road is worth $641.
The naive version is easy and immediately dies. You cannot render 10 million DOM elements. You cannot even render 100,000. The browser falls over long before you reach interesting amounts of money.
So the road runs on WebGL, through Three.js. The trick that makes it work is that you never build the whole road at all. You render one fixed stretch of it. Its texture scrolls beneath the vehicle through a UV offset, so the ground appears to move. A reusable pool of trees, posts and roadside signs is wrapped back ahead of the camera once they fall behind you. Nothing is created and destroyed on every frame. The money is "infinite" the way a treadmill is infinite. The belt moves, you stay roughly in place, and the same handful of objects keep getting recycled to the front.
The vehicles were their own rabbit hole. Each one has a distinct configured top speed, so the tractor genuinely crawls while the F-35 moves far faster than the cars. These are game-style limits, not spec sheets. A sports car is capped at 200 km/h here even though a real one goes faster. The jet does not drive on the road, it hovers above it with a procedurally generated model, because a fighter jet rolling down a paper highway looked stupid and a flying one looked right. Small detail. Took an evening.
The thing I underestimated was keyboard driving. Holding the up arrow to accelerate, down to brake, feels trivial until you handle the edges: the road losing focus, multiple keys held at once, or the player restarting while accelerate is still pressed. Restart has to reset distance, speed, the wheel animation, the held inputs, and the milestone popups, all together, or you get ghosts. A car that keeps rolling after you reset it. I found most of these by feel, driving the road at 2 a.m. and noticing something was slightly off.
Problem three: keep the numbers canonical, convert only to display
The driving section can switch between metric and imperial units. You flip a switch and kilometres become miles, the speed and distance and the travel-time table all update together.
The obvious way to build this is the wrong way. If you store the displayed value and write it back, you drift. Store "1.56 km," convert to 0.97 miles for display, then let the user flip back and you convert that rounded 0.97 into km again. Every round trip that writes back a rounded number loses a little precision. Flip enough times and the value visibly wanders.
The fix is a rule I now apply to everything with units. Keep one canonical value and convert only at the last moment, when you format it for the screen. The road simulation integrates distance in metres and speed in metres per second. The kilometre and the mile are both just views of that stored number. Switching units never writes back to the underlying value, so it cannot drift. You can flip the toggle a thousand times and the numbers match the first flip exactly.
It is a boring rule and it saves you from a whole category of "why are the numbers slightly wrong now" bugs that are almost impossible to reproduce on purpose.
Problem four: a crawler cannot drive a car
Here is the tension in the whole project. The interesting part of the page is interactive and lives in WebGL. A search crawler may execute JavaScript, but it cannot reliably extract meaning from a WebGL canvas or press the arrow keys the way a person would. To a crawler, a beautiful interactive scene can look like a blank rectangle.
So the page is built in two layers. The real answer, all of it, exists as plain server-rendered HTML. The measurements, the comparison table, the "here is a billion versus a million" section, the questions and answers at the bottom. Every number a person or a crawler could want is in the HTML before a single line of WebGL runs.
Then the interactive road and the drag-to-scale widget layer on top, for humans with a browser and a GPU. If the JavaScript never loads, you still get a complete, readable, correct page. If it does load, you also get to drive a jet along a road of money.
This is the part I would tell anyone building visual, interactive content. The fancy layer is a progressive enhancement, not the foundation. Build the boring, complete, server-rendered version first. Then make it fun on top. Not the other way around, because the other way around is invisible to a big part of the internet.
What it actually is, underneath
Strip away the driving and the sliders and the money model is almost embarrassingly simple. A few physical constants. A pile of multiplication. Some comparisons to things people already have a feel for, a bath towel, a bus, the Burj Khalifa, the area of Paris inside its ring road.
The engineering was never in the math. It was in making the math felt: rendering a road that cannot fit in memory, driving it without jank, keeping units honest, and making sure a machine that cannot see any of it still gets the full answer.
If you want to look at the thing, it is here How big is a billion dollars. Pick the F-35, hold the up arrow, and go find the trillion-dollar sign. In single-row mode it is about 33.7 days away at the jet's top speed. The supercar would need about 162 days, and the 200 km/h sports car about 325. Which is its own kind of answer to the original question.
I am still building the rest of the site around this, more visual ways to look at money and its history. If you spot something broken, or you have built infinite-scrolling WebGL worlds and know a better trick than the one I used, I want to hear it. This is the first project I have put in public, and before this the only audience for my side projects was me and my fish.

Top comments (0)