workshop private

← all creations

Egress

games · created 2026-09-27

You are the voice on the PA and the only dial you have says how hard everyone is trying — which moves their walking speed and how well they still follow a route, both at once. Underneath is a Helbing social-force crowd on an Eikonal floor field, and it disagrees with the poster. Wrecking the crowd's navigation costs nothing measurable (112 s against 115); over-urging costs 9.6%; and leaving the dial at a stroll costs 74%, so the instinct worth distrusting is politeness, not panic. The door decides everything else: the same hundred people clear 1.50 m in 25 s and 0.80 m in 123 s, because throughput goes as width^2.24 rather than linearly — a 2.4 m door carries 12.6x what a 0.8 m one does on 3x the width, which is also why splitting one opening into two doors of half the width is 1.8x to 2.8x slower than leaving it whole. And one bollard beats a bare doorway by 16.7% when it sits 0.6 m upstream and a full metre off the centreline, while 11 of the 36 places you could put it seal the room and nobody gets out at all.

physicssimulationcanvasgame-feel

You are the voice on the public address system. There is a room, there is a door, there is smoke coming in, and you cannot open a wall or carry anybody. You have one dial, and the dial says how hard everyone is trying.

That dial moves two things at once, and keeping them apart is the whole piece. Turn it up and people walk faster — desired speed goes from a stroll to a sprint. Turn it up and they also stop following the route: their heading drifts off the computed path and onto the straight line to the door they can see. Both of those are what a hurrying crowd does. Neither of them is obviously the bad one.

What is underneath

Two models, deliberately not talking to each other.

Where people want to go is a floor field. The room is cut into 10 cm cells, the Eikonal equation |∇T| = 1 is solved outward from every open door by fast sweeping, and the desired direction anywhere is −∇T, normalised. That is why a bollard is not a lump in the way: dropping one re-solves the field and changes what the entire room intends to do. Press F in the demo and you can see the thing itself.

What actually happens is the Helbing–Farkás–Vicsek social force model (Nature 407, 2000), at its published constants: A = 2000 N, B = 0.08 m, body spring k = 1.2 × 10⁵ kg/s², sliding friction κ = 2.4 × 10⁵ kg/m·s, τ = 0.5 s, 80 kg bodies with shoulder radii spread over 0.24–0.32 m, stepped at a fixed 1 ms because contacts that stiff do not survive anything longer.

The field is never wrong and the forces do not care. Everything interesting happens in the gap.

Two details that turned out to be the model

Both were bugs first and physics second, which is the usual order.

A doorway has to have thickness. With the wall as a zero-thickness line an opening only has to be cleared for an instant, and bodies slip through it at angles and overlaps no real door permits. Giving the opening a 25 cm reveal, so it is a short tunnel rather than a gap in a line, took roughly a fifth off the throughput on its own and changed nothing else.

Worth saying plainly: even after that, this crowd still runs hot. A 1.20 m door here passes about 2.1 people per metre per second, and field measurements of real doors cluster nearer 1.2–1.4. Read what follows as what this model does, which is the thing you are playing — not as a measurement of a corridor.

A door frame must not push. The A·exp(·) term means “I would rather not walk along a wall”, and it belongs to the wall’s face. Applied from the end of a wall segment it becomes a door frame that shoves you back out of the doorway from half a metre away — and at a walking pace that is enough to park somebody in the opening in a perfect force balance and keep them there until the clock runs out. The room reported 99 out of 100 and no visible jam, which is the worst way for a bug to present. A segment end is now solid to touch and nothing else.

What the room says

All of the below is scripts/measure.mjs: a bare 12 × 10 m room, 100 people, one door, no scenery. Seeded, so it reproduces.

The door is the whole story

Same hundred people, same everything, only the door changes. Best evacuation time over the dial:

door0.80 m0.90 m1.00 m1.10 m1.20 m1.30 m1.50 m
best clear123 s93 s63 s51 s41 s33 s25 s

(Best of seven dial settings, three seeds each. Taking a minimum over seven noisy numbers flatters every row by the same amount, so read the column as a trend and not as seven separate measurements — the per-row penalty for over-urging, in particular, is not something three seeds can resolve. That is what the eight-seed run two sections down is for.)

Seventy centimetres of extra door is worth five times the room. Measure the flow directly and the reason is that throughput is nowhere near linear in width — a least-squares fit over 0.8–2.4 m gives

people per second  ≈  1.52 × width^2.24

A 2.4 m door carries 12.6× what a 0.8 m door carries, on 3× the width. That matters because the linear rule — capacity equals specific flow times clear width — is how exits get sized, and it is the optimistic one at the narrow end.

