Case study · Footy26
One summer. 104 matches. 20,451 predictions.
Footy26 is a World Cup 2026 bracket game I designed, built, and operated end to end, as a nights-and-weekends passion project. 273 people played. 146 finished a full 104-match bracket. 43 of them called Spain.
- players
- 273
- complete 104-match brackets
- 146
- predictions scored
- 20,451
- days live on the tournament clock
- 39
The TL;DR
What the season proved
Eight learnings, each earned in production, spanning the five pillars of the project: data, marketing, product insight, engineering, and entrepreneurship. The chapters below tell the step-by-step story behind every one.
- 01
A deadline sells better than any feature.
60% of players joined in kickoff week; complete brackets rose 55% on deadline day
Chapter 3 Marketing - 02
Friends are the growth channel.
80% joined a friend league; 7 in 10 arrived in a league someone else created
Chapter 4 Marketing - 03
Started means finished when the path is short.
81% of bracket builders completed all 104 exact-score picks
Chapter 3 Insights - 04
Ship on the signal, mid-season.
The bonus-pick prompt converted 87% of the players it reached
Chapter 4 Insights - 05
A live event is a retention engine.
9.5-minute sessions at a 22.5% bounce rate; week-6 return still at 9.9%
Chapter 4 Data - 06
The pocket is the product.
86% of player pageviews were mobile; the desktop share in my raw data was mostly me
Chapter 4 Data - 07
Operate like production from day one.
A day-2 framework race was contained in 48 hours: recovery first, then root cause, then a guard test
Chapter 4 Engineering - 08
Build the platform, ship the tenant.
The chassis is sport-agnostic; every season reviewer asked for the next bracket
Chapter 6 Entrepreneurship
The rates a growth team asks for
- visitor to account conversion
- 45%
- of builders finished all 104 picks
- 81%
- of players arrived in kickoff week
- 60%
- bonus-prompt conversion
- 87%
- week-1 return rate, kickoff cohort
- 22.7%
- bounce rate across the run
- 22.5%
- average session length
- 9.5 min
- joined a friend league
- 80%
Chapter 1 · Entrepreneurship
The bet
The 2026 World Cup was the biggest ever staged: 48 teams, 104 matches, hosted across the United States, Canada, and Mexico. The prediction games built for it split the group stage from the knockouts, buried the fun under ads, or treated playing with your actual friends as an afterthought. My bet: one clean bracket, built around private friend leagues, could turn casual viewers into a summer-long ritual.
This was a project built for fun, passion, and learning, not money. Free to play, no ads, no paid tier; the only revenue surface is an optional tip jar. That freedom set the quality bar: every decision could optimize for the player.
Chapter 2 · Engineering
Built in the margins
First commit May 1. First kickoff June 11. The six weeks in between were nights and weekends around a full-time role: feature-complete for a friends beta about a month before kickoff, then hardened for strangers in the final stretch. What shipped: 47 routes in 12 languages, an installable PWA, one-tap Google sign-in, the 104-match bracket with its cascading knockout tree, bonus picks, private leagues with invite links, live leaderboards, a share-card studio, an AI news desk, a public stats hub, and a penalty-shootout mini game standing in for the 404 page.
A design system, not a pile of screens
Every surface draws from one hand-rolled token catalog: an OKLCH palette, type scale, and status colors in a single theme block, consumed by a small shared primitive layer. Cross-surface consistency is a written contract, not a habit: one documented pick-vs-result grammar covers every place a pick is rendered, and a unit test pins the shared treatment so surfaces cannot silently drift.
Shared data, private by construction
A social game runs on shared data, so privacy is structural, not polite. Row-Level Security is forced on all 21 tables; social surfaces share counts, never values (a league roster shows "98 of 107 picks", never the picks); and every table’s exposure is declared in a manifest that CI verifies against the real schema on every change. A future competition cannot inherit the World Cup’s visibility rule by accident: the rule is a required field with no default. 148 adversarial probes and a weekly fresh-context audit kept the whole thing honest.
Pressing share
The hardest ship of the six weeks was not technical. Posting a personal project to LinkedIn, something made for the love of it rather than for work, took real vulnerability. I hit publish in late May, once a two-day adversarial security audit signed off the product as safe to promote to strangers.
Security by construction
01Row-Level Security forced on all 21 tables, every write routed through locked-down SECURITY DEFINER functions, and column-level triggers guarding server-owned fields. 148 adversarial attack probes run in CI, and a fresh-context audit re-derived the threat model from scratch every week of the live window.
A scoring engine proven by simulation
02The 104-match scoring rules live in TypeScript and SQL, and CI plays an entire simulated tournament (eight seeded players, penalty shootouts included) to assert the two engines agree to the point. 20,451 real predictions were scored without a single scoring dispute.
The merge is the deploy
031,553 unit tests, 48 end-to-end specs, and 73 idempotent migrations. Merging to main applies migrations and ships to production; smoke tests then run against the live site. Documentation drift fails the build: change a subsystem without updating its architecture doc and CI stops the PR.
Instrumented to the fiber
04114 documented analytics events with PII stripped at the source, tagged exception capture on every error path, and live-debugging playbooks that can read React’s internal fiber state in a running production tab. When something got weird, I could name it in minutes.
12 languages for five cents a PR
05Locale files with 1,947 keys each, in 12 languages, kept in sync by an incremental machine-translation pipeline that only translates missing keys: about five cents and under a minute per PR, with key parity enforced in CI.
An AI news desk for $1.07
06A daily AI-written digest and curated headlines from a 22-publisher allowlist, refreshed hourly by cron. Total model cost for the entire tournament: $1.07.
The system
One person operating all of it, with CI as the second pair of hands
Chapter 3 · Marketing · Data
Launch week: the wave
Growth was engineered around one moment: the bracket lock at first kickoff, June 11 at 19:00 UTC. A five-email countdown drip anchored to that deadline, invite links that unfurled into a league preview, and a printable schedule people could stick on the fridge all pointed at the same date. 60% of all players joined in kickoff week.
The deadline did the selling. Over the final day before the lock, completed brackets surged from 94 to 146, up 55%, with the last-call email converting 32% of the players it caught mid-bracket into finishers. The last bracket was completed eight minutes before kickoff. Across the whole campaign the system delivered 1,034 emails with zero spam complaints.
The 15-minute budget
The product’s depth is also its steepest ask. Footy26 wants an exact scoreline for all 104 matches: that is what makes every match matter and every leaderboard swing exciting. But in the expanded 48-team format, it is a real demand on a passerby’s attention. I treated about fifteen minutes as the ceiling a curious visitor would give the game, and spent design effort against it: two taps per pick with an empty save defaulting to 1-0 (visibly hinted), auto-save in the visual editor, and resume-anywhere progress.
New players per week
Accounts / UTC week
60% of all players joined in kickoff week. The lock at first kickoff was the single event the whole growth motion pointed at.
From first visit to a finished bracket
Analytics + production DB
45% of visitors created an account. 80% of bracket builders went the full 104 picks: once people started, they finished.
Google auditioned the site, then kickoff came
Search impressions / day · Search Console
Google surfaced the schedule, group, and team pages more each day through the run-up: 17,495 impressions peaking at 2,723 on kickoff day, with queries in a dozen languages and 10 of 12 locales earning clicks (a third landed on non-English pages). Once play began, Google's own live coverage took the results page, and what remained was brand and long-tail demand: 167 clicks in total, with the site ranking #1 for its own name.
Chapter 4 · Insights · Data · Engineering
39 days live: operating a heartbeat
From kickoff to the Final, Footy26 ran on the tournament clock: scores ingested every minute, leaderboards moving during matches, the knockout tree resolving round by round against 146 frozen brackets. Sessions averaged nine and a half minutes at a 22.5% bounce rate, and the spectator surfaces out-drew the editor: more players watched league standings move than ever touched their own bracket again after the lock.
Stoppage time on day two
Operating started immediately. Two days in, some dashboards were hanging on their loading skeletons: a rare race in the framework’s streaming hydration that only live-traffic timing could trigger. The response became the playbook. Bounded self-healing shipped before the root cause was known, turning a stuck page into a slow one; live probes then read React’s internal fiber state inside stalled production tabs until the race had a name; and every closed defect left a guard test in CI so the class cannot recur. Contained in 48 hours, cured for good by a framework upgrade weeks later, and the diagnostic playbooks are now part of the repo.
Shipped on signal
The live window is where instrumentation earned its keep. Every user-facing flow emits an event, so when behavior surprised me, the product changed while the tournament was still running:
The invisible last step
01Signal
Players finished all 104 match picks, watched three of four bracket cards flip to "Complete", and stopped, never discovering the three bonus picks.
Shipped
A prompt at the exact moment the 104th pick saves, a bonus status card on the summary, and a persistent "All 3 bonus picks saved ✓" state replacing a dead grey button.
Result
The prompt converted 87% of the players it reached. Bonus completion (85% of builders) ended ABOVE full-bracket completion.
As shipped

Picks at a glance
02Signal
The #1 in-tournament question was "who did I pick, and what scoreline?", costing a three-tap trip to the bracket, repeated several times a day. Then a player asked to see leaguemates’ picks the same way.
Shipped
A "Your picks today" digest at the top of the dashboard, then leaguemate pick grids and tap-to-compare, two days later.
Result
114 players used the digest (4,769 views); league standings stayed the most-fired event in the app.
As shipped
Daily Kick
03Signal
Late joiners faced a 15-day dead zone between the lock and the Second Chance bracket, with nothing to do daily.
Shipped
A one-tap daily poll with an animated global and league reveal, plus public archive pages. Works for every segment: leaders, busted brackets, and brand-new arrivals.
Result
299 votes from 81 players across the run, on top of its SEO surface.
As shipped
Second Chance
04Signal
A real cohort arrived after the lock with no bracket to play, and busted veterans needed a reason to stay.
Shipped
A knockout-only bracket seeded from the real 32 qualifiers: one perfectly fair board where late joiners and veterans pick with identical information.
Result
101 players entered, and the app stayed alive for its newest users.
The network effect, by construction
The social loop was designed, not hoped for. Every new account auto-joins a global league, so nobody ever sees an empty leaderboard. Invite links unfurl into a league preview and complete the join after sign-in. The result: 80% of players joined a friend league (median size 5, largest 27), and 7 in 10 players arrived in a league someone else created. The product’s best growth channel was a friend with a bracket.
Built at a desk, played in a pocket
My raw analytics read roughly half desktop, but that desktop share was mostly me: filter out my own operating sessions and dev traffic, and players were three-quarters mobile before kickoff and 86% mobile across 32,500 tournament pageviews. The desktop experience I polished at my desk was, in effect, built for an audience of one. The tip jar taught the lesson best: it lived in a desktop sidebar, so most players never saw it. A mobile-visible tip tile shipped mid-tournament, and design for the pocket first became the standing rule for every surface that followed.
The tournament heartbeat
Daily active users · Jun 1 – Jul 25
Deadline pressure beat the football itself: the biggest day was the eve of the bracket lock, not kickoff. The Second Chance bracket and the knockouts brought the second wave, the Final lifted engagement 2.2x day-over-day, and then a point-in-time product ended on schedule.
- leagues · median size 5, largest 27
- 41
- entered the Second Chance bracket
- 101
- daily-poll votes across the run
- 299
- of schedule visitors downloaded the wall planner
- 56%
Did they come back?
Kickoff cohort · n=414
Retention held nearly flat from week 1 through the Final instead of decaying: the tournament itself was the retention hook, and one in ten of the kickoff wave was still returning in the last week.
Where visitors came from
First-touch channel
Reddit alone out-drew Google search, word of mouth carried the rest, and AI assistants (ChatGPT, Perplexity) showed up as a real 2026 channel. One UTM code traced back to a paper flyer in an apartment building.
Where the game was played
Player pageviews by device, my sessions excluded
May, pre-kickoff
Live, Jun 1 - Jul 25
Players were a phone audience from the first week and only more so once the tournament went live. The desktop share in the raw analytics was mostly my own operating sessions, and the tip jar in a desktop sidebar proved which surface mattered.
Chapter 5 · Insights · Marketing
The Final, and the VAR review in 24 hours
Spain beat Argentina 1-0 on July 19. Of the 146 complete brackets, 43 had called the champion from day one, a 29.5% hit rate, while 60.3% watched their pick go out before the Final. The global leaderboard was decided by 55 points, 756 to 701.
The tournament’s emotional peak is a 24-hour window, so the send had to land that night. "Footy26 VAR: Tournament Review", the per-player season recap covering placement, accuracy, round-by-round scoring, and bonus verdicts, went from approved spec to production sends in about 24 hours with zero new database migrations. Six pull requests shipped on Final day itself, and the thank-you edition reached all 177 reachable bracket builders that evening. Players who reviewed the season rated it 4.5 out of 5.
Who we crowned before a ball was kicked
146 complete brackets
43 brackets (29.5%) called Spain from day one. 60.3% saw their champion eliminated before the Final ever kicked off, which is exactly what made the leaderboard worth watching.
- of complete brackets called Spain
- 29.5%
- decided the global leaderboard
- 55 pts
- from Wrapped spec to production sends
- ~24 hrs
- season rating from player reviews
- 4.5/5
Extra time
The moments worth keeping
A bracket game lives or dies on its moments. These are the ones the product staged on purpose, captured as they shipped.
Kickoff
01When the tournament went live, the whole app paused for a 3-2-1 countdown, a confetti drop, and a personal strip: your champion, your sealed picks, your leagues. Shown exactly once per player.
Second Chance opens
02
June 28: busted brackets and late arrivals got a fresh knockout board seeded from the real 32 qualifiers, announced with a one-time sheet counting down to its own lock. 101 players entered.
Footy26 VAR: Tournament Review
03Within 24 hours of the Final, every builder got a personal full-time review: where they finished, how sharp they were round by round, and a verdict on their champion call. Players rated the season 4.5 out of 5.
Share cards
04
Five card kinds in three formats each, rendered server-side per player: brag, underdog, rivalry, champion, and the match call, the one players reached for most. Nearly every card went out in the square format, straight to the group chat, and league invite links (shared 163 times) did the heaviest social lifting.
The penalty shootout 404
05A canvas penalty mini game standing where an error page should be: drag the ball, pick your corner, and the keeper sharpens as your streak climbs. 83 shootouts from 38 players, 15 of them started from a wrong URL. Still live.
Take your penaltiesChapter 6 · Entrepreneurship
What it becomes
The sharpest thing the season taught me: I did not build a World Cup app. I built a social-prediction platform and shipped a World Cup app as its first tenant. The auth, leagues, invites, scoring rails, email machine, observability, design tokens, and 12-language chassis are sport-agnostic, and the privacy model was deliberately built so a second competition cannot cut corners: shared data stays counts-not-values, and every new competition must declare its own visibility rule.
The players saw it too: every season-review respondent independently asked for tennis and NBA brackets next. The architecture work is specced, with rolling per-round lock windows replacing the single global lock so a season stays alive for late joiners, and the 2027 Women’s World Cup, same sport, same flags, same audience, is close to a configuration change. The bracket is the beginning.
The demo is still on the pitch
Footy26 stays live as a playable demo, and the platform it proved out is what I am building next. If you want product built with this kind of care, from strategy to shipped and operated, let's talk.
- Role
- Solo: product, design, full stack, growth, ops
- Timeline
- First commit May 1 · Final Jul 19, 2026
- Stack
- Next.js · Supabase · Vercel
- Status
- Tournament complete · playable demo live
Every figure on this page is an exact production number, pulled 2026-08-11 UTC (traffic, device, poll, share, and penalty figures re-verified 2026-08-19 UTC) from the live Postgres database, PostHog analytics, and Google Search Console (export window May 18 to Aug 17, 2026), cross-checked against dated snapshots taken during the tournament. Counts of people use the database as ground truth; behavior and traffic use analytics (consenting users only, so behavioral counts are floors, not estimates). Traffic figures count pageviews on footy26.com only, excluding localhost and preview-deploy noise, and device figures also exclude my own operating sessions where stated. The fifteen-minute attention budget is a stated design assumption, not a measurement. Product captures come from the Footy26 demo environment with simulated players: the privacy model shares counts, never a real player’s picks, and that applies to this page too.