Amtrakr
Live Amtrak train tracking: real-time positions, delays, and journey timelines on an interactive map.
- Web App
- Open Source
- UI/UX
The question I actually wanted answered
I ride Amtrak often, and checking a train’s progress used to feel harder than it should. The official experience could tell me a schedule, but understanding where a train was, how late it had become, or when it would reach a particular stop still required too much digging and mental arithmetic.
There are sophisticated rail-tracking tools that expose speed, heading, geodetic coordinates, route geometry, and nearly every other detail a train enthusiast could want. Those products are genuinely cool. Amtrakr is intentionally for someone else: the average rider checking a connection, or a family member asking when a loved one’s train will arrive.
That changed the product question from “How much train data can I display?” to “What is the smallest amount of information that makes someone feel oriented?”
Choosing what deserves the screen
Amtrakr covers every active Amtrak train across the United States and Canada, but the interface begins with a single search field. A person can enter a train number, route, station code, station name, or city. From there, the product answers four ordinary questions in a consistent order.
- Where is it?
- A live position on the national map.
- Is it late?
- Plain-language status and revised times.
- When will it get here?
- The next stop and a live countdown.
- What happens after that?
- A readable stop-by-stop journey.
I treated information hierarchy as the main feature. Train number and route establish identity. Current status and delay establish confidence. The next-stop card gives a revised time and a plain-language countdown such as “arriving in 23 minutes.” The full journey stays available underneath, but it does not compete with the immediate answer.
Station pages use the same approach. Instead of exposing the entire network, they show today’s trains in the order that matters: currently at the station, approaching, then departed. Scheduled and updated times sit together, so a delay never has to be inferred from two disconnected screens.
Using the map as context, not the product
The network-wide MapLibre view makes the system feel alive and gives curious users a way to explore. Every train is converted into a GeoJSON point and colored by a small status vocabulary: on time, minor delay, late, or unknown. At a glance, the map communicates the state of the network without covering it in labels.
Selecting a train removes the rest of the fleet and reframes the map around one journey. Stations already passed become a muted solid segment; the remaining route is darker and dashed; only upcoming stops remain prominent. The destination gets a distinct terminal marker.
The implementation still does some train-nerd math. It calculates a great-circle bearing from the live position to the next known station so the marker faces the direction of travel. That detail helps the map read naturally, but the angle itself never becomes another number the rider has to interpret.
Designing the details mobile-first
This experience is most useful while someone is moving: on a platform, between connections, or waiting to pick somebody up. I designed the train details as a responsive surface rather than shrinking a desktop sidebar until it fit.
A bottom sheet preserves map context and keeps the important information thumb-reachable.
A side panel makes room for the complete timeline without covering the live map.
On mobile, selecting a train opens a bottom sheet over the map. The next stop appears first, the journey scrolls vertically, and the sheet can be closed without losing map position. On desktop, the same information slides into a fixed side panel and the map resizes around it. Deep links preserve the selected train or station in the URL, so a useful view can be sent directly to someone else.
Any stop in the timeline can become an anchored destination. Tapping it changes the summary from the system’s next stop to the stop the rider actually cares about. The visual language stays quiet: past stops recede, the current stop pulses, revised times replace scheduled ones without hiding them, and motion respects reduced-motion preferences.
Translating live data into plain language
Amtrakr consumes Amtraker v3 by piemadd, a community-maintained API that collects and normalizes public train- tracking feeds. It provides live positions, route details, schedules, station states, velocity, and timestamps. The application flattens the train-number response into a typed model and joins each journey with separately cached station coordinates.
TanStack Query refreshes the fleet every 30 seconds with a 15-second stale window. Station metadata changes far less often, so it stays cached for an hour. Keeping the previous response visible during refresh prevents the map from blinking or collapsing while new positions arrive.
Some of the source data is less direct than the final interface suggests. The API does not expose a dependable high-level delay label; its marker color encodes timeliness. Amtrakr converts that color to HSL, reads the hue, and maps it into the four status groups shown above. At each stop, it compares actual and scheduled timestamps to produce minute-level delay badges and localized arrival times in the station’s own time zone.
This is the kind of complexity I wanted the code—not the rider—to absorb. React and TypeScript organize the interface, MapLibre renders live and focused route layers, TanStack Query controls freshness, and Framer Motion makes panels and state changes feel continuous without making the application theatrical.
What I would add next
Live rail data is only as reliable as the underlying reporting. A train can occasionally hold a stale position, and its revised schedule can lag reality. I would like to compare reported schedules with position history to estimate confidence instead of presenting every update as equally current.
The next user-facing feature would be selective notifications: tell me when a chosen train’s ETA moves meaningfully, not every time a coordinate changes. That follows the same principle as the rest of Amtrakr. The system can know a great deal; the interface should interrupt someone only when that knowledge becomes useful.