Varun Kulkarni
All projects

BracketLab

Fantasy tennis bracket challenge with live scoring, private groups, and global leaderboards.

  • Web App
  • Consumer Product
  • UI/UX

Fantasy tennis, without the spreadsheet

BracketLab is a March Madness-style bracket challenge for Grand Slam tennis. Players predict every match in the men’s or women’s draw, join private groups, and watch their picks turn into points as the real tournament unfolds.

I wanted it to feel like a consumer sports product rather than a statistics dashboard. Most people arrive through a friend’s invite link, often on their phone, and may never have filled out a tennis bracket before. The interface had to explain itself quickly, make a very large prediction feel manageable, and remain interesting long after the picks were locked.

500+
active players
15+
countries represented
24h
after launch
127
picks per draw

Within 24 hours of launch, more than 500 active players from over 15 countries had used the platform. More encouragingly, completion held up late in the picking flow, and engagement continued after submission as players returned for results and standings.

Making 127 picks feel manageable

A full tennis draw contains 127 matches. Showing all of them at once creates a beautiful poster and a terrible mobile interface. I divided the draw into four quarters and a finals section, so each screen has a clear local task while still preserving the logic of the bracket.

Picking is tap-only—there is no drag interaction to learn. The selected player advances into the next round immediately, progress updates continuously, and saves happen in the background. An optional auto-fill advances the better seed through every empty matchup, giving casual players a useful starting point without making choices for people who want to build the whole draw themselves.

The dependency-aware picking flow
01Choose a playerOne tap advances the winner.
02Build downstreamThat player becomes available in the next round.
03Change safelyImpossible later picks are cleared automatically.

The subtle difficulty is correction. If someone changes an early-round winner, every later pick that depends on that player may now be impossible. BracketLab walks up that player’s advancement path and removes only the invalid descendants. The user can revise one decision without rebuilding unrelated parts of the draw.

Scoring every round fairly

Correct picks are worth 1, 2, 4, 8, 16, 32, and 64 points as the tournament progresses. Doubling the value each round creates a useful balance: there are fewer opportunities to score late, but the total number of available points remains exactly 64 in every round. Early knowledge matters, and a great finals prediction can still reshape the leaderboard.

Each round contributes the same 64 possible points
Round 164 picks × 1 point
Round 232 picks × 2 points
Round 316 picks × 4 points
Round of 168 picks × 8 points
Quarterfinals4 picks × 16 points
Semifinals2 picks × 32 points
Final1 pick × 64 points
Perfect draw448 total points

In code, the rule is compact: points = 2^(round − 1) for each correct pick. The harder part is producing a stable ranking when hundreds of brackets share similar outcomes. BracketLab sorts by points, then correct picks, then a final-score tiebreaker. Identical results share the same competition rank.

For the tiebreaker, each player predicts the final set by set. I compare that prediction with the real score using game-level distance across aligned sets, plus a 13-point penalty for every missing or extra set. Thirteen represents a full set at its largest legal distance, so predicting the wrong match length cannot accidentally beat a structurally correct score. The same validator understands the different best-of-three and best-of-five formats for the women’s and men’s draws.

Turning picks into a live competition

Submission is only the beginning of the product. Players can create private groups, share an invite link, compare brackets, and move between group and global leaderboards. During the tournament, a match ticker connects each real result to the player’s pick and shows the points gained or the path that was lost.

The bracket is submitted once; the competition lasts two weeks
01PickComplete a men’s or women’s draw
02GroupInvite friends with a shareable link
03FollowSee live results and graded picks
04CompeteClimb group and global standings

Picks and champion predictions stay sealed until the tournament locks. This matters socially: nobody can wait, inspect the field, and copy a rival’s strategy. The lock is checked against server time, and row-level security keeps other players’ data private rather than merely hiding it in the interface.

I treated small feedback states as part of the game. Correct picks become visibly successful, eliminated paths recede, ranks use country flags and clear numeric hierarchy, and the leader’s position feels distinct without turning the experience into a casino. The tone is competitive, but still closer to an editorial tennis special than a betting app.

Built for the tournament spike

Bracket products have an unusual traffic shape: a rush of signups before the draw locks, then repeated leaderboard reads whenever matches finish. I built BracketLab with Next.js and Supabase, keeping game logic in typed server code and the database focused on durable invariants.

Rules are enforced in layers, not trusted to the interface
BrowserFast bracket UI and read-only public data
Server actionsAuthentication, lock times, and game rules
Scoring layerCached standings with per-viewer privacy
PostgresNormalized picks, constraints, and RLS

The scoring layer reads normalized brackets, picks, results, players, and tiebreakers, then computes complete leaderboard rows. That expensive, viewer-independent result is cached once per data change. A lightweight privacy pass is applied afterward for the current viewer, so hiding sealed champion picks does not require rescoring the entire field on every request.

Server actions authenticate every write and enforce ownership, tournament lock times, and bracket completeness. Database constraints and triggers provide a second backstop: they reject inconsistent picks and clear dependent results even if application code fails. Reads page beyond the data provider’s default row limit, so the architecture is not quietly capped at the first thousand records.

What I took away

BracketLab was a lesson in designing for sustained participation. Getting someone to register is easy compared with getting them through 127 decisions and giving them a reason to return for two weeks. The launch numbers mattered, but the low late-stage drop-off and continued engagement were better signals that the core loop worked.

The most successful decisions joined product and engineering: dividing the draw into manageable sections, clearing impossible picks automatically, keeping every round valuable, and making live scoring fast enough to feel immediate. None of those features is ornamental. Together, they make a complicated tournament feel like a simple game.