projects · commute-tracker

Should I go in — and when?

A hybrid schedule turns "how bad is traffic" into a decision you make ten times a week. commute-tracker collects real corridor timings around the clock, learns the shape of each day, and answers the only two questions that matter: which office days cost the least, and when to leave. Verdict it settled: the new office runs ~1.8–1.9× the old commute — so the answer is a plan, not a feeling.

The product

A dashboard that answers, then explains

The layout is a three-tier answer machine: the verdict now, the trends that justify it, and the exploration layer underneath.

dashboard — when to leave · plan your week
commute-tracker dashboard: morning and evening departure-time charts with 10th–90th percentile bands, and a plan-your-week row of weekday cards with GO badges
The answer, drawn: median and 10th–90th percentile bands per departure time (real history, not a vendor's guess), then the week planned into GO days. One crimson doing all the talking.
the verdict
The damage: new office runs 1.8 times the old commute — 25 vs 46 minutes, 238 extra hours per year
The question the app was built to settle, settled: 1.8× the old commute, measured the same way, priced in hours and fuel.

Native on iPhone

A SwiftUI app (plus home-screen widget and watch app) ships through TestFlight — and it's not just a viewer. The phone is the platform's ground truth: it records actual drives in the background (battery-friendly coarse GPS) and uploads them, so predictions are continuously checked against real trips rather than trusted on faith. It talks to the server over a bearer-token API — the one part of the system published to the internet — so the phone works from anywhere while the dashboard stays private.

  • StackFastAPI + SQLite server · vanilla JS + Chart.js + MapLibre web · SwiftUI iOS app, widget & watch app
  • DataCorridor timings sampled continuously for months — predictions are quantiles over real history, with an uncertainty band, never a vendor's black box
  • SecurityTwo surfaces, and they are not the same product: the dashboard is same-origin checked but unauthenticated by design, so it belongs behind a perimeter you trust — here it is never published to the internet and answers only on the private network. The phone's API is the one public surface: bearer auth with constant-time comparison, published through a Cloudflare Tunnel that dials out, so the origin needs no open inbound port. Hardened after an internal security review.
  • Sourcegithub.com/travismetcalf/commute-tracker — MIT, server + iOS in one repo, with the architecture notes that explain why each piece is built the way it is
iOS · simulator
Commute iOS app: when-to-leave percentile charts and the plan-your-week list, in the dusk theme
The same answers, native: quantile bands and the week plan in SwiftUI, wearing the dusk theme.

Answers that arrive on their own

The dashboard answers when you open it; notifications answer when you don't. A morning brief at 07:15, a push when a drive finishes telling you how it went against what was anticipated, a Saturday summary of the week just ended, and an alert when the commute goes unusually bad inside the windows you actually care about. Every type is switchable per channel — and each offers only the channels that suit it: a finished trip is a push, never an email; the poller-failure notice is email only, because a push saying the server is down depends on the server.

Lessons

What it taught the platform

Degrade like you mean it

The map is the page's least important widget and most fragile dependency, so when WebGL isn't available it fails to a single honest sentence while every number keeps working.

One crimson, two tones

The corridor map carries traffic in a single hue — darker means slower. That rule, made here, became house law on the styleguide.

Publish only what has to be public

The dashboard has no login, so it stays on the private network; the phone has to work from anywhere, so its bearer-token API is the one public surface. Naming each surface's trust model separately — rather than assuming one policy covers both — became the platform's trust-surfaces pattern.

Alert on the channel that survives

The poller-failure notice is email only, because a push saying the server is down depends on the server. Announce a failure on a path that doesn't run through the thing that failed.

Copied