The Spark
Tournament day at a ballpark runs on a whiteboard.
Someone draws the bracket the night before. Someone else builds a schedule in a spreadsheet and prints it. By the second round the whiteboard has arrows and eraser smudges, the printed schedule is wrong because one game ran long, and every coach is walking over to ask the same two questions: who do we play next, and when?
We've spent a lot of time around recreational softball, and we've built tournament software before. Every time, the same thing stood out: the hard part of tournament day isn't collecting registrations. It's the few hours when games are finishing, scores are coming in, and a bracket has to stay right while everyone is watching it.
So we built a product for exactly that window. Ribby Sports is our own platform, built and run by the studio, and sold to tournament organizers.
What We Built
An organizer sets up a tournament in a few steps: teams, fields, and the hours each field is available. Ribby Sports builds the bracket and a schedule that fits it. From there, tournament day runs itself:
- Brackets for any team count. Single elimination, double elimination, or a three-game guarantee, with the two common ways of keeping 0-2 teams playing.
- A schedule built around the fields. Game length, the break between games, and each day's hours go in; a schedule comes out where no game starts before the games it depends on are over.
- Umpires score from the field. A six-digit code or a QR sign at the field opens a scoring screen on any phone. No accounts, no app to install.
- A live bracket for everyone. Players, parents, and fans open one link and see the bracket, the schedule, and scores as they come in.
- Game-day changes in a couple of taps. A rain delay shifts every later game on a field, pushes along anything it runs into, and can move games to the next day. Matchups can be rearranged by dragging one team onto another. Both come with Undo.
- Printable when paper wins. A printed bracket, and a one-page QR sign umpires can scan to start scoring.

The Hard Parts
A guarantee that holds for any number of teams
"Every team plays at least three games" is easy to promise and surprisingly hard to keep. With 16 teams the bracket is tidy. With 22, or 13, or 27, byes appear, some teams lose early in lopsided halves of the bracket, and a naive generator quietly leaves someone with two games and a long drive home.
We didn't want to believe the guarantee held. We wanted proof. Our test suite plays every bracket format for every team count from 4 to 36, with random winners in every game, over and over, and checks that every team got the games it was promised. If a change to the bracket code ever breaks the guarantee for any combination, that test fails before the change goes anywhere near a real tournament.
Brackets are worked out from scores, never written down
In most bracket tools, when a game ends, the winner's name is copied into the next game. That works until someone enters a score wrong. Fix the score and the wrong team is still sitting in the next round, and possibly the round after that.
In Ribby Sports, a game only stores its structure (which game's winner or loser fills each side) and its score. Who plays whom is worked out from the scores every time the bracket is shown. Correct a score and the whole bracket downstream corrects itself, because nothing was ever copied.
Scoring on one bar of signal
Ballparks have terrible cell service. An umpire taps Save, the phone spins, and the signal drops. Did the score go in? Tapping Save again shouldn't record it twice.
Every score an umpire submits carries a unique key. If the phone retries, the server recognizes the key and acknowledges the score it already has instead of saving it again. If two people score the same game at once, the second one is told the game changed rather than silently overwriting the first. When the signal comes back, a Retry button sends the score, and it lands exactly once.
The scoring code is protected too: repeated wrong guesses lock that phone out for a while, so a six-digit code can't be guessed by brute force, and the organizer can close scoring or issue a new code at any time.
A live page for a whole park, on a small budget
When a few hundred people have the bracket open on their phones, "live" can get expensive fast. Each open page checks for news every 30 seconds by asking only for a version number, and downloads the bracket only when something has changed. Phones at the same version share one cached answer from the edge network, so the database answers about once per tournament every few seconds, no matter how many people are watching. A locked phone doesn't check at all, and picks up the latest the moment it wakes.
Built for more than one organizer from day one
Ribby Sports is sold to many organizers, and one organizer's teams and scores must never show up in another's tournament. Rather than relying on every query remembering a filter, the database itself refuses to connect a game to a team from another organization. On top of that, every read and write in the app goes through a single layer that scopes it to the signed-in organization, and a test fails the build if anything tries to go around it.
Trying to break it before tournament day
Before any organizer used it, we pointed two automated runs at the live site. The first tries to break it as every kind of user: organizer, umpire, spectator, and someone who shouldn't be there at all. It uses real phones' screen sizes and Safari's engine, cuts the network mid-save, and reads the QR codes off the rendered screen to check they open the right page. The second plays an entire tournament from setup to champion. Both run against a test organization, never a customer's.
The Stack
- Next.js and React, TypeScript in strict mode, deployed on Vercel.
- PostgreSQL on Railway, with Drizzle for type-safe queries and migrations.
- Better Auth for organizer accounts, organizations, and invitations.
- Resend for invitations and password resets, kept as plain notes so they land in the inbox rather than the Promotions tab.
Tournament Weekend
Ribby Sports ran its first real tournament over a weekend in October 2026: a 22-team softball tournament with a three-game guarantee, on four fields.
- 22 teams, 49 games, two days. Thirty-six games on Saturday, starting at 9:00 AM, and thirteen on Sunday, ending with the championship that afternoon.
- The guarantee held. Twenty-two is exactly the kind of team count that breaks tidy brackets. Every team played at least three games; six teams played exactly three, and two teams played seven.
- The bracket kept up. After every score, the bracket worked out the next matchup on its own, through eight rounds on the losers side. Seven games were decided by half a point, including the winners final and the losers final.
- The "if needed" game wasn't. The six seed went unbeaten, so the second championship game stayed empty, and the bracket marked it as not needed on its own.
What This Project Showed Us
Ribby Sports is the first product the studio has built for itself, and it pulls together everything we do for clients:
- Product thinking. Choosing the narrow, hard problem (tournament day) over the broad, crowded one (league management).
- Correctness where it matters. A bracket that's almost right isn't right, so the rules that matter are tested for every case, not a few examples.
- Building for the real world. Bad signal, sunlight on a phone screen, a printer at the concession stand.
- Running software people depend on. Tested against the live site, not just a laptop, before anyone relied on it.
What's Next
If you're running tournaments and want tournament day to be the easy part, take a look at ribbysports.com. And if you have your own idea for a product like this, we'd love to talk about what you're trying to build.