Technical description
How Commute is built
Every number on the site comes from a public source and a deterministic rule. This page says which, and what talks to what.
Shape of the system
The browser runs a Next.js app with a MapLibre map and talks only to the backend over JSON with a bearer token. The backend is a FastAPI service on Render with a managed Postgres, and it alone talks to the data providers, so caching, time budgets and the honesty labels live in one place.
Five providers, none of them needing a key: postcodes.io for coordinates, OSRM for car, cycle and walking routes, the TfL Journey Planner for bus and tube journeys, Open-Meteo for the next three hours of weather, and the gov.uk conversion factors with HMRC fuel rates, read once and pinned in code.
Frontend
One MapLibre map is mounted for the whole page. With nothing chosen it is a globe over the UK that sways slowly; a postcode flies the camera from orbit to the street in 3D. Routes are line layers on top, one colour per mode, the selected route wider and shared-car routes dashed. The panel is one component in two layouts, a card on desktop and a bottom sheet on phones, and every string lives in a single copy file so a linter can enforce house style.
| Piece | What it is |
|---|---|
| Framework | Next.js 15 App Router, React 19, TypeScript strict |
| Styling | Tailwind CSS with design tokens as CSS variables, light and dark from one class |
| Components | shadcn/ui-style components on Radix primitives, lucide icons, framer-motion; every one listed with licence in UI-COMPONENTS.md |
| Map | MapLibre GL JS 5 with the globe projection; tiles and 3D buildings from OpenFreeMap (OpenStreetMap data) |
| Tests | tsx unit tests, ESLint, strict typecheck, a copy linter, a Tailwind class-merge guard |
Backend
Four tables: profiles (home, the cached car route to the office, the shared-ride settings), saved places, trips (CO2, saving, XP, whether the arrival check passed) and carpool matches. XP is never stored as a total; it is the sum of a user's trips, so it cannot drift.
Planning a journey geocodes both postcodes, calls OSRM driving, cycling and walking and the TfL planner concurrently with a six second budget each, prices every option from the pinned factors, adds carpool candidates for a signed-in user, and asks Open-Meteo for the next three hours. Every option carries a provenance: routed, estimated (the public OSRM server returns a driving time for bike and foot, so those times are recomputed at 15 and 5 km/h and labelled) or unavailable. A failing provider never fails the request.
| Piece | What it is |
|---|---|
| Framework | FastAPI 0.115 on Uvicorn workers under Gunicorn; Pydantic v2 for every request and response |
| Database | SQLAlchemy 2, Alembic migrations applied at boot, Postgres in production |
| Auth | JWT bearer tokens, bcrypt password hashes, email verification; slowapi rate limits on every endpoint |
| Tests | pytest, 87 commute tests, no network: providers faked at the client seam |
Carbon and money
Every CO2 and cost figure comes from a pinned factor with its source row named beside it in the code. The factors were checked against the published 2025 workbook and the HMRC page on 3 October 2026. Annual figures assume 220 working days, 440 one-way trips.
| Quantity | Value | Source |
|---|---|---|
| Car, average, unknown fuel | 0.16725 kg CO2e per km | UK Government GHG conversion factors 2025 |
| Local bus | 0.10385 kg CO2e per passenger km | same, business travel (land) |
| London Underground | 0.02780 kg CO2e per passenger km | same |
| Walk, cycle | 0 | |
| Fuel cost | 17p per mile, 10.6p per km | HMRC advisory fuel rates, petrol 1401 to 2000cc |
| Bus fare | 1.75 GBP per hour of bus legs | TfL Hopper fare |
| Tube fare | 2.80 GBP per rail leg | assumption, named in the response |
XP, the arrival check and the leaderboard
XP for a trip is the share of your own solo drive you avoided, from 0 to 100, plus 10 for sharing a car when the pair saves something. Cycle and walk pay 100, bus and tube about 80, a shared car between 35 and 50. Because it is a share and not a weight, a long commute earns no more than a short one: the leaderboard compares choices, not distances.
The server recomputes the option at log time and ignores any number the client sends. The same trip twice within 30 minutes is refused, trips under 1 km are logged without points, and only four trips a day score. A trip is verified when the phone is within 500 m of the destination and the time since Start is at least 60% of the routed duration; it deters rather than proves, and a GPS trace classifier is the next step.
The leaderboard ranks everyone by total XP, all time or this week from Monday 00:00 London. Weekly prizes go to the top three; milestones at 500, 1,500, 3,000 and 6,000 XP are badges kept for good.
Shared rides
The person who sends the request is the driver. Candidates are the six nearest homes among people who opted in; for each, OSRM routes driver home, partner home, office, and the candidate is kept when the detour adds at most 12 minutes. The detour and overlap are measured once from the driver's seat and stored, so both people see the same numbers.
The saving is the pair's total, both solo drives minus the shared drive, split equally; each traveller's XP is their share against their own baseline. The sharing bonus is only paid when there is a saving. Once a pair is accepted and both consented, each sees the other's phone number, and nowhere else is a number shown.
The organisers' resources
| Resource | Used for |
|---|---|
| gov.uk conversion factors | every CO2 figure |
| OSRM | car, cycle, walk and carpool detour routes |
| Open-Meteo | the weather suggestion |
| Bus Open Data (DfT) | not used: it needs a key and gives timetables, not journeys; TfL's planner gives London journeys with no key |
| Nomis | not used in the app |
Known limits
- The public OSRM server is shared and rate limited; routes are cached 30 minutes.
- Carbon factors are UK averages, not the user's actual car.
- The arrival check deters rather than proves.
- Carpool XP is paid to each traveller on their own log; a paired payout is designed, not built.