Cadence

Plans a truck trip that stays inside US Hours of Service rules, and draws the driver's daily log sheets.

Role
Solo
Where
Personal project
Year
2026
Stack
Django, React, TypeScript, MapLibre
Status
Live

Live appSource on GitHub

What it is

A truck driver runs four clocks at once: 11 hours of driving, a 14-hour window, a break after 8 hours of driving, and 70 hours on duty in 8 days. Whichever runs out first stops the truck.

Cadence takes four inputs: current location, pickup, dropoff and cycle hours already used. It returns the route with every legally required stop on it, plus a filled-in log sheet for each day. It uses free routing and maps only (OSRM, Nominatim, OpenFreeMap), with the frontend on Vercel and the Django API on Railway.

What I did

  • Wrote the Hours of Service engine as a pure function. At each step it drives the shortest of every remaining limit, then inserts whichever rule stopped it.
  • Built the planner: four gauges and a trip scrubber driven by the engine's own snapshots, so the display can't disagree with the plan.
  • Ranked alternative routes by what they cost the driver in hours, not by miles.
  • Drew the FMCSA log sheet in SVG: 24-hour grid, 15-minute ticks, duty line, remarks with real place names, one sheet per printed page.
  • Made every plan a shareable URL, and the browser re-checks the finished plan against the rules.
  • 147 backend tests, plus hand-checkable cases in the repo.

Hard parts

Rules most planners get wrong

After 14 hours a driver may still work, just not drive, and loading still uses up the 70-hour cycle. A one-hour pickup already counts as the 30-minute break, so adding a separate break after loading is wrong, not just redundant.

Sheets that always total 24 hours

Times are whole minutes from end to end. The log builder splits at every midnight in a loop, because a 34-hour restart crosses two midnights and a single split would put more than a day on one sheet.

Staying up when free APIs don't

Responses are cached and every upstream URL is configurable. If a public routing server is down, the app falls back to an offline city table and a straight-line estimate instead of going blank.