How it’s built

Why the browser plans the route and the object decides

On a Saturday the bakery plans 63 deliveries on up to four vans twice, once on the server and once in your browser, and publishes neither until one object has checked the plan against its own numbers. This is why the work is split that way, how to read the gap between the two plans, and what the plan does not promise.

The problem

The seeded Saturday has 63 stops and four vans. Dealing the stops out in the order the orders arrived sends each van across the city and back more often than it needs to. A good plan groups stops that are near each other and orders them so the van does not backtrack, and finding one is real computation: the number of ways to split 63 stops among four vans and order each van’s stops is far past what any program tries one by one.

What the lab shows, and what it cannot

The Route Lab, as read on 2026-11-06, is a made-up bakery in Seattle with 20 to 63 orders. It plans the same orders two ways, as they came in and as VROOM plans them in a Web Worker, and reports the difference: on its busy Saturday, 3 h 12 min of driving against 5 h 34 min. Its page says VROOM “promises a good plan fast, and not always the best one”, and that its travel times assume no traffic.

It compares a plan with no thought against one with a lot. It has no second planner of comparable quality, so it can show no gap between two good plans. It has no record of deliveries, so it cannot show what happens when a plan is wrong, or a stop is missing, or a van has already left. Its data are Seattle’s, from OSRM; the bakery’s are Salt Lake City’s, from openrouteservice. The bakery adds the server’s planner and the object that judges.

Two planners, neither exact

The server plans with the savings heuristic of Clarke and Wright (1964). Every stop starts as its own round trip from the bakery. Joining the end of one trip to the start of another saves d(i,0) + d(0,j) − d(i,j) minutes, where d is the drive time and 0 is the bakery. The heuristic scores every ordered pair, 63 × 62 = 3,906 of them for the Saturday, sorts by saving, and merges trips from the largest saving down while a van has room. It uses no clock and no random draw, so the same input gives the same plan. It is in crates/domain/src/dispatch/savings.rs.

VROOM, run in the browser as WebAssembly, builds a plan and then moves and swaps stops between routes until no change shortens the driving. It looks harder, so it should find a shorter plan more often than not. The page asks it to look at level 5 up to 40 stops and level 3 above that, and to stop after 5 seconds (apps/web/src/lib/dispatch.ts).

Neither is exact, and the gap on the dispatch screen has to be read with that in mind. The screen reports the server’s total less the browser’s, as a percentage of the browser’s. That is a gap between two heuristics. A gap of zero says they agree, not that either is best. A positive gap is a floor on what the server’s plan leaves unused, since the browser’s plan is a real plan and the best one can only be shorter still. A negative gap, where the server’s plan is shorter, is possible, and the screen says so in words. The gap does not say how far VROOM is from the best plan, which neither planner computes. ADR 0012 sets the bakery’s own line at 25 percent of total drive time on the seeded days, a revisit trigger, and the roadmap records no measured gap.

Neither is trusted: the object decides

The browser can send anything, so the browser’s plan is a request. The Dispatch object, one per sandbox and business day, checks it before it is stored and again before it is published, since stops come and go in between. Its rules have names a visitor can see in a refusal: vans, stop-unknown, stop-twice, over-capacity, route-ends, stop-missing and, since 2026-11-08, window. Every stop appears once, no van carries more than its capacity, every route starts and ends at the bakery, no stop is left out, and no van reaches a stop after its delivery window closes. A plan that sends its own total is refused with 400, since the object does not read it. The minutes are always worked out from the object’s own travel-time matrix, the same arithmetic for the server’s plan and the browser’s.

That is what makes the comparison fair. The page builds VROOM’s input from the same matrix, with every leg rounded up to whole minutes, so VROOM minimizes the figure the object will count. Since 2026-11-08 it also gives VROOM the times the object checks: the vans leave at 9:00 am, a stop may be served until two hours after its window opens, and each door takes 3 minutes. Until then the browser’s plan ignored time. A published plan is a RoutePublished event, and it is the only one that reaches the kitchen board and the orders. Once a van has left, the object asks the orders about it and refuses a new publish with 409 published.

Where the computation is free and where it is paid

A plan, not a promise

The times are free-flow drive times with no traffic. The totals count driving only; the time the last van is back adds 3 minutes at every door and any wait for a window to open. The map draws straight lines between stops, not roads. Delivery addresses are made up and map onto the 64 places by index, so two customers can share a drop point. A published plan says what the bakery would do on a road with nobody else on it.

What to try

On the dispatch screen, plan Saturday’s stops both ways, read the gap as above, and publish one. Then try the Route Lab and add or remove an order on its map and watch both of its plans re-solve. The summary on How it’s built says the same in short.

Sources

Back to the routing principle on How it’s built, or to the lab.