Living in New York, Amtrak is often a competitive alternative to flying or driving around the Northeast Corridor. But as someone who likes to track the progress of my transit, I found the options lacking.

Google Maps will offer Amtrak for transit routing, but it doesn’t actually let you see where trains are, or to track someone else’s train. It also doesn’t communicate delays well as they accumulate or improve along a journey. Amtrak’s official tracker page is pretty rough (just try to zoom on desktop or pan on mobile), and unofficial sites like RailRat are functional but don’t have the most user-friendly design.

Enter TrainTracker, a modern app with a snappy map, easy train search, clear progress visualization, and push notifications. It supports Amtrak, VIA Rail, and Brightline. The basic idea was something like “Flightradar24 for trains.” It prioritizes sound design and user experience.

Powering it on the backend is much data-wrangling from official and unofficial sources to source rail track and station data. Live train updates required decrypting Amtrak’s obfuscated API and parsing its frankly insane response structure into a clean API (shout-out to Greg Walker for reverse-engineering the decryption process). VIA Rail and Brightline data is mercifully a bit more straightforward.

For the map, I used MapLibre GL JS with vector tiles from OpenFreeMap. The map shows estimated real-time train positions using a combination of GPS location and timetables. This required some pretty intensive code to “snap” a train’s position and heading on the appropriate track and smoothly animate it.

I employed several performance optimizations to make this responsive, including caching, culling elements outside the viewport, and leaning on CSS transitions to create smooth animations between JavaScript-calculated updates.

This was also my first experience with the web Push API, which enables users to receive push notifications when a train arrives at or departs a particular station. Push subscriptions are stored in a SQLite database managed through Prisma.

For TrainTracker on mobile, I wanted a bottom sheet pattern similar to Google Maps, where a sheet with info about a train or station appears over the map and can “snap” fully open, halfway open, or closed. To achieve this, I wrapped react-modal-sheet and modified it so a swipe gesture contextually switches between dragging the sheet vs. scrolling the content within the sheet in an intuitive way.

TrainTracker gives North American rail enthusiasts and commuters a better way to follow along their journey.