Tournament engine (pools, single/double elimination, standings with proper tiebreaks, withdrawals, two-court queue with ETAs and alerts), a single-process Node server with SQLite via node:sqlite and a WebSocket live board, organizer desk, QR landing page that follows the tournament phase, Dockerfile and compose with cloudflared, and documentation for deploying and running a day. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01MB7nCCAscYsb3zzkT6LHZi
5.4 KiB
Organizer guide
Everything an organizer does happens at /admin on a phone or laptop. Sign in with the organizer password (or through Cloudflare Access if your deployment uses it).
Before the day
Create the tournament. Name, date, number of courts, team size (2s/3s/4s/6s), scoring (points to, win by, cap, best of), and stages: pools then bracket, pools only, or bracket only. Notes you enter here appear on the registration page.
Print the QR code. From the tournament desk click "QR code", then Print. Put it on the check-in table, on the shelter, wherever people gather. That one code is used all day; it always points at /t/<slug> and shows whatever phase you are in.
Rule of thumb for fitting the day. A 2s set to 21 runs 12–15 minutes plus a 3-minute changeover. Two courts give you about 8 matches an hour. Twelve teams in three pools of four is 18 pool matches (about 2¼ hours), then an 8-team bracket adds 7 matches (about an hour). Pools of 4 or 5 fit two courts best; with 16+ teams consider pools of 4 and advancing only the top 2.
Check-in
Teams scan the code and fill in team name, captain, mobile, number of players, optional player names. They get a private team link on the confirmation screen with a "Text it to myself" button. The team link shows their schedule, record, and alerts all day.
From the desk you can rename a team, fix the captain or player count, remove a team, add a walk-up team, and set seeds (1 = strongest). Seeds decide pool placement (snake order) and bracket placement. If you don't seed, registration order is used.
Starting play
- Close registration. The public page switches to the roster and "schedule coming."
- Generate pools (pick the pool size) or, for bracket-only, generate the bracket. You can clear and regenerate freely until the first score is entered. A late team? Clear generated play, add the team, regenerate.
- Go live. The engine immediately assigns the first matches to the courts. Teams whose match is next see "on deck for Court 2, ~15 min" on their team page and the public board.
Scoring
Each court panel on the desk has a +/− pad. Every tap updates the running score on the public board and the TV display. When a set is over tap Mark final; the engine records the result, standings update, and the next match goes onto that court.
For best-of-3, or if you'd rather type, use "Save sets" with 21-18, 19-21, 15-9. The score must obey the rules you set (win by 2, cap, etc.); the desk tells you if it doesn't.
Corrections. Open "All matches" and save the right score on any match, even after later matches have been played. Pool standings recompute. Bracket results propagate forward only when a match is first decided, so if you correct a bracket match whose winner has already played on, also correct the affected later match.
Forfeits. The "Forfeits" button next to a live match records a forfeit by the selected team (0 to points-to for standings purposes).
Things that go wrong on the day
A team leaves. In the Teams list click Withdraw and choose:
- forfeit remaining — their unplayed pool games become forfeit wins for the opponents; played games stand. This is the fair default when a team leaves mid-pool.
- void all games — every game they played or would play is removed from standings, as if they never entered. Use this when a team leaves early and forfeits would distort the pool.
In a bracket the opponent advances automatically either way.
Rain, injury, net repair. Pause the court (with an optional reason shown on the board). The queue keeps flowing to the other court; Resume picks up where it left off.
Move a match to the other court. "Move to court" on the live match, e.g. for sun fairness or a scheduled feature match.
A team isn't back from lunch. Push back their pending match in the Queue; it drops two places and the next ready match goes on instead.
Tell everyone something. Set a banner (persistent, shown on every public page until cleared) or send a broadcast (one-time alert on every team page). Both are good for "lunch is out", "Court 2 closed 20 min", "finals start at 2:00".
From pools to bracket
When pool play is done (the desk allows it earlier, with a warning), choose how many teams advance per pool and how many wildcards fill the bracket, pick single or double elimination and whether to play a 3rd-place match, and generate. Pool winners are seeded first, then runners-up, ordered by win percentage and point ratio; wildcards are the best remaining teams by pool position and record. Byes go to the top seeds automatically.
Standings tiebreaks, in order: head-to-head (among the tied teams only), set ratio, point ratio, point differential, seed. A * next to a team on the public board means a tiebreak decided its position; hover or tap for which one.
The TV display
Open /t/<slug>/display on a tablet or TV browser and go full-screen. It shows the courts with big scores, the up-next queue, and standings, in a dark high-contrast theme, and gently scrolls between top and bottom every 15 seconds. It updates live.
After the last match
Once every match is decided the tournament marks itself Final and the QR page becomes the results page with the champion, bracket, and standings. If you need to end early, press Final yourself. The page stays up as long as the tournament exists; delete it from the Danger zone when you no longer want it public.