Building Pulse
I was poking around the FRC Nexus site one night and noticed they had an API. That was basically the whole idea. If I could pull live event data, I could throw together a dashboard for our pit and stop dealing with a problem that had bugged me all season.
If you haven't worked an FRC pit, the deal is this. During a competition your pit crew has to keep track of a bunch of small things at once: when your next match is, whether you're being queued, who your alliance partners and opponents are, what bumper color you're wearing, how the teams near you usually play. None of it is in one spot. So everyone does the same thing, which is to keep The Blue Alliance open on a laptop and refresh it constantly, or write the next few matches on a whiteboard and erase and rewrite them every twenty minutes. It works fine. It's also one more thing to babysit when you're already elbow-deep in a robot.
So Pulse started as one screen for the pit that just stayed correct on its own.
Where the data comes from
The dashboard part isn't really the hard part. The annoying part is that the data lives in a few different services that all hand it over differently, and you have to glue them together so it feels like one thing updating live.
Pulse uses three sources. The Blue Alliance and FRC Nexus both have webhooks, so when something changes at an event, like a match getting scored or the queue moving, they hit Pulse instead of me sitting there polling them. Statbotics is the third one, for the deeper team stats, and that one I have to go fetch myself.
When a webhook comes in, Pulse updates its copy of the event and pushes the new state out over a WebSocket to everyone currently watching that event. The browser just stays connected and I send it the change the second it happens. No refreshing, no whiteboard.
The Statbotics problem
Statbotics is where it got annoying. The stuff I wanted, which was detailed stats for every team at the event, isn't something you can grab in one call. I had to hit a separate endpoint for each team, for every team at the event, every single time anything updated. The API is already slow, so running it dozens of times back to back meant a refresh could sit there for around ten seconds before the richer stats filled in.
I'm pretty sure I could fix it by just hosting Statbotics myself and querying my own copy locally, so I'd skip all the network round-trips. It's open source, so nothing's stopping me. I never actually got around to it though. The dashboard worked, ten seconds was survivable, and there was always something more urgent. So it stayed on the list. Still on the list, honestly.
Launching from inside a pit
When Pulse first went out it picked up around four thousand users in the first week, which was way more than I expected. Bad timing though, because I was at a competition when it happened. So I spent the weekend going back and forth between fixing our own robot and patching Pulse live while real traffic hit it for the first time. After that first day of putting out fires it held up fine. It ran on Railway at first and I later moved the backend over to Vercel, which is where it lives now.
The split, and what I'd change
The part I didn't see coming: I built Pulse for pit crews, but when I looked at how people were actually using it, a lot of them were just at home following an event, nowhere near a pit. So I figured, why not make a version built for that. I split Pulse into two. There's Pulse for Spectators, which is meant for following matches on your phone, and the pit display version on desktop. The spectator one also does match notifications through a service worker, so it can tell you your match is coming up even when the app's closed.
Splitting it was the right move for users but kind of a pain for me. The two apps run on the same backend and the frontends are almost identical, just different enough to be annoying. If I started over I'd keep it as one app that handles both cases instead of maintaining two basically-the-same versions. It'd be less to keep alive, not more. The second one is never as little work as you think it'll be.
Where it's at now
Pulse is pretty much done at this point, so I'm mostly just keeping it running instead of adding to it. There's stuff I'd still like to do, like self-hosting Statbotics, but for now I just want to keep it up for the teams using it.
If there's a takeaway it's pretty boring: I saw an API, built the smallest thing that fixed my own annoyance, and the people using it kind of told me what it should be. That worked out better than if I'd tried to plan the whole thing up front.