Inspiration

If you play padel or tennis, you know the moment: the rally is over, everyone walks back to their spot, and somebody asks "wait, what's the score?"

Counting in your head works until the first long rally, a side change, or a deuce that goes back and forth four times. Scoreboard apps exist, but they all want you to walk over to the phone and tap it after every point. With a racket in one hand and a ball in the other, you don't.

So the idea was simple: put the phone by the court, score from wherever you are, and let the phone call out the score.

What it does

Smash is a scoreboard for padel and tennis. You score with whatever is at hand: a Flic button on your bag, a cheap Bluetooth selfie remote, your Apple Watch or Wear OS watch or a tap on the board itself. After every point Smash reads out the score and who serves next, with your names if you like. It knows golden point, tie-break, match tie-break, advantage sets and side changes, and it tracks the serve, including the serving order in doubles.

How I built it

The app is Angular with Ionic, wrapped in Capacitor for iOS and Android.

The most important decision was that a match is not a score. It's a list of things that happened. Every point, side change and serve change is an event appended to a log in SQLite, and the current state is a replay of that log:

$$S_n = f(S_{n-1}, e_n), \qquad S_0 = \text{start of the match}$$

Undo means deleting the last event and replaying. There is no "reverse this point" logic anywhere, which in tennis would be a nightmare: taking back one point can un-win a game, un-win a set and hand the serve back.

The rules themselves are pure functions. A game is won when

$$p_{\text{own}} \ge 4 \quad \text{and} \quad p_{\text{own}} - p_{\text{other}} \ge 2,$$

golden point drops the second condition at deuce, and a tie-break is the same idea with 7 instead of 4.

All remotes end up in one stream, so for the scoreboard a Flic press, a volume button and a watch tap are the same thing.

The watch apps are native: SwiftUI on watchOS, Kotlin with Jetpack Compose on Wear OS. I built them as remotes with a display. The phone holds the match, renders every label and sends the watch a snapshot of plain text. The watch shows it and sends taps back. That means a new sport never needs a watch update.

For testing I use unit tests for the rules and screenshot tests for the UI, across platforms, devices, themes and languages.

What I learned

  • Store what happened, not the result. The event log made undo, the match history, rematches and the spoken announcements easy.
  • Keep the smart part in one place. Because the watch is dumb, there is one set of rules and nothing can drift apart.
  • Test on real hardware early. Simulators don't pair a phone with a watch, and the watch bugs that mattered showed up on my wrist, not on my Mac.
  • Cutting things is a feature. Workout tracking on the watch sounded great, but it would have meant HealthKit, Health Services and extra store declarations. It's out for v1, and the app ships sooner because of it.

Built With

Share this project:

Updates

Submission history