Fairness
Every shuffle on JustPoker is committed to before you play and can be reproduced by you afterwards. This page explains the mechanism, publishes the exact algorithm and lets you verify any hand in your browser.
In a conventional online card room you have to trust that the shuffle was random and that the operator did not look at your cards or the dealer's before deciding anything. A provably fair shuffle replaces that trust with arithmetic you can check. It works in three moves, known as commit–reveal.
We generate a secret server seed and publish only its SHA-256 hash. The hash pins the seed down without disclosing it — like sealing a prediction in an envelope and handing you the envelope.
Each deck is derived from our secret seed plus a client seed you control and a hand counter. Because your input arrives after the commitment, neither side can steer the result alone.
When the seed is retired we reveal it. You hash it, confirm it matches the commitment you saw, and recompute every deck it produced. If any card differs, the proof fails.
The algorithm below is frozen: the code that deals your hands, the verifier on this page and the reference vectors all implement it byte for byte. Any change would invalidate every previously published hand.
Before a server seed can be used, we generate 32 random bytes, write them as a 64-character hex string and publish SHA-256 of that string on your Fairness & Seeds page. The seed itself stays secret. Because SHA-256 is one-way and collision-resistant, we cannot later swap in a different seed that produces the same hash.
Your client seed is combined with our server seed on every hand. You can change it whenever no hand is in progress, and the seed you choose is applied to a server seed whose hash was already published — so we cannot pick a server seed with your client seed in mind.
The nonce is a counter that starts at 0 for a seed pair and increases by one per hand. The HMAC message is clientSeed:nonce:round, so every hand with the same pair gets a different, fully determined deck. Your hand history records the hash, client seed and nonce for each hand.
HMAC-SHA256 with the server seed as key produces 7 blocks of 32 bytes. Every four bytes form one float in [0, 1). A Fisher–Yates shuffle walks the deck from position 51 down to 1, swapping each position with floor(float × (i + 1)). Exactly 51 floats (204 bytes) are consumed, in order.
Positions 0 and 1 are your hole cards, 2 and 3 the dealer's, 4 to 6 the flop, 7 the turn and 8 the river. There are no burn cards, so the deck alone determines the whole hand. The dealer's cards are held server-side until showdown.
When you rotate your seed pair, the old server seed is revealed and the pre-committed next seed becomes active. Paste the revealed seed, your client seed, the nonce and the published hash into the verifier below: it recomputes the hash and the entire deck in your browser and shows the cards that must have been dealt.
// 1. Commitment — published before the seed is ever used
serverSeedHash = SHA256( utf8(serverSeed) ) // serverSeed: 64 hex chars
// 2. Byte stream — 7 HMAC rounds of 32 bytes each
for i in 0 .. 6:
block[i] = HMAC_SHA256( key = utf8(serverSeed),
message = utf8(`${clientSeed}:${nonce}:${i}`) )
bytes = block[0] ‖ block[1] ‖ … ‖ block[6] // first 204 bytes are used
// 3. Floats — 4 bytes each, 51 floats in [0, 1)
f[k] = bytes[4k] / 256 + bytes[4k+1] / 256² + bytes[4k+2] / 256³ + bytes[4k+3] / 256⁴
// 4. Fisher–Yates shuffle
deck = [0, 1, …, 51]
for i = 51 down to 1: // k = 51 − i
j = floor( f[k] × (i + 1) )
swap deck[i], deck[j]
// 5. Card encoding
rank = card mod 13 // 0 = Two … 8 = Ten, 9 = Jack, 10 = Queen, 11 = King, 12 = Ace
suit = floor(card / 13) // 0 = ♠ spades, 1 = ♥ hearts, 2 = ♦ diamonds, 3 = ♣ clubs
// 6. Deal order — no burn cards
player = deck[0], deck[1]
dealer = deck[2], deck[3]
flop = deck[4], deck[5], deck[6]
turn = deck[7]
river = deck[8]Every game on the site is driven by the same commit–reveal scheme and the same HMAC-SHA256 byte stream. Two games extend it without changing it.
Blackjack uses your own seed pair exactly like Ultimate Texas Hold'em, but the Fisher–Yates shuffle runs over 312 positions instead of 52, consuming 311 floats (39 HMAC rounds). Each position is a card from one of the 6 physical decks. The shoe is derived anew for every round with the next nonce and never persists, so there is no card-counting edge for anyone — including us. The dealer's hole card is position 3 of the shoe; it stays on our server until the dealer's turn.
At a shared table up to five players receive cards from one deck, so the deck cannot depend on a single player's seed. Each table has its own committed server seed, published as a hash before it is used and rotated every 200 rounds. The client seed for a round is the SHA-256 of the round id and the client seeds of every seat that was dealt in, in seat order. The round id itself is the table id, the table seed hash and the round counter — all public before the deal — so nothing about the shuffle is left for the operator to choose once the players are known. Your hand history records the seed inputs and the combined seed for every live round you played.
// Blackjack — a 6-deck shoe from the SAME byte stream, only the shuffle is longer
n = 52 × 6 = 312 // 311 floats = 1244 bytes = 39 HMAC rounds
shoe = [0, 1, …, 311]
for i = 311 down to 1: j = floor( f[k] × (i + 1) ); swap shoe[i], shoe[j]
card(v) = v mod 52, physicalDeck(v) = floor(v / 52)
deal: shoe[0] player, shoe[1] dealer up, shoe[2] player, shoe[3] dealer hole,
then every hit, double, split card and dealer draw from shoe[4] onwards
A fresh shoe is derived for EVERY round (nonce + 1), so nothing carries over.
// Live table — one deck for up to 5 seats, from the TABLE's committed seed
roundId = tableId + ":" + tableSeedHash + ":" + nonce // derived, never chosen
combinedClientSeed = SHA256( roundId + ":" + seatClientSeeds.join(":") ) // seats dealt in, ascending
deck = deriveDeck( tableServerSeed, combinedClientSeed, nonce )
seat k : deck[2k], deck[2k+1] dealer : deck[2n], deck[2n+1]
flop : deck[2n+2 .. 2n+4] turn : deck[2n+5] river : deck[2n+6]
The table seed rotates every 200 rounds: the old seed is revealed, the next hash was already shown.Re-derive any round from its seeds and nonce. Pick the game: the single-player deck (commitment, 52-card order, the nine cards dealt and both hands evaluated), the blackjack shoe (commitment, opening deal, the first sixteen cards and the full 312-card shoe with its hash) or a live-table round (commitment, combined seed, every seat's cards, the dealer and the board). Hand history "Verify" links open the right mode with the fields filled in.
Choose the game, then copy the values from the hand's detail page or from a revealed seed. Everything is computed in your browser.
Results appear here.
You will see whether the server seed matches its published hash, the full shuffled deck, the nine cards that were dealt and which hand won. Use Load example to try it with a published test vector.
These inputs and outputs never change. They are checked by our automated tests on every release, and an independent implementation of the algorithm must reproduce them. Each row opens in the verifier.
| # | Server seed | Client seed | Nonce | First nine cards | Deck hash | Action |
|---|---|---|---|---|---|---|
| 1 | 3f9a1c8e6b…9d0f2a | justpoker | 0 | 5c 8c 5h 7s 6s Ts Ks As 3c | b971417169…f39c5e | Verify |
| 2 | 3f9a1c8e6b…9d0f2a | justpoker | 1 | 6h 5h 7h 3h 4h 2s Tc 8d 7d | 486e5b7015…b2d62f | Verify |
| 3 | c0ffee00de…abcdef | player-client-seed_01 | 12345 | Ad As Tc 5h 7h 5s 8d Kc 6s | b65678656a…1df056 | Verify |
| 4 | 0000000000…000000 | a | 0 | 8d Qc 6s 5h 3d Ts 6c 2s 4d | 24a4549278…cede1c | Verify |
The first sixteen cards in deal order (card identity only) and SHA-256 of the whole shoe's raw values.
| # | Server seed | Client seed | Nonce | Decks | First sixteen cards | Shoe hash | Action |
|---|---|---|---|---|---|---|---|
| 1 | 3f9a1c8e6b…9d0f2a | justpoker | 0 | 6 | 8d Jc Tc 4h Ts 9d 3s 3h Ah Th 8d Jc Qs 5s 6d Kc | 3d289d102e…d0f20a | Verify |
| 2 | c0ffee00de…abcdef | player-client-seed_01 | 12345 | 6 | 7d Ah Jh Tc 5h 9h Ah 3s Qh Td 8c 4s 5d 7c 8c 6s | e7195dab0b…b8ee72 | Verify |
| 3 | 0000000000…000000 | a | 7 | 8 | 3s Td 6s Qd 4c 7d Qd Js 5c 8h 4d 9h Ts 6d 2h Qh | 761e669147…f30842 | Verify |
These vectors pin the combined-seed hash and the seat deal order. Their round ids are free-form test values; at a real table the round id is always tableId:tableSeedHash:nonce.
| # | Table seed | Round id · seat seeds | Nonce | Seats | Dealer · board | Deck hash | Action |
|---|---|---|---|---|---|---|---|
| 1 | 3f9a1c8e6b…9d0f2a | round-0001alice, bob, carol | 0 | 4c Qc · 3d Td · Qh 9s | Ts 7s · 6d 3h 8c 9d 6c | 9f3978fdde…8e95d8 | Verify |
| 2 | c0ffee00de…abcdef | 7b1f0c3e-2d4a-4f6b-8e9d-0a1b2c3d4e5fjustpoker, player-client-seed_01, x, seat-4, Zz_- | 199 | Ac 5d · Js 3s · Qh Jd · Kc Kd · 9s 7s | 8h 2d · Kh Ad Ks Qd 9c | d0a3c10485…702b97 | Verify |
The deck is a pure function of three inputs: the server seed, your client seed and the nonce. The server seed is fixed the moment its hash is published, which happens before you can influence anything. If we altered the seed afterwards, SHA-256 of the revealed seed would no longer match the hash you were shown, and the verifier would report a mismatch.
We also cannot choose a favourable seed in advance, because your client seed — which we do not know when the server seed is committed — changes every deck. Nothing in the algorithm depends on the bets you place or the decisions you make during the hand.
A revealed server seed lets anyone compute every future deck for that pair, including the dealer's hole cards. That is why a seed is only revealed once it is retired. Rotation retires the active seed, activates the next seed (whose hash you have already seen) and immediately commits a fresh next seed, so there is always a published commitment waiting.
Rotation is refused while a hand is in progress, because revealing the seed mid-hand would expose the dealer's cards.
Your client seed is your contribution to the randomness. Setting your own value after the server seed hash is published proves that the deck could not have been pre-selected: we would have needed to know your seed in advance. The default is random; typing something of your own makes the guarantee personal rather than a matter of trust in our generator.
Changing the client seed never creates a new server seed. The nonce keeps counting and each hand records the client seed it was dealt with.
Yes. Every hand in your hand history stores the server seed hash, the client seed and the nonce it was dealt with. Once the seed pair has been rotated, the hand's detail page links straight to this verifier with the values filled in — in blackjack mode for a blackjack round. Hands dealt with the currently active pair can be verified as soon as you rotate.
Live-table hands are dealt from the table's own seed rather than yours. Their detail page shows the table seed hash, every seated player's client seed and the combined seed, and links to the live-table verifier once the table seed has dealt its 200 rounds and been revealed.
No. The verifier runs entirely in your browser using the same open code that runs on our servers. Nothing is transmitted or stored. The "Copy verification link" option simply places the values you typed into the page address so you can share or bookmark the check.
Yes. The algorithm above is fully specified and uses only standard SHA-256 and HMAC-SHA256. Any language with those primitives can reproduce a deck in a few lines. The reference vectors on this page are fixed; an independent implementation must reproduce them exactly.
If a verification ever fails and you believe the inputs are correct, email compliance@justpoker.example with the hand reference.
See your active server seed hash, set a client seed of your own, rotate to reveal past seeds and verify every hand you have played.