SnapEX Games: provably fair
How the result of every round is fixed before the bet, the formulas of all six games and a verifier that recomputes any round in your browser.
How it works
- Before you bet, you see the SHA-256 hash of the current server seed (64 random hex characters). The seed is fixed from that moment and cannot be changed.
- The result also depends on your client seed. You can change it at any time; the hash of the next server seed is published before you choose the client seed for it.
- Every round uses the next nonce of your seed pair: 0, 1, 2… One pair is shared by all six games.
- When you rotate the pair (the “Provably fair” tab of any game), the server seed is revealed. Check that its SHA-256 matches the hash you saw, then recompute any round played on it — below or with your own script.
“My bets” on every game page shows the server seed hash, client seed and nonce of each round.
Verify a round
The check runs in your browser (Web Crypto), not on our server. The fields are filled with an example pair — not a real player’s round.
Random numbers
Every game takes as many numbers as it needs from the same stream:
hash(server_seed) = SHA256(server_seed) -- server_seed is a 64-char hex string, hashed as text
block[cursor] = HMAC_SHA256(key = server_seed (as text),
message = client_seed + ":" + nonce + ":" + cursor) -- cursor = 0, 1, 2...
words = each block split into 8 big-endian uint32 values, blocks taken in order
f[i] = word[i] / 2^32 -- 0 <= f < 1
scale(word, n) = floor(word * n / 2^32) -- integer from 0 to n-1
Formulas for each game
Dice 1 number · RTP 99%
roll = floor(word[0] * 10001 / 2^32) / 100 -- 0.00 ... 100.00 "Under X" wins if roll < X, chance = X% "Over X" wins if roll > X, chance = (100 - X)% payout = bet * 99 / chance -- chance from 0.02% to 98%
Crash 1 number · RTP 99%
crash = max(1.00, floor(99 * 2^32 / (2^32 - word[0])) / 100)
= max(1.00, floor(99 / (1 - f[0])) / 100)
P(crash >= x) = 0.99 / x -> 99% RTP for any cash-out target
A round stops by itself at ×10,000 (or where the payout reaches the $10,000 per-round maximum for your stake).
Mines 24 numbers · RTP 99%
pool = [0, 1, ..., 24] for i = 0 ... 23: take pool[ scale(word[i], 25 - i) ] out of the pool the last remaining tile is the 25th mines = the first N tiles taken multiplier after k safe tiles = floor(0.99 * C(25, k) / C(25 - N, k) * 100) / 100
Tiles are numbered 0–24 left to right, top to bottom.
Plinko 8 / 12 / 16 numbers · RTP 99%
for each row r: word[r] >= 2^31 -> right, otherwise left bin = number of "right" bounces (0 ... rows) payout = bet * table[rows][risk][bin]
The nine tables (multiplier for each bin, from left to right):
| 8 · low | 5.6 2.1 1.1 1 0.5 1 1.1 2.1 5.6 |
|---|---|
| 8 · medium | 13 3 1.3 0.7 0.4 0.7 1.3 3 13 |
| 8 · high | 29 4 1.5 0.3 0.2 0.3 1.5 4 29 |
| 12 · low | 10 3 1.6 1.4 1.1 1 0.5 1 1.1 1.4 1.6 3 10 |
| 12 · medium | 33 11 4 2 1.1 0.6 0.3 0.6 1.1 2 4 11 33 |
| 12 · high | 170 24 8.1 2 0.7 0.2 0.2 0.2 0.7 2 8.1 24 170 |
| 16 · low | 16 9 2 1.4 1.4 1.2 1.1 1 0.5 1 1.1 1.2 1.4 1.4 2 9 16 |
| 16 · medium | 110 41 10 5 3 1.5 1 0.5 0.3 0.5 1 1.5 3 5 10 41 110 |
| 16 · high | 1000 130 26 9 4 2 0.2 0.2 0.2 0.2 0.2 2 4 9 26 130 1000 |
Roulette 1 number · RTP 97.3%
number = scale(word[0], 37) -- 0 ... 36, each 1/37 pays (stake included): number x36, dozen / column x3, red-black / even-odd / 1-18 / 19-36 x2 zero loses every bet except a bet on 0
Classic Slots 3 numbers · RTP 96%
stop[i] = scale(word[i], 32) -- i = 0, 1, 2; each reel strip has 32 symbols window of reel i = strip[i][stop-1], strip[i][stop], strip[i][stop+1] (wrapping around) lines: middle, top, bottom, two diagonals payout = bet / 5 * sum of line pays
Reel strips (stop = index from 0) and line pays:
| Reel 1 | 0:melon 1:lemon 2:bell 3:melon 4:orange 5:cherry 6:seven 7:lemon 8:orange 9:lemon 10:grapes 11:gem 12:melon 13:orange 14:melon 15:cherry 16:grapes 17:bell 18:cherry 19:bell 20:lemon 21:orange 22:grapes 23:cherry 24:orange 25:cherry 26:grapes 27:gem 28:cherry 29:lemon 30:cherry 31:lemon |
|---|---|
| Reel 2 | 0:cherry 1:grapes 2:gem 3:bell 4:lemon 5:cherry 6:seven 7:cherry 8:orange 9:melon 10:grapes 11:lemon 12:grapes 13:cherry 14:lemon 15:cherry 16:bell 17:orange 18:bell 19:orange 20:melon 21:orange 22:cherry 23:lemon 24:melon 25:lemon 26:gem 27:grapes 28:cherry 29:orange 30:lemon 31:melon |
| Reel 3 | 0:melon 1:cherry 2:lemon 3:cherry 4:lemon 5:grapes 6:cherry 7:bell 8:gem 9:orange 10:cherry 11:grapes 12:melon 13:lemon 14:melon 15:grapes 16:cherry 17:orange 18:gem 19:bell 20:lemon 21:orange 22:melon 23:orange 24:lemon 25:bell 26:lemon 27:cherry 28:seven 29:cherry 30:orange 31:grapes |
Line pays (units; payout = bet ÷ 5 × sum of the lines)
- Seven × 3 — 1,000
- Diamond × 3 — 150
- Bell × 3 — 80
- Watermelon × 3 — 40
- Grapes × 3 — 30
- Orange × 3 — 25
- Lemon × 3 — 20
- Cherry × 3 — 15
- Any three of bell / gem / seven — 15
- Cherry + cherry on reels 1–2 — 6
Lines: middle, top, bottom, two diagonals.
Check it yourself (Python 3, no libraries)
import hmac, hashlib, struct
def words(server_seed, client_seed, nonce, count):
out, cursor = [], 0
while len(out) < count:
msg = f"{client_seed}:{nonce}:{cursor}".encode()
out += struct.unpack(">8I", hmac.new(server_seed.encode(), msg, hashlib.sha256).digest())
cursor += 1
return out[:count]
def scale(w, n): return w * n // 2**32
def mines(w, n):
pool, order = list(range(25)), []
for i in range(24): order.append(pool.pop(scale(w[i], 25 - i)))
return sorted(order[:n])
s, c, n = "7e347011f1144055ee29838050723a5663fa660519a1f039aca9ec17924bcbf7", "80ca3b044457", 329
w = words(s, c, n, 24)
print("hash ", hashlib.sha256(s.encode()).hexdigest())
print("dice ", scale(w[0], 10001) / 100)
print("crash ", max(100, 99 * 2**32 // (2**32 - w[0])) / 100)
print("roulette", scale(w[0], 37))
print("plinko16", sum(x >= 2**31 for x in w[:16]))
print("mines(3)", mines(w, 3))
print("slots ", [scale(x, 32) for x in w[:3]])
For the example pair it prints the same results as the verifier above.
Return to player
Mines 99%, Crash 99%, Dice 99% (exactly 9900 / 10001 = 98.99%), Plinko 98.9–99.1% depending on the table, Roulette 97.3% (European, single zero), Slots 96.07%.