Hood to Coast Athlete TrackeriPhone web app
When is our runner getting here?
Making a nearly 200-mile relay a little less chaotic.
Hood to Coast is a 12-person relay where runners rotate through dozens of legs while two vans leapfrog their way from Mt. Hood to the Oregon coast.
After racing it, one problem kept bothering me: we were surprisingly bad at knowing when the next handoff was actually going to happen.
- Role
- Designer, developer, team captain, runner
- Platform
- iPhone web app
- Timeline
- Jul 2026–present
- Status
- Raced once, iterating for 2027

Why I made it
Hood to Coast is the largest relay race in the world, run every year since 1982. It starts at Timberline Lodge on Mount Hood, runs through Portland and the Coast Range, and finishes on the beach in Seaside, through a full day and an overnight. Teams split into two vans that leapfrog down the course, and every handoff depends on the next runner being at the exchange at the right moment, often in the dark and often with no cell service.
- Miles
- 197
- Runners
- 12
- Vans
- 2
- Legs
- 36
- Hours to the finish
- 28
- Teammates using the app
- 12 of 12

I was the team captain, one of the twelve runners, and the only person building the app. That overlap shaped the product more than anything else: I couldn’t design something that needed me watching it. It had to work while I was running, sleeping, or out of signal.
The project came straight out of our first year, when we shared iPhone locations. It felt useful, but a dot on a map says where someone is, not when they’ll reach the exchange. Working that out meant guessing distance and pace and doing the math in a moving van, and a wrong answer meant a runner waiting in the cold or a van arriving late. The printed schedule was a good plan for about the first hour. By the overnight legs it was mostly guesswork.
The problem wasn’t location. It was timing: turning where the runner is into when they’ll arrive, and keeping that accurate as the race drifts away from the plan.
What I designed
Most running apps are built around one athlete. This one was built around a team.
The runner already knows where they are. Everyone else needed the information: the next runner deciding when to warm up, the driver deciding whether there’s time to stop, and the rest of the team deciding whether they can sleep. Find My shows where someone is, Strava and Garmin track one athlete well, and the printed schedule can’t adjust. None of them answered the timing question.
The product came down to one mental model: what’s happening now, and what happens next. The main screen answers it in one glance, from a passenger seat, often at night. It shows who’s running, which leg, and how far along they are, and it leads with the ETA and the next runner, because those change what people do. Instead of coordinates, the app says “Runner 8 · 72% through Leg 8 · Exchange in ~14 min.” The map is still there, but as supporting detail, not the headline.


Built to be read in two seconds by someone who just woke up in a van.
I knew location would drop out, so I built a fallback. When GPS goes quiet, the app keeps estimating from the leg’s start time, its distance, and the runner’s planned pace, then snaps back to real data when a location returns. That works because the app keeps the plan and the live state separate.
Deciding what not to build mattered as much. Social features, chat, leaderboards, and deep analytics are already well covered elsewhere. If a feature didn’t help someone get to the right place at the right time, it didn’t ship.
How I built it
- Tools
- Claude, Claude Code, VS Code, GitHub Copilot, Notion, FigJam
- Tech stack
- Vanilla JavaScript, Supabase, Vercel, Leaflet, OpenStreetMap, Traccar
I built it as a progressive web app, so setup meant opening a link in Safari and choosing Add to Home Screen. There was no App Store review and no TestFlight. Under the hood it’s vanilla JavaScript, HTML, and CSS, with no framework and no build step. Supabase handles the database and live data, Vercel handles hosting, and Traccar reports each runner’s location in the background. The map runs on Leaflet and OpenStreetMap, so there were no licensing costs.
I was the only person on the project, but I didn’t work alone. Claude was my strategy and spec partner, Claude Code in VS Code did the implementation, and GitHub Copilot handled small interface tasks. The speed came from the tools. The quality came from the rules I set around them. Every task started with an audit before any change, the fragile parts (race state, ETA math, handoff logic, the map) were off-limits unless they were the target, and every change went through a branch, a pull request, and my review. That was more than 60 pull requests. Notion held every bug and feature as its own item, and FigJam became my bug-reporting tool: I’d mark up a screenshot from my phone and bring it back into Claude Code.


Notion tracked what to build. FigJam showed what to fix.
A few days out, I removed the testing simulation entirely, because leftover test data had come close to interfering with the live race. The night before, I built a manual override that let me set the active runner and leg directly. I hoped not to need it.
Using it for real
There was no test environment. The race itself was the test: all twelve of us used the app for 28 hours, overnight, while running, driving, and sleeping in shifts.

The core idea held up. Instead of asking where the runner was, the team could see who was running, how far along, and when they’d reach the exchange. Next runners timed their warm-ups off the ETA, and the other van could follow the race without a stream of texts. The pace fallback worked better than I expected: when runners lost signal, the app kept estimating instead of freezing.
Connectivity was the biggest constraint. Losing a runner’s GPS mid-leg was survivable. Losing service at an exchange wasn’t: automatic handoff detection couldn’t register, so the app kept the previous runner active after the race had moved on. The override earned its place. I corrected the active runner and leg from the van, and the fix reached every phone in seconds, because only the live state was wrong. It was a recovery tool, not the experience I ultimately want, but it’s why the app stayed useful all the way to Seaside.
What I learned

Going in, I thought I was building a prediction tool: how do I predict when the runner will reach the exchange? Coming out, I saw the real problem was different: how do I keep a distributed team in sync when the network can’t be trusted? I’d had the design constraint backwards. I designed for connectivity as the normal case, but through the Coast Range and overnight, no signal was the default, and the app worked because the fallbacks carried it.
Two more lessons. Any system that decides things on its own, like deciding a handoff happened, will eventually get something wrong, so the key question is how quickly and safely a person can correct it. And no amount of testing at home would have shown me what happens at an exchange with no signal at 3 a.m.
The next version treats poor connectivity as a core requirement from day one: race state kept on each phone, handoffs confirmed at the exchange itself, automatic recovery when a runner’s location reappears, and an ETA that shows how confident it is. The goal isn’t to eliminate uncertainty. It’s for the app to understand its own uncertainty and say so clearly.
This is why the project leads the Making page. There was no brief and no stakeholder. I hit a problem during something I love, built something, used it for real, and watched my assumptions break. That’s the work I want to keep doing.
Credits: Designed, built, and operated by Tyler Wain. Raced by The Buffalos, Hood to Coast 2026.