workshop private

← all games

Good Roller

games · created 2026-10-09

A hump yard where gravity does the pushing and two retarders are the only things you own. A 1% grade is worth exactly 20 lb per ton, the bowl falls at 0.12%, and whether that is downhill or uphill depends on a number about the car that nothing in the yard can measure.

physicssimulationcanvasgame-feel

A yard engine shoves a cut of eight over the crest at 3.2 mph. Past the crest each car uncouples and goes down the hill on its own, and there is no throttle on the far side of that moment — only two retarders, 370 feet apart, and the clock.

A car couples cleanly at 4 mph or less. How fast it will be going when it gets there depends on its rolling resistance, and rolling resistance is the one quantity in a hump yard that nobody can read off anything. You get the weight from the weigh rail and the speed from the radar. The number that decides the answer you have to infer, from how the car has been behaving, while it is already most of the way to the place it will be wrong.

The identity underneath all of it

Resistance is quoted in pounds per ton. A grade pulls with pounds per ton too:

a_grade = g·(grade/100)        a_roll = g·(R/2000)

which are equal when R = 20 · grade%. A 1% grade is worth exactly 20 lb per ton, and once you see it that way the whole yard reads as one number line:

gradepulls with
4.0% — the crest drop-off80 lb/ton
1.1% — through the master retarder22 lb/ton
0.7% — the ladder14 lb/ton
0.12% — the bowl2.4 lb/ton

The bowl’s grade is the interesting one, because 2.4 lb/ton sits below every car in the yard. A loaded hopper resists about 2.7; an empty gondola about 7 before its journals are even considered. So the bowl is very slightly uphill for all of them. Nothing runs away down a bowl. Things stop short in one, which is a different problem and a worse one, because the car then has to be fetched.

Why the empty is the hard one

The resistance model is Davis, in the form yards use it:

R = 1.3 + 29/w + 0.045·V + 0.0024·A·V²/tons        w = tons per axle

The 29/w term does nearly all the work, and it is divided by weight. A loaded hopper carries 32 tons on each axle and pays 0.91 lb/ton for its bearings. An empty gondola carries 6.3 and pays 4.64 for the same bearings. It is not a worse car. It is the same drag spread over a quarter of the tons.

tonsR at 8 mphmust arrive at the bowl doing
loaded hopper1282.702.8 mph
coil car1122.852.9 mph
empty autorack424.824.1 mph
empty gondola256.985.0 mph

Add the journal stiffness the demo hides from you — a wider draw on empties, because empties are the cars whose bearings and springs have the loudest say — and the real spread at the bowl throat is 3.0 mph for a 128-ton loaded hopper and 6.4 mph for a 25-ton empty gondola, with 270 ft of clear track in front of both. Twice the speed, through the same sixty feet of retarder, for two cars told apart by a number no instrument in the yard can read.

Two levers, and they are not doing the same job

F — the master retarder, 140 ft of shoes starting 150 ft past the crest. J — the tangent retarder, 60 ft at the bowl throat, after every switch. H holds the pusher. That is the whole input surface.

The instruments are two marks on each speed tape, and they are different kinds of thing:

And here is the thing the yard is built around. For a loaded car in the master retarder, the needle reads zero. Not “slow down” — zero, as in there is no speed slow enough, because the 230 ft of 0.7% ladder waiting below hands a good roller back more than a coupling is allowed to have. The first brake cannot meter that car at all.

What it can do is keep it under the ceiling. Measured at the master retarder’s exit, that ceiling is:

LOADED HOPPER       128 t   leave under   3.9 mph
COIL CAR            112 t   leave under   4.8 mph
LOADED BOXCAR        97 t   leave under   5.8 mph
EMPTY GONDOLA        25 t   leave under  16.8 mph

So the first lever makes a capacity decision — get it slow enough that the second lever can still finish — and the second makes a precision decision. On an empty, the ceiling is so far above anything the car will ever be doing that the master retarder is simply not needed; on a loaded hopper it is the only thing standing between you and a car that cannot be saved. You can tell which is which from the weight, which you are given. That is the one gift in the game.

Where the information is, versus where the decision is

The estimate of R comes out of energy in minus energy out over the distance rolled, with a radar good to 0.45 ft/s and a fixed error per car. Its uncertainty falls as 1/distance, so, measured over twelve cuts at the moment each car arrives at each lever:

                                       σ quoted      error actually made
the master retarder, station 150 ft    ±2.79 lb/ton      1.47 lb/ton
the tangent retarder, station 520 ft   ±0.75 lb/ton      0.53 lb/ton

That is the shape of the whole thing. The lever that makes the decision nothing downstream can undo stands where the car has rolled 150 feet. The lever with a good reading is 370 feet too late to change the answer. Every retarder yard ever built is an argument about this gap, and the usual answer — build a third retarder further down — just moves it.

What the reference operator gets

A bang-bang operator, master riding 92% of the ceiling and tangent riding the needle, over twelve seeded cuts of eight:

never holding the hump      237s +  21s = 257s    6.1/8 clean
holding for gap             276s +   3s = 280s    7.7/8 clean
...and a resistance gauge   276s +   5s = 280s    7.6/8 clean

Two things in there are worth the trouble of having measured them.

The hurry wins on the scoreboard. Holding the pusher to keep gap costs 40 seconds of clock and saves only 17 seconds of penalty, so running the cars close is 22 seconds ahead — and buys that with 1.6 more cars a shift ending up somewhere they have to be fetched from. Which of those two numbers a yard is graded on is not a physics question, and the game does not settle it for you.

A perfect resistance gauge is worth nothing here. Not because the estimate is good — at the master retarder it is out by 1.5 lb/ton on average — but because by the lever that actually sets the coupling speed it is out by 0.5, and everything lost after that is lost to a lever that is only ever fully on or fully off, and to the cut in the bowl creeping toward the throat while the car is still in flight toward it. The expensive instrument would fix the error you can already live with.

Run node scripts/measure.mjs and every number on this page comes back out of src/yard.mjs.

The other ways it goes wrong

What’s under it

src/yard.mjs is the engine: framework-free, headless, no canvas anywhere in it. Feet, seconds, pounds-force, short tons. The physics is one dimensional along the track — the ladder’s lateral weave is drawing, not simulation — and within any one grade segment both accelerations are constant, so the projections (coast, requiredSpeedAt, capacitySpeedAt) are closed form rather than integrated. That is what makes it cheap enough to run the whole prediction band twice a frame for every car in flight.

src/render.mjs is the drawing. The plan view stretches the lateral axis about eight times, the way every yard diagram ever printed does, because the real thing is a thousand feet long and ninety feet wide and nothing about the physics is lateral.

Reuse

Gotchas