THE PROBLEM WITH LEADERBOARDS
The first version of GridJoy had a global leaderboard. XP, streak, total solves — ranked against everyone. It was fine. Nobody really cared.
The problem with global leaderboards in a niche puzzle game is that they're not social. They're a ranked list of strangers who happened to play more than you. Beating a stranger's XP total doesn't feel like anything. There's no story to it.
What I actually wanted was to send a specific puzzle to a specific person and find out whether they could beat my time. That's a different thing entirely.
THE FIRST ATTEMPT (GIFT LINKS)
The first Arena implementation used encoded gift links: a URL that contained the puzzle seed and the sender's time, compressed into a query parameter. The recipient tapped the link, the app decoded the seed, generated the same puzzle, and showed the sender's time as a target.
It worked. It was also limited in a specific way: the link was one-way. Once you sent it, you got no feedback. Did they solve it? Did they beat your time? The sender had no way to know unless the recipient explicitly told them.
That limitation matters because the interesting moment in a challenge is the reveal — seeing whether you won or lost, not just playing the puzzle in a vacuum.
THE SECOND ATTEMPT (FIRESTORE CHALLENGES)
The current Arena system stores challenges in Firestore. When you create a challenge, it writes a document with your solve time, the puzzle seed, and a 24-hour expiry window. The recipient gets a link. When they solve it, their time is written to the same document.
Both players can then see the result. The sender gets a notification (when the challenge resolves) and can see the head-to-head on the Arena dashboard. The recipient sees immediately whether they beat the target.
This is what the gift link version was missing: a shared record that both players own. The puzzle is the same, the time target is the same, and the result is visible to both sides.
PUBLIC ARENA
The friend-challenge system is for people you know. Public Arena is different: a challenge anyone can attempt, with a leaderboard of everyone who solved it within the 24-hour window.
Public Arena puzzles are seeded from the challenge creator's solve — same puzzle, same difficulty, same layout. The creator's time becomes the bar everyone tries to beat. At the end of the window, the fastest solver wins.
This is closer to the original leaderboard concept, but with one important difference: the puzzle is anchored to a specific person who posted a specific time. You're not competing against anonymous totals — you're trying to beat a real solve by a real player. That's enough story to make the competition feel like something.
WHAT WE LEARNED
The hardest part of building Arena wasn't the Firestore architecture or the deep-link handling or the expiry sweeper. It was finding the right unit of competition.
XP is a bad unit because it accumulates over months — a new player can never catch an old one. Streak is a bad unit because it rewards consistency, not skill. Solve time on a specific puzzle is a good unit because it's bounded: the puzzle has a difficulty, the time has a natural scale, and any two players can compete on equal terms regardless of how long they've been playing.
The goal was to make puzzle-solving feel social without making it competitive in a way that punishes casual players. A challenge is an invitation, not an obligation. You can ignore it. You can accept and fail without losing anything. That low-stakes design took several iterations to get right — but it's what makes Arena feel like sharing rather than competing.