01 / 26

Pickle Royale

A 2v2 pickleball ladder I designed and built for my own group.

Role
Design and build, solo
Type
Personal project
Live
pickleball.dhrumilkherde.com/demo

Part one

Building the ladder

The problem, the look, and the shape of the rating system.

The problem

Me and my friends play 2v2 every week, random teams every time.

Nobody could remember who was actually any good. Rankings lived in people's heads and got argued about in the car park.

So I built the scoreboard.

Pickle Royale standings for Tuesday Night Rumble, showing Priya as reigning champion and the ranked group below her.
The shared scoreboard that replaced rankings kept in people's heads

Solo did not mean unstructured

I framed the problem, designed the match flow and rating behaviour, chose the tradeoffs, built the frontend and backend, and handled deployment.

Claude Code accelerated implementation. The product decisions, references, constraints, and quality bar stayed mine.

For visual direction, I annotated specific references in Shottr and handed over the source folder, so implementation followed evidence instead of a vague style prompt.

The form follows the moment

Log Match asks: who played, what was the score, who carried.

That is the order you decide it in, standing on court with one phone between four people.

Not the order a database would prefer.

Three taps, one phone, no database thinking

Pickle Royale Log Match on a real phone screen, team-assignment step, nobody picked yet.

Nobody picked yet

Pickle Royale Log Match on a real phone screen, Kabir and Zara vs Vikram and Rohan, win-probability bar showing.

Step one: who played

Pickle Royale Log Match on a real phone screen, score entered 11 to 7.

Step two: what was the score

Why a flat rating could not work

The AI proposed the simple version. Everyone starts at 100. Win, go to 110. Lose, go to 90. Every time.

Beat the best or worst player in the group: the same +10. It cannot tell those matches apart, so it cannot tell you anything.

Rate everyone, predict every match, and move people by how wrong the prediction was.

Beat someone you were meant to beat and barely anything happens. Lose to someone you were meant to beat and it costs you.

Hard to climb, easy to fall down. That is what makes the top of it worth anything.

Pickle Royale match history showing routine wins, an upset, and a pickle with different rating movements.
The same scoreline does not always mean the same performance

Part two

What that looks like in play

Three real matches, logged live, with the ratings that came out of them.

The matchup

Priya and Kabir, rated 1490 and 1392. Ishaan and Sam, 1056 and 979.

The model gave the underdogs an 8.5% chance.

They won 11-9. Thirty points moved each way.

David beats Goliath, on a real phone

Pickle Royale upset-alert reveal card in context on a phone screen, reading David Beats Goliath, showing Ishaan plus 30 to 1086, Sam plus 30 to 1009, Priya minus 30 to 1460, and Kabir minus 30 to 1362.
UPSET ALERT: Ishaan +30 to 1086, Sam +30 to 1009, Priya -30 to 1460, Kabir -30 to 1362fig. 03

Margin, dampened

An 11-0 counts for more than an 11-9, on a log curve, so a blowout matters without being everything.

And a strong team crushing a weak one earns less than the scoreline suggests, deliberately. Otherwise whoever is top farms the bottom of the ladder and never defends anything.

Win the match you were expected to win and the whole table moves 48 points. Lose it and the same table moves 120.

Pickled, in context

Pickle Royale result reveal card in context on a phone screen, an 11-0 skunk, reading Pickled, showing Zara plus 24 to 1228, Rohan plus 24 to 1185, Vikram minus 24 to 1125, and Ishaan minus 24 to 1062.
PICKLED, 11-0: Zara +24 to 1228, Rohan +24 to 1185, Vikram -24 to 1125, Ishaan -24 to 1062fig. 04

Who carried

In doubles a win is not split evenly, and everyone at the court knows it.

A slider from centre, 10/90 at the extremes. Drag it toward whoever did the work.

In a loss it inverts: whoever carried loses less. The split only moves points inside a team, never changes the team total.

The same slider, at rest and dragged

Before
Pickle Royale who-carried slider at rest on a real phone screen, Kabir vs Zara, reading 50/50.
At rest, Kabir and Zara 50/50
After
The same Pickle Royale who-carried slider on a real phone screen, dragged toward Kabir, reading Kabir 90%, Zara 10%.
Dragged to 90/10 toward whoever carried

Ratings are never stored, only replayed

Every rating is recomputed from the whole match log, in order, per group.

So deleting a mistyped match is safe, and one group's results cannot leak into another's.

Pickle Royale stats showing a rating journey, form, rivalries, and playstyle computed from the match log.
The match log replayed into a rating journey and player context

Part three

A player is not just a score

Claiming a player, one debugging story, and what the ladder remembers.

Invites, without a paid email service

Invites are a six character code and the share sheet your phone already has. I built email invites through Resend, then took them out of the flow. Free and open source only.

Pickle Royale invite card on a real phone screen, on an unclaimed player, offering a native share-sheet link and a copy-link fallback.
Share link or copy link, no paid email service in the path

The redirect that was never there

Google sign-in worked locally, broke in production, and curl kept telling me it was fine.

Cloudflare was serving a static asset for /auth/login before my Worker ever ran, so the redirect never happened. curl could not see it, because curl does not send Sec-Fetch-Mode: navigate, and without that header the asset layer answers differently.

Sending the browser's real headers turned a 302 into a 200 and exposed the fix: run_worker_first for /api/*, /auth/* and /demo.

Pickle Royale login screen on a real phone, Continue with Google gate.
Every request to /auth/login, answered by a static file

The ladder is not neutral, and that is the point

Before
Pickle Royale standings on a real phone screen, top of the ladder, Priya reigning champ with an 11-match win streak badge.
Top of the table, flattering
After
Pickle Royale standings on a real phone screen, scrolled further, showing Sam's 7-match losing streak badge and Wooden Spoon title.
Last place: Wooden Spoon

Then I had to let people in

The login wall blocked everyone I wanted to show it to. So there is a demo: every visit mints a throwaway group seeded with a real season, signs you in, hands you a player. Log a match, watch the ladder move.

A daily cron deletes them. Real groups are never touched.

Pickle Royale group switcher sheet on a real phone screen, showing the demo group's join code with a copy button.
A throwaway group, seeded and ready

A shared record replaced memory and debate

The group can log a match from one phone, see why every rating moved, and revisit the season as more than a leaderboard.

The honest outcome is qualitative: a working, deployed product for a known group, not a claim of market scale or growth.

Pickle Royale player profile for Priya, showing her rating journey and personal performance context.
A player became a history, not just a row on the ladder

What I would validate next

I designed around a group I knew, which made the court context unusually clear but limited how much I learned from unfamiliar players.

Next I would observe new groups logging their first match, measure completion and correction rates, and test whether the contribution slider resolves debate or creates more of it.

Built with

Frontend
React, Vite, TypeScript
Platform
One Cloudflare Worker serving app and API, D1 for data, one deploy
Auth
Google sign-in
Ratings
shared/elo.ts: base 1200, K of 40 for the first ten games then 24, contribution clamped 10/90, margin of victory on a FiveThirtyEight-style log curve
Made with
Claude Code, with design references annotated in Shottr

Thanks for walking through it

Dhrumil Kherde, product designer.

dhrumilkherde.com