Live London Underground headways, service gaps and train bunching
π track-the-gap.peng.ly
TfL will tell you a line has a good service. It won't tell you that the next Central line train to Hainault is nine minutes away when it should be four.
Track the Gap works that bit out. It reads TfL's live arrival predictions, rebuilds the trains from them, and measures the headway - the time between one train and the next in the same direction over the same bit of route. That gets compared against the timetabled headway for that stop, that day type and that hour, and the ratio is the whole story: near 1 is an even service, 2 means your wait has doubled.
Branches are kept apart from trunks, beacuse they're different railways. A train only counts towards a section if its service pattern actually runs over it. Where lines share track, you get the combined corridor headway and each line's own figure side by side.
There's also excess wait, which is what TfL publishes for the same thing - how much longer the average wait runs than the timetable asks for. A long gap counts for more than a short one, because more people are stood on the platform during it. That's the number to read when a line's typical headway looks fine and its verdict doesn't.
The map on each line page is a time axis, not a geographic one - distance across the page is timetabled running time, so two trains six minutes apart sit six minutes apart on screen.
Nothing to configure. / is the board for all eleven lines, and each line has its own page at
/central-line, /district-line and so on. Hover, tap or tab onto any section of a line map to
see its headways, the trains on it, and how far off the timetable it is.
Every reading carries the time it was taken. Nothing is judged on colour alone - each state has its own mark and its own words.
Fork the repo, import it into Vercel, and set TFL_APP_KEY (see Configuration).
Or use the button π
For Cloudflare Workers it's ADAPTER=cloudflare npm run build then npx wrangler deploy - the
config is already in wrangler.jsonc, and the app needs wrangler secret put TFL_APP_KEY once.
That's what track-the-gap.peng.ly runs on, deploying itself off
main.
Multi-arch image on DockerHub and GHCR
(ghcr.io/notaflightrisk/track-the-gap):
docker run -p 3000:3000 -e TFL_APP_KEY=your-key notaflightrisk/track-the-gapGrab site.zip off the latest release,
unzip it, then node build. It's a plain Node server, port 3000 by default.
Follow the Development steps, then ADAPTER=node npm run build and node build.
| Variable | Default | What it does |
|---|---|---|
TFL_APP_KEY |
none | Your TfL Unified API key, free from api-portal.tfl.gov.uk. The API answers without one, at a much lower rate limit. |
TFL_POLL_SECONDS |
15 |
How long a live snapshot is held before refetching. Two API calls per refresh, no matter how many people are looking. |
ADAPTER |
vercel | Set to node for a self-hosted server build. |
PUBLIC_PLAUSIBLE_SCRIPT |
none | Your Plausible script URL, to count visits. Read at build time, so set it before building. |
Thresholds, the day-type bands and the "insufficient data" cutoff all live in
src/lib/config/health.ts rather than in any component, so you can retune what counts as a gap
without touching the UI.
The network model - topology, branches, run times and timetabled headways - is generated, not
hand-written. npm run network rebuilds src/lib/data/network.json off the TfL API, and the
πΊοΈ Network workflow does it monthly and opens a PR if anything moved.
You'll need Node 22 or newer, plus Git. It's a SvelteKit app.
git clone git@github.com:NotAFlightRisk/track-the-gap.git
cd track-the-gap
cp .env.example .env # then put your TfL key in it
npm install
npm run devDev server is on localhost:5173.
Before committing, run npm run check (types), npm test (tests) and npm run format.
You can also build the container with docker build -t track-the-gap .
Powered by TfL Open Data. Contains OS data Β© Crown copyright and database rights 2016 and Geomni UK Map data Β© and database rights 2019. The headways here are worked out from those predictions and are not official TfL figures.
Licensed under MIT, Β© NotAFlightRisk 2026