Every House Games result is calculated from three inputs that are fixed before you bet: a server seed we commit to in advance, a client seed you control, and a nonce that counts your bets. Nothing about a round can change after it starts, and you can recompute any result yourself with the code on this page.
Interactive version with copy buttons: https://www.housebets.com/en/provably-fair
How it works
Server seed: 64 random hexadecimal characters chosen by us. Before you bet you only see its SHA-256 hash. The seed itself is revealed when you rotate the pair, and you can check that it hashes to the value you were shown.
Client seed: Your half of the pair. It is set at random, and you can replace it with any word of 1 to 64 visible characters when you rotate. We committed to the server seed before you chose it, so neither side can steer a result.
Nonce: Counts the rounds played on the current pair, starting at 1. Every round uses a new nonce, so a pair never produces the same round twice.
Rotating: Rotating ends the current pair. Its server seed is revealed so you can verify every round it settled, and the next pair, whose hash you have already seen, takes over with the nonce back at 1. Each game keeps its own pair.
From seeds to numbers
A round reads bytes from HMAC-SHA256, keyed with the server seed, over the message clientSeed:nonce:block. Block 0 gives the first 32 bytes, block 1 the next 32, and so on, as many as the round needs.
Bytes are read four at a time as an unsigned 32-bit number u from 0 to 2^32 − 1, most significant byte first. Divided by 2^32 it becomes a fraction from 0 to just under 1.
Games that must not repeat an outcome (mines, traps, keno balls) pick without replacement: draw i takes position floor(u × (N − i) / 2^32) among the items not taken yet, kept in ascending order.
Card games that need an exact whole number below n skip any draw at or above 2^32 − (2^32 mod n), so no card is more likely than another.
The code below is shared by every game on this page. Run it with Node.js 18 or newer.
const { createHmac } = require("node:crypto");
const TWO_32 = 2 ** 32;
// The round's bytes: HMAC-SHA256(key = serverSeed, message = "clientSeed:nonce:block")
// for block = 0, 1, 2, … — 32 bytes per block, read in order.
function* bytes(serverSeed, clientSeed, nonce) {
for (let block = 0; ; block++) {
yield* createHmac("sha256", serverSeed)
.update(clientSeed + ":" + nonce + ":" + block)
.digest();
}
}
// Draws: 4 bytes at a time, big-endian, as an unsigned 32-bit number u (0 … 2^32 − 1).
// u / 2^32 equals byte0/256 + byte1/256^2 + byte2/256^3 + byte3/256^4.
function* draws(serverSeed, clientSeed, nonce) {
const b = bytes(serverSeed, clientSeed, nonce);
while (true) {
let u = 0;
for (let i = 0; i < 4; i++) u = u * 256 + b.next().value;
yield u;
}
}
// k of N without repeats: draw i takes position floor(u × (N − i) / 2^32)
// among the items not taken yet, kept in ascending order.
function pick(serverSeed, clientSeed, nonce, N, k) {
const d = draws(serverSeed, clientSeed, nonce);
const left = Array.from({ length: N }, (_, i) => i);
const out = [];
for (let i = 0; i < k; i++) {
out.push(left.splice(Math.floor((d.next().value * (N - i)) / TWO_32), 1)[0]);
}
return out;
}
// A whole number below n with no modulo bias: draws at or above
// 2^32 − (2^32 mod n) are skipped.
function below(d, n) {
const limit = TWO_32 - (TWO_32 % n);
for (;;) {
const u = d.next().value;
if (u < limit) return u % n;
}
}Verify a bet
In the game's Provably fair window, rotate the seed pair the bet was played on. Its server seed is revealed.
Check that the SHA-256 hash of the revealed server seed equals the hash you were shown before betting.
Take the client seed and the bet's nonce, and run the game's function below with the three inputs.
The result equals the round you played. The Verifier tab in the game does the same calculation for you.
Games
Dice
How a round is made
One draw. The roll is floor(fraction × 10,000) / 100: one of 10,000 equally likely values from 0.00 to 99.99. Your bet compares the roll with your target; the lower edge of a winning zone wins and the upper edge does not.
Payout
The multiplier is 99 ÷ win chance (in %), so every bet returns 99%.
Code
function dice(serverSeed, clientSeed, nonce) {
const u = draws(serverSeed, clientSeed, nonce).next().value;
return Math.floor((u / TWO_32) * 10000) / 100; // 0.00 … 99.99
}Worked example
Server seed d2b5b3f6b4a7d4e8c95c1a4f0e3b2a19c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3, client seed housebets, nonce 1.
dice(serverSeed, clientSeed, nonce)
// Result: 95.77
Limbo
How a round is made
One draw. The result is floor(1e8 ÷ (fraction × 1e8) × 0.99 × 100) ÷ 100, between 1.00× and 1,000,000×. Your bet wins when the result is at or above your target.
Payout
A target t is reached with probability 0.99 ÷ t, so every target returns 99%.
Code
function limbo(serverSeed, clientSeed, nonce) {
const f = draws(serverSeed, clientSeed, nonce).next().value / TWO_32;
const cents = Math.floor((1e8 / (f * 1e8)) * (1 - 0.01) * 100);
const capped = Number.isFinite(cents) ? cents : 1e8;
return Math.min(Math.max(capped, 100), 1e8) / 100; // 1.00× … 1,000,000×
}Worked example
Server seed d2b5b3f6b4a7d4e8c95c1a4f0e3b2a19c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3, client seed housebets, nonce 1.
limbo(serverSeed, clientSeed, nonce)
// Result: 1.03
Mines
How a round is made
The mines are the first m picks without replacement from the board's tiles (25, 36, 49 or 64), numbered from 0, left to right and top to bottom. The whole board is fixed before your first click.
Payout
After k safe tiles with m mines on N tiles the multiplier is 0.99 × C(N, k) ÷ C(N − m, k), so every board and every cash-out returns 99%.
Code
// Tiles 0 … N − 1, left to right, top to bottom; the mines are the first m picks.
function mines(serverSeed, clientSeed, nonce, tiles, m) {
return pick(serverSeed, clientSeed, nonce, tiles, m);
}
Worked example
Server seed d2b5b3f6b4a7d4e8c95c1a4f0e3b2a19c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3, client seed housebets, nonce 1.
mines(serverSeed, clientSeed, nonce, 25, 3)
// Result: [23, 13, 19]
Blackjack
How a round is made
Card k of the round is floor(u × 52 ÷ 2^32), always drawn from a full deck (an infinite shoe). Cards are taken in the order the table uses them: each seat's first card, the dealer's up card, each seat's second card, the dealer's hole card, then hits, doubles, split hands, burnt cards and the dealer's draws as play reaches them.
Payout
Blackjack pays 3:2 and the dealer stands on all 17s. With best play the main game returns 99.43%.
Code
// Card k of the round, each from a full deck (an infinite shoe).
function blackjack(serverSeed, clientSeed, nonce, count) {
const d = draws(serverSeed, clientSeed, nonce);
return Array.from({ length: count }, () => {
const i = Math.floor((d.next().value * 52) / TWO_32);
return "23456789TJQKA"[i >> 2] + "DHSC"[i & 3];
});
}
Worked example
Server seed d2b5b3f6b4a7d4e8c95c1a4f0e3b2a19c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3, client seed housebets, nonce 1.
blackjack(serverSeed, clientSeed, nonce, 10)
// Result: [AH, 9D, QH, 3H, 9S, 8H, 7D, 5S, 3C, AS]
Plinko
How a round is made
One draw per row. The ball goes left when the draw's top bit is 0 (u below 2^31) and right when it is 1. It lands in the bucket equal to the number of rights, from 0 to the number of rows.
Payout
Every rows and risk table is built so that Σ C(rows, k) × multiplier(k) = 0.99 × 2^rows, so each one returns exactly 99%.
Code
// One draw per row: 0 = left, 1 = right. The bucket is the number of rights.
function plinko(serverSeed, clientSeed, nonce, rows) {
const d = draws(serverSeed, clientSeed, nonce);
return Array.from({ length: rows }, () => (d.next().value >= 2 ** 31 ? 1 : 0));
}
Worked example
Server seed d2b5b3f6b4a7d4e8c95c1a4f0e3b2a19c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3, client seed housebets, nonce 1.
plinko(serverSeed, clientSeed, nonce, 16)
// Result: [1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1]
Keno
How a round is made
The ten balls are the first ten picks without replacement from 40 numbers (tiles 0 to 39, shown as 1 to 40).
Payout
Every paytable returns 99%, to within one millionth, at every risk level and every number of picks.
Code
// 10 balls from 40 numbers (tiles 0 … 39, shown as 1 … 40).
function keno(serverSeed, clientSeed, nonce) {
return pick(serverSeed, clientSeed, nonce, 40, 10).map((tile) => tile + 1);
}
Worked example
Server seed d2b5b3f6b4a7d4e8c95c1a4f0e3b2a19c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3, client seed housebets, nonce 1.
keno(serverSeed, clientSeed, nonce)
// Result: [39, 22, 32, 4, 24, 19, 15, 11, 6, 40]
Roulette
How a round is made
One draw. The pocket is floor(u × 37 ÷ 2^32): a European wheel with one zero, pockets 0 to 36.
Payout
A bet covering n pockets wins with probability n ÷ 37 and pays 36 ÷ n times the stake, so every bet returns 36 ÷ 37 = 97.30%.
Code
// European wheel: 37 pockets, 0 … 36.
function roulette(serverSeed, clientSeed, nonce) {
const u = draws(serverSeed, clientSeed, nonce).next().value;
return Math.floor((u * 37) / TWO_32);
}
Worked example
Server seed d2b5b3f6b4a7d4e8c95c1a4f0e3b2a19c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3, client seed housebets, nonce 1.
roulette(serverSeed, clientSeed, nonce)
// Result: 35
Coin Flip
How a round is made
Flip i is the top bit of draw i: 0 is heads, 1 is tails. A round of n flips reads draws 0 to n − 1; with several coins of F flips each, coin i reads draws i × F to i × F + F − 1.
Payout
A round that needs at least t of n calls right pays 0.99 × 2^n ÷ S(n, t), where S(n, t) is the number of ways to get t or more right, so every round returns at most 99%.
Code
// Flip i is the top bit of draw i: H = heads, T = tails.
function coinflip(serverSeed, clientSeed, nonce, flips) {
const d = draws(serverSeed, clientSeed, nonce);
let out = "";
for (let i = 0; i < flips; i++) out += d.next().value >= 2 ** 31 ? "T" : "H";
return out;
}
Worked example
Server seed d2b5b3f6b4a7d4e8c95c1a4f0e3b2a19c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3, client seed housebets, nonce 1.
coinflip(serverSeed, clientSeed, nonce, 10)
// Result: TTTHTHHHHT
Baccarat
How a round is made
A fresh 8-deck shoe of 416 cards is shuffled by Fisher–Yates from the top, using an unbiased draw for each swap, and dealt from position 0. Player takes positions 0 and 2, Banker 1 and 3, and any third cards follow in order, Player first. Standard drawing rules apply.
Payout
Every bet is priced on the fresh 8-deck shoe to return 99%.
Code
// A fresh 8-deck shoe (416 cards) shuffled by Fisher–Yates from the top;
// only the first 6 positions can ever be dealt.
function baccarat(serverSeed, clientSeed, nonce) {
const d = draws(serverSeed, clientSeed, nonce);
const shoe = Array.from({ length: 416 }, (_, k) => k % 52);
for (let k = 0; k < 6; k++) {
const j = k + below(d, 416 - k);
[shoe[k], shoe[j]] = [shoe[j], shoe[k]];
}
return shoe.slice(0, 6).map((id) => "A23456789TJQK"[id % 13] + "SHDC"[Math.floor(id / 13)]);
}
Worked example
Server seed d2b5b3f6b4a7d4e8c95c1a4f0e3b2a19c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3, client seed housebets, nonce 1.
baccarat(serverSeed, clientSeed, nonce)
// Result: [8S, AD, QS, 8D, TH, 5D]
Hi-Lo
How a round is made
The first card is the one you see when you bet. Every card after it comes from the stream as an unbiased number below 52, each from a full deck (an infinite deck). Aces are lowest and kings highest.
Payout
After right guesses where n₁, n₂, … of the 13 ranks win, the multiplier is 0.99 × 13 ÷ n₁ × 13 ÷ n₂ × …, so the game returns 99% at every cash-out point.
Code
// Every card is independent (an infinite deck); A is lowest, K highest.
function hilo(serverSeed, clientSeed, nonce, count) {
const d = draws(serverSeed, clientSeed, nonce);
return Array.from({ length: count }, () => {
const id = below(d, 52);
return "A23456789TJQK"[id % 13] + "SHDC"[Math.floor(id / 13)];
});
}
Worked example
Server seed d2b5b3f6b4a7d4e8c95c1a4f0e3b2a19c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3, client seed housebets, nonce 1.
hilo(serverSeed, clientSeed, nonce, 8)
// Result: [8S, 8D, 8D, 2D, 9D, 9C, 7S, 5C]
Hubie's Road
How a round is made
The road has 25 slots. The traps are the first m picks without replacement (easy 1, medium 3, hard 5, daredevil 10), and lane L is deadly when slot L − 1 is a trap. Every trap is fixed before you start crossing.
Payout
After k safe lanes with m traps the multiplier is 0.99 × C(25, k) ÷ C(25 − m, k), so every difficulty and every stop returns 99%.
Code
// 25 slots; the traps are the first m picks (easy 1, medium 3, hard 5,
// daredevil 10). Lane L (1-based) is deadly when slot L − 1 is a trap.
function road(serverSeed, clientSeed, nonce, traps) {
return pick(serverSeed, clientSeed, nonce, 25, traps);
}
Worked example
Server seed d2b5b3f6b4a7d4e8c95c1a4f0e3b2a19c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3, client seed housebets, nonce 1.
road(serverSeed, clientSeed, nonce, 3)
// Result: [23, 13, 19]