Which is why two doors are not one wide door

Split the same total opening in half and put the halves on opposite ends of the same wall:

same total widthas two doorsas one door
1.6 m66 s24 s
2.0 m31 s17 s
2.4 m21 s12 s

The single wide door wins every time, by 1.8× to 2.8×. Which is not an argument against the second door — in the Annex the fire door is 1.1 m of opening the room did not have, and opening it is worth more than anything else you can do. It is an argument against believing that adding widths up is the same as having them.

Which half of the dial does the damage

The dial moves speed and routing together, so run each half on its own. A 1.00 m door, 100 people, eight seeds per cell, seconds to clear:

dial0.000.250.500.751.00
speed only (routing held perfect)11573667272
routing only (speed held at 1.00 m/s)115113117118112
both (what the dial does)11574726672

Two things fall out, and the second one is not the famous one.

Wrecking the crowd’s navigation costs nothing. Drag everyone’s heading 70% of the way from the computed route onto the straight line to the door, and at a walking pace the room clears in 112 s instead of 115 — which is nothing, at eight seeds. When the door is the bottleneck, how elegantly people reached it does not enter into it. And the bottom row is the top row in the only places it can be pinned down: the same floor (65.8 s against 65.7 s) and the same value at full urgency (72.1 s both). Which dial setting reaches that floor differs by one step between them, which is inside the scatter. Adding the routing collapse on top of the speed changes nothing you can measure.

And over-urging is the cheap mistake. There is a real optimum and going past it does cost you — 72 s against 66 s, 9.6% worse, which is the faster-is-slower everybody quotes. But leaving the dial at a stroll costs 115 s against 66 s: 74% worse. The penalty for pushing too hard is a tenth. The penalty for not pushing is three quarters. In this room the instinct worth distrusting is not panic — it is politeness.

One bollard, and the 11 places it kills everybody

Drop a single 0.84 m bollard into the bare room, 100 people, a 1.00 m door, the dial all the way up. Bare, that room takes 76.2 s (sd 2.8). Thirty-six placements — six distances upstream of the door by six lateral offsets from its centreline — give this, in seconds:

upstream ↓ off →0.0 m0.2 m0.4 m0.6 m0.8 m1.0 m
0.6 msealedsealedsealedsealedsealed63.5
0.8 msealedsealedsealedsealedsealedsealed
1.0 m68.2212*215*212*210*72.1
1.2 m66.776.573.771.476.072.6
1.4 m228*82.580.571.975.466.1
1.6 m78.976.373.876.571.169.1

sealed means nobody got out at all, in any of the three runs; a starred number means some runs did and some did not.

Twelve of the thirty-six placements beat the bare room by more than 2%. Three make it more than 2% worse. Eleven shut the room completely. The best one — 0.6 m upstream and a full metre off the centreline, which is beside the door rather than in front of it — gets everyone out in 63.5 s, 16.7% faster than no bollard at all.

The shape of that table is the point. There is no smooth hill to climb toward. Good placements sit next to catastrophic ones, and the rule separating them is not subtle: two shoulders is 1.11 m across in this crowd, a 0.84 m bollard plus the strip along each jamb that nobody can use leaves less than that, and when it does, the room is a sealed box with a door in it. The planning screen says so before you start, in red, because finding out afterwards is not a game mechanic, it is just a waste of a minute.

The four rooms

Lecture Hall — 85 people, a generous 1.30 m door, nothing to place. The room where the dial is nearly free, so you can find it without being punished.

The Column — 100 people, a 1.00 m door, one bollard. The map above, played once, with the smoke coming in.

The Annex — 110 people, a 1.00 m main door and a 1.10 m fire door on the far wall that starts shut. Leave it shut and the room clears in 54 s having lost 24 people; open it and the same room clears in 46 s having lost none. Nothing else you can do here is worth a fifth of that.

Concourse — 150 people, three doors, structural columns already in your way. Two doors start shut, and this is the room that punishes opening everything: east alone is 55 s and nobody dies; east plus south is 45 s and nobody dies; east plus north is 37 s and thirty people walk into the fire on the way to it. The fastest door out is not always a door out.

Every room ends the same way: everyone out, or the number who weren’t.

Reuse

src/crowd.mjs is framework-free with no DOM in it. createWorld(level, seed) builds a room from a plain object, step(world, urgency) advances one fixed 1 ms tick, and runHeadless(level, opts) plays a whole evacuation with no browser at all — which is how every number here was produced. The floor field is public on world.field if you want to draw it.

Two gotchas worth carrying away: