Fair deal

How the shuffle works

Three sentences, then the code.

Every hand starts from a fresh 52-card deck, shuffled once with a Fisher-Yates shuffle. Fisher-Yates is the standard unbiased shuffle: each of the 52! orderings is equally likely.

The random numbers come from crypto.getRandomValues, the browser's cryptographic generator, fed by the operating system. Nothing about the players, the stacks, or the previous hand goes into it, and no one (including us) can predict or replay it.

The same source turns the roulette wheel, rolls the dice, flips the coin, drops the plinko balls, and picks crash points.

The shuffle

export function shuffle(deck: string[]): string[] {
  const d = [...deck];
  for (let i = d.length - 1; i > 0; i--) {
    const j = randInt(i + 1);
    [d[i], d[j]] = [d[j], d[i]];
  }
  return d;
}

The random integer

Rejection sampling, so picking from n is exactly uniform with no modulo bias.

export function randInt(n: number): number {
  const limit = 4294967296 - (4294967296 % n);
  for (;;) {
    crypto.getRandomValues(buf);
    if (buf[0] < limit) return buf[0] % n;
  }
}

Things people ask

I got rivered three times in a row. Is it rigged? No. Over a night of 150 hands, streaks like that are expected. The replayer shows the equity of every all-in, so you can see how often the "bad beat" was really a coin flip.

Where is the deck? On the host's device, which deals to everyone at the table. Guests never receive the deck or other players' hole cards, only their own and what's on the board.

Why not reshuffle after every card? It wouldn't change the odds. One unbiased shuffle already makes every card equally likely at every position.

Back to the table