← Bryan Lee05 / 05

SideBet

05 / 05 · Private · Since Dec 2025

Friendly bets, kept honest.

Make a bet with friends, set the stakes, and let the ledger and leaderboard keep score.

Private. One group of friends.
Made for
One group of friends
My role
Idea, design, and code
Built with
Next.js, Prisma, Auth.js
Status
Private, one group of friends
My Bets: one open, one waiting to settle, one settled
An open bet where Side B needs $10 more
The leaderboard, with Ben marked as you
Every bet, an open bet that needs $10 more, and the leaderboard.SideBet is private, so these screens are redrawn with made-up names.
The storyDec 2025

A friendly bet is a promise made out loud. A week later, nobody agrees who took which side, how much each person put up, or who owes whom.

SideBet keeps that record for one group of friends. You write the bet as two outcomes, put people on each side, and set what each person risks. The app pairs the money at the agreed odds and says what’s still missing.

A chosen arbiter calls the result. Then the app lists who pays whom, and a leaderboard keeps the running score. A friend without an account can still join as a guest.

It started in December 2025 as a plain bet tracker. In August 2026, an automated design review graded it harshly. For one thing, money showed up in five different formats. I shipped a full redesign about 12 hours later.

The idea
One question runs the whole app: who is in, for how much, and who pays whom?
What makes it good04 ideas
01

Matching that fixes itself

Each time anyone adds or removes a stake, SideBet clears every match on the bet and pairs the money again from scratch, oldest stake first, at the bet’s odds. Money with no partner stays unmatched, so it isn’t at risk.

Because matches are rebuilt every time, they can’t go stale. An older version patched them in place, and deleting a stake could leave one side with more matched money than the other side had put up.

How the money pairs upEven money

Ada puts $30 on Side A, Cy puts $40 on Side B, then Ben puts $20 on Side A.

Ada$30$30Cy
Ben$10$10Cy
Ben$10open

Ada drops out, so SideBet starts over

Ben$20$20Cy
open$20Cy
✓ Matched money on both sides is always equal
02

One sentence says what’s missing

A bar across the top of the bet says what’s missing, like "Side B needs $10 to match Side A." Under each stake box, a line reads "Risk $20 → win $20 if matched" and updates as you type.

You know where the money stands before you commit. The payoff line uses the same math as the matcher and the settlement, so all three agree.

The balance bar on an open bet
03

The pay list comes first

Before the arbiter declares a winner, a dialog lists every payment that will follow, like "Cy pays Ada $30.00," and the total that moves. Only then does the arbiter commit.

The browser runs the same settlement code as the server, so the preview matches what actually happens. Wins and losses are netted first, so four people settle up in three payments at most.

The declare dialog, with the three payments it will create
04

Color that only means money

The brand is ink black on white. Color shows up only when it carries meaning: green is won or settled, red is lost, blue is open, and orange needs attention. Gold appears only when someone wins.

In an app about money between friends, color should only tell you the state of a bet. Both sides stay gray until someone calls a winner, so the screen doesn’t take a side. After the call, a payment to you turns green.

Bets in three states, each with its own color
The winner in gold, and the payments to Ben in green
The look06 colors
  • Ink#181818
  • Page#fafafa
  • Line#e4e4e7
  • Win#137738
  • Lose#b71824
  • Gold#7b5f06

Geist Sans in the design system, and tabular numbers for every amount.

Four written rules run the design: One Meaning, Neutral Sides, Gold Reserve, and One Formatter.

Try itA working demo · made-up data

Pick a side, then settle the bet.

Money moves only when both sides match. The ledger always adds up to zero.

SideBetPick a side
The small stuff06 details
  • One card for every edit

    Both sides live on one card. Tap an amount to change it, or tap the arrows to move someone across. It replaced four separate sections.

  • A referee with a paper trail

    Only the named arbiter can call the bet, change the call with a reason, or reopen it. Every step lands in an activity log.

  • One money format

    Whole dollars by default, cents only for payments, and the minus sign always before the dollar sign.

  • Errors you can’t miss

    The form lists every problem at once, scrolls to it, and says "Fix 2 things before you create the bet."

  • Deletes that warn you

    Deleting a bet counts the payment records it will destroy, and warns that settled payments go too.

  • Guests count too

    A friend with no account can join a side, stake, and get paid, by name alone.

Under the hoodNext.js, Prisma, Auth.js
  • Next.js 16 and React 19, with strict TypeScript. Pages load their data on the server, and only the live parts run in the browser.
  • Prisma on Postgres, and Auth.js for email and password sign-in. Nine tables hold users, bets, sides, stakes, payments, and the activity log.
  • The matcher and the settlement code are two small files, 106 and 132 lines. The settlement file loads in the browser too, which is how the pay list can show before the call.
  • Odds are stored as two whole numbers. Decimal and moneyline odds exist only on screen, and a typed moneyline turns back into a fraction.
  • Only matched money settles. Debts are netted first, then the biggest debtor pays the biggest creditor, which keeps the list short.
  • Tailwind and shadcn/ui, with color tokens named for what they mean, like win and lose. Most multi-row writes run in one database transaction.
Started
Dec 2025
Commits
48
Lines of code
9,109
Database tables
9