tl,dr
What it does:
Court tablet score wall + serve clock; voice EN/FR with typed-call fallback; organizer queue/courts/rules/sponsors/results; changeover sponsor loop; offline queue + replay.
Tech Stack:
Node.js, WebSockets (ws), Web Speech API, WebGL, Vanilla JS, HTML/CSS.
Challenges:
Adjacent-court noise → reconciliation + wake word + tap-to-arm + self-mute; HTTPS-mic requirement → self-signed cert script (scripts/generate-cert.js); stale replay → monotonic seq; doubles tiebreak serve cycle.
Accomplishments:
Tests passing + e2e offline scenario; learned Web Speech confidence alternatives; next = on-device model for noisy courts, sponsor video, DB/auth for multi-venue, human-like text-to-speech
Links:
GitHub https://github.com/maouslli/RallyPoint. run instructions: cd courtside-ai then npm install && npm start
Inspiration
Community tennis tournaments rarely look like the ones on television. There is no umpire on every court, no electronic line calling, and often no reliable Wi-Fi. There is a paper draw taped to a fence, a volunteer with a clipboard trying to keep two or three courts moving at once, and players shouting scores over the net while the next match waits to find out if a court is free. We kept coming back to the public courts at Parc Jarry in Montréal as our reference point. Weekend tournaments there are joyful and chaotic, a variety of games played at the same time, constant disputes about whether that last call was 30-15 or 40-15. We wanted to build something that would actually survive that environment, a tablet mounted on the fence that players could talk to between points, and a single organizer screen that always knew what was happening on every court. That became RallyPoint: voice-scored court tablets and one organizer desk, synced live, a practical assistant for the people who actually run club tennis.
What It Does
RallyPoint gives each court a tablet and the tournament a shared brain. On the court, players see a high-contrast score wall designed to be read from the baseline, with sets, games, and points laid out clearly, the server marked in optic yellow, and the live call echoed underneath. The tablet listens continuously in English or French. Players can simply say the score — "thirty fifteen," "quinze partout," "deuce," "ad in," "game," "fault," "let" — and the wall updates, speaks the call back, and restarts the serve clock. If it is too noisy or the microphone is not available, every voice call can also be typed into the call box and goes through the exact same path, so nothing about the demo depends on perfect audio. The tablet also handles everything around the points. There is a 25-second serve clock, timed changeovers and set breaks with a full-screen countdown, a spoken "Time" call at 80 seconds, giant touch buttons for Fault, Let, Correction, and Override for when humans disagree, and a way to call the tournament director that lights up at the desk until it is acknowledged. During rests, the tablet plays a sponsor loop with colors, taglines, and a spoken line. At the desk, the organizer sees all of this in one calm screen. Court cards show status, score, server, rest countdown, and which rules version each court is on. The queue holds upcoming matches with one-tap assignment. Rules are global and versioned, so pushing Standard, No-Ad, Fast4, or a single set reaches every live court immediately. Sponsors can be edited and pushed to courts, with play counts and a dollars-per-play estimate. Results accumulate into a log with CSV export. If a court goes offline, it keeps scoring locally, queues its updates, and replays them on reconnect without rolling anything backwards.
How We Built It
We kept the stack intentionally small so the whole thing could run on a laptop hotspot at the side of a court. The server is Node with ws for WebSockets and a JSON file for persistence. The clients are vanilla JavaScript with no framework, written as plain scripts that run unchanged in the browser and in Node so the server can require the same logic the tablets use. There are three pieces that matter most. The rules engine in scoring.js is pure and tested, and it holds the hard parts of tennis: No-Ad, Fast4, set and 10-point match tiebreaks, and the four-player serve rotation in doubles tiebreaks where the engine has to know who serves from which side on every point. The voice layer in voice.js parses numbers, words, French, glued digits like "3015," and common mishearings like "five" for fifteen, and tries multiple recognizer alternatives in confidence order. The tournament reducer in hub.js is the single source of truth for courts, queue, rules, and sponsors, while each tablet remains the source of truth for its own match and reports versioned snapshots. Transport is layered for resilience. WebSocket is first, BroadcastChannel is second for file-based use with no server, and in-page messaging is third for the demo. Every tablet persists its match locally and holds an outbox that replays in order, guarded by monotonic sequence numbers so a delayed replay can never rewind a court. The night-court landing page uses vendored three.js so it works offline.
Challenges We Ran Into
The hardest problem was not hearing words, it was deciding whether to believe them. On a real club site, two courts sit ten feet apart. A confident "40-15" from the next court should not move the score on this one. Every spoken score is treated as an assertion about this match: if it equals the current score it is a confirmation, if it is exactly one point away it is applied, if it is ambiguous between server-first and receiver-first readings the tablet asks, and anything else is rejected with a clear message like "Not reachable from 30-15" plus a one-tap override in case the tablet is the one that is wrong. Layered on top are a confidence floor, an optional wake word, and a tap-to-arm mode that only listens for eight seconds after a tap for noisy days. The second challenge was the browser itself. Microphone access requires a secure context, which meant plain HTTP over the club Wi-Fi failed silently on tablets. We moved the server to HTTPS-only with an auto-generated self-signed certificate on first boot and a printed LAN address, accepting the one-time browser warning as the price of working voice. We also learned to design for failure: every rest timer, sponsor play, and point had to survive a dropped connection, which is why the outbox, local persistence, and stale-snapshot guards exist. Doubles exposed a quieter kind of complexity. In a tiebreak, the player due to serve starts, then each of the four players serves two points in rotation, ends change every six points, and the next set starts with the player who received the first point of the break. Getting that rotation wrong is exactly how real teams lose track, so the wall now names the server and receiver and the side for every point.
Accomplishments
We are proud that the system holds together as a whole rather than as a collection of features. You can assign a match from the desk, start scoring by voice on the tablet, cut the network, keep playing offline, reconnect and watch the replay land cleanly, push a rule change mid-match, finish the match, and see the result in the log and the CSV — all without refreshing a page. That end-to-end path is covered by an automated test that does exactly that sequence, alongside 43 unit tests for the engine, parser, hub, and homepage rally. We are also proud of the design. The tablet is readable and clutter-free in direct sun with a glare mode. The organizer screen has no modes to learn: queue on the left, courts in the middle, rules and sponsors on the right.
What we learned
We learned that voice interfaces for sport are less about recognition accuracy and more about trust. Every accepted call now shows a confirmation along with "undo", "correction" or "override" commands or buttons reverts up to 80 steps. We also learned how much of tennis lives in the transitions — the changeover, the set break, the wait for the director — and how much goodwill you can earn by handling those moments well with a countdown, a next-server hint, and something useful on the screen while people rest.
What's next for RallyPoint - Tennis Courtside Assistant
The roadmap follows directly from the limitations we already know. First is moving from the browser's Web Speech API to a small on-device model tuned for tennis vocabulary in wind and crowd noise, so recognition works without network access and without sending audio anywhere. Second is rounding out the sponsor loop with short video and QR codes, so a changeover can actually drive foot traffic to the shops that fund the event. Third is growing the hub beyond a single Node process with a JSON file toward a proper database, authentication, and multi-venue support for leagues that run the same format across several parks. Last but not least, we aim to import a more human-like voice model that replaces the generic text-to-speech currently employed. Beyond that, we want to close the loop for players: personal call profiles that adapt to how you pronounce scores, a post-match summary with serve-clock habits and break-point conversion, and a director view that learns which courts tend to run long.
Built With
- html/css
- javascript
- node.js
- webgl
- websockets
- webspeechapi
Log in or sign up for Devpost to join the conversation.