tourney.social

tourney.social

Tourney Social

tourney.social is a tournament platform for tennis and padel. Instead of a WhatsApp group and a spreadsheet, the whole cup lives at one link.

You open it and the bracket is there, with the court schedule and whatever score just came in. Nothing to install and no account to make, which matters more than it sounds like it should, because every signup step costs you a chunk of the people you were trying to reach in the first place.

Tourney.Social Website
Tournament Bracket
Tournament Bracket

How it started

I kept seeing the same setup. A club runs a weekend cup, and the entire tournament is a WhatsApp group plus a spreadsheet on somebody's phone.

Someone draws the bracket on paper and photographs it. The photo goes in the group. A match finishes and the photo is out of date, so eventually a new one goes up. Players scroll back looking for their court time, give up, and ask in the group instead. The organizer answers from the edge of a court, in between running the matches they are actually there to run.

In fairness, this works. It has worked for years, and nobody doing it is walking around wishing for software. But it costs one person their entire Saturday, and it means the tournament is invisible to anyone outside that group chat, which includes most of the people who might have come to watch.

I didn't set out to build a platform. I wanted to see whether the whole thing could be a URL you could put on a poster.

What it actually does

The organizer side is where the work happens. You make a tournament, add divisions (men's doubles, women's, mixed, usually split by level), enter the pairs, then either generate a draw or build the fixtures by hand when the format is odd. Scores go in as matches finish. Courts and start times live in a schedule you fill in yourself.

The public link is that same data seen from outside: a bracket for each division, group standings where the format uses them, the schedule by day, the player list, and a page for any individual match. Scores update on their own, so a spectator can leave the tab open and stop thinking about it.

There is a share sheet with a QR code, mostly because the fastest way to get a room full of people onto a link is to print it and tape it to the registration desk.

The piece I am happiest with is presentation mode. It is a full-screen deck that cycles through the draw, the standings, what is coming up next and a QR code, sized for a TV at the venue. It updates itself, so once someone puts it on a screen in the morning nobody has to touch it again.

Real Time Schedule
Real Time Schedule

How it actually works

Go on the back end (Gin, pgx, Postgres on Supabase), SvelteKit on the front. The parts worth writing about are the constraints I ended up designing around.

Live scores only travel in one direction. A spectator receives updates and never sends anything, so the whole thing rides on server-sent events rather than WebSockets: a single HTTP stream that the browser reconnects on its own when someone's phone loses signal for a minute. No Redis, no broker, nothing extra to operate.

That choice has a real cost, though. The list of who is currently listening sits in the server's memory, so if I ran two copies of the API, half the crowd would never hear about scores published by the other half. It deploys as one machine on purpose, and there is a comment in the Fly config explaining why, since a single machine looks exactly like the sort of thing someone would later "fix" by scaling up.

Scheduling is enforced in Postgres instead of in application code. An exclusion constraint refuses to store two slots whose time ranges overlap on the same court, so a double-booking is not something I have to remember to check for. There is a softer rule layered on top of it: schedule a pair within half an hour of their previous match and you get a warning you can override, because sometimes you do genuinely need to send them straight back out.

Times are stored in UTC and shown in the tournament's own timezone, which defaults to Asia/Makassar. The organizer and someone following along from another country see the same start time, the one printed on the draw sheet, instead of whatever their phone believes local time is.

The bracket is hand-written DOM and CSS rather than a library. Tennis wants per-set games with tiebreaks in the right column, and it has to stay legible on a phone held sideways at the edge of a court, and I found that easier to build than to configure around.

It's live

tourney.social is up, with a demo tournament seeded on it so you can click through a full bracket, a schedule and the live view without signing up for anything.

It is early. Draws are single elimination and round robin so far. It does nothing at all about registration or payment, which is deliberate for now, though I suspect that is the first thing anyone will ask me for.