How we roll the dice — and why it's fair
Are online backgammon dice rigged? Here's exactly how our server generates rolls: cryptographic, unbiased, and identical odds to physical dice.
Let’s start with the question every online backgammon player has thought at least once, usually right after rolling 6-1 when they needed anything else: are the dice rigged?
It’s a fair thing to be suspicious about. So instead of telling you “trust us,” we’re going to show you exactly how our dice work — in plain language for everyone, and with the actual code for the skeptics who write software for a living. By the end you’ll know more about our dice than you know about the wooden ones in your closet.
The dice are rolled on the server, not on your phone
When you tap to roll, your device doesn’t decide anything. It sends a request to our server, the server generates the dice, and it sends the result back. The outcome is fixed the instant it’s created.
This sounds less trustworthy at first — “wait, you roll the dice?” — but it’s the opposite. If dice were generated on your phone or in your browser, a tampered app could simply roll whatever it wanted. Doubles every turn. By centralizing rolls on a server we control and can audit, no participant can lean on the dice — not you, not your opponent, and not us mid-game.
Predictable randomness vs. cryptographic randomness
Here’s where most “random” generators quietly go wrong.
Many programming languages ship a default random function that is fast but predictable. It starts from a single number (a “seed”) and produces a fixed sequence from there. Anyone who learns the seed — or simply watches enough rolls go by — can start predicting what comes next. For a competitive game where points and pride are on the line, that’s unacceptable.
We don’t use that kind of generator. We use Go’s crypto/rand — the same class of randomness used to generate encryption keys that protect bank traffic. It’s drawn from the operating system’s entropy pool, it cannot be seeded, and it cannot be predicted from past rolls. There’s no hidden sequence to reverse-engineer, because there is no sequence.
A couple of specifics for the engineers reading, accurate as of Go 1.26 (the language our backend runs on):
- The randomness source can’t be silently swapped for a weaker one. As of Go 1.26 a secure, OS-backed source is always used; nothing in our code or our dependencies can quietly downgrade it.
- There is no fallback to guessable values. If the operating system’s entropy source ever failed — practically impossible — the program crashes outright instead of limping along with bad randomness. The worst case is that the game stops, never that it cheats.
The subtle trap: modulo bias
This is the part most people never hear about, and it’s where “almost fair” dice come from.
Computers produce randomness as raw bytes — numbers from 0 to 255. The naive way to turn that into a die roll is to take the remainder after dividing by 6 (“modulo 6”). It looks correct. It is not. The range 0–255 doesn’t divide evenly into six groups, so a couple of faces come up slightly more often than the others. It’s a tiny, permanent, invisible bias — but over millions of rolls it becomes statistically real. That’s the difference between fair dice and subtly loaded ones.
We avoid it entirely. Instead of the remainder shortcut, we draw a single uniform number across all 36 possible dice combinations using crypto/rand.Int. Under the hood that uses rejection sampling: it quietly throws away the “leftover” values that would skew the result, so what’s left is mathematically uniform. Every one of the 36 outcomes is exactly equally likely. No remainder, no bias.
The actual code
We’re not asking you to take our word for it. This is the real mechanism (Go 1.26):
package dice
import (
"crypto/rand"
"math/big"
)
// cryptoRoll returns the dice for a single backgammon roll.
// Each of the 36 combinations is exactly equally likely, with no modulo bias.
// Doubles return four moves; a normal roll returns two.
func cryptoRoll() []int {
// One unbiased draw over all 36 combinations [0, 36).
// rand.Int uses crypto/rand (secure, OS-backed) and rejects the
// leftover values internally, so every outcome is equally likely.
// On Go 1.24+, rand.Int cannot return an error here, so discarding
// it is safe: crypto/rand.Read never errors and crashes the process
// irrecoverably on the (practically impossible) entropy failure.
n, _ := rand.Int(rand.Reader, big.NewInt(36))
i := int(n.Int64())
d1, d2 := i/6+1, i%6+1
if d1 == d2 {
return []int{d1, d1, d1, d1} // doubles → 4 moves
}
return []int{d1, d2}
}
For the curious: big.NewInt(36) is the full space of 6 × 6 outcomes, and i/6 + 1 and i%6 + 1 split that single number back into two faces from 1 to 6.
Doubles work exactly like physical dice
A roll is two dice. When they match — a double — backgammon gives you four moves of that value, so the function returns four entries. The odds are identical to a real pair of dice: any double comes up 1 in 6, and a specific double (say, double 5s) is 1 in 36. We don’t make doubles rarer to slow the game down, or more common to speed it up. They land exactly as often as physics says they should.
What the roll deliberately does not do
This is the heart of the “rigged” suspicion, so let’s be blunt. The dice function:
- does not read the score, the match state, or who’s about to bear off;
- does not know the stake, or who you are, or who you’re playing;
- has no comeback logic, no difficulty curve, no house-favoring nudge.
Its only input is randomness. Its only output is two fair dice. The dice genuinely do not know — and cannot know — that you needed a 6-1 and got a 2-1 instead.
Trust is earned, not asserted
We could have just written “our dice are fair” and moved on. We’d rather hand you the mechanism and the code and let you check our work. That’s the whole point of this post: transparency you can verify beats reassurance you have to swallow.
If you’ve ever sworn the dice were out to get you, you’re in good company — every backgammon player has. The difference is now you can read exactly how they’re made.
Play a game and see for yourself →
Prefer an app? Get it on the App Store or Google Play.
Ready to play?
Play Now