Building a Provably-Fair Dice Game in 13KB of JavaScript

There’s a habit that shows up every summer when the js13kGames jam rolls around: developers reach for a dice roll or a spinning reel as their very first prototype. It makes sense. Chance-based mechanics are cheap in bytes, instantly understandable, and endlessly replayable — exactly the kind of thing that thrives under a 13-kilobyte ceiling. But the moment a coder writes Math.random() and watches a number pop out, a deeper question creeps in: how does a player actually know that roll was fair? That single worry, multiplied across millions of plays, turns out to be one of the most interesting design problems in browser gaming today.

That same question has become the backbone of an entire corner of the web, where on-chain ranked lists score the best crypto gambling sites of 2026 on exactly this kind of transparency. A resource like btc casino tracks no-KYC crypto casinos, Web3 sportsbooks, and prediction markets, ranking them by metrics that any js13k developer would recognize as honest engineering signals: 24-hour transaction volume, unique active wallets, chain support, and verifiable fairness. For someone curious about how randomness gets proven rather than merely promised, those rankings are a live field guide — every entry on the list lives or dies by whether players can independently check that the dice were never loaded.

Why Math.random() Falls Apart Under Pressure

For a casual particle effect or a procedurally generated dungeon, JavaScript’s built-in Math.random() is perfect. It’s fast, it’s free, and it costs zero bytes to import. The catch is that it was never designed to be tamper-proof. The numbers it produces are deterministic under the hood, seeded by the browser in ways a developer can’t control or verify. Nobody can prove after the fact that a given roll wasn’t quietly nudged.

That distinction barely matters in a platformer. It matters enormously the second a game asks players to trust an outcome. A 13KB dice game that wants to feel legitimate needs randomness that two parties can agree on — the player and the code — without either side being able to cheat. This is where browser cryptography enters the picture, and happily, modern engines ship it for free.

The 13KB-Friendly Toolkit for Honest Randomness

The first upgrade is cryptographically secure randomness baked right into the browser. Instead of Math.random(), a developer can call the browser’s secure random API, which fills a typed array with unpredictable values drawn from the operating system’s entropy pool. It’s a single line of code, weighs almost nothing in a zip file, and produces numbers no script can foresee. For a jam entry, that’s a near-perfect fit: maximum fairness, minimum byte cost.

But unpredictability alone doesn’t make a game provably fair. A skeptical player still has to trust that the server — or the code — didn’t peek at the random value and then choose a result that favored the house. Solving that requires one more clever idea borrowed straight from cryptography.

Commitment Schemes: Promising a Roll Before Revealing It

The trick that makes “provably fair” actually provable is something called a commitment scheme. The concept is elegantly simple. Before the player ever rolls, the game generates a secret number (the server seed), hashes it, and shows the player the hash up front. The player then adds their own input — a client seed they fully control. The two seeds get combined to produce the outcome. Afterward, the original secret is revealed, and the player can hash it themselves to confirm it matches the promise made before the roll.

Because a good hash can’t be reversed, the game couldn’t have known the player’s seed in advance, and the player can prove the server didn’t swap its secret after seeing their choice. The classic academic treatment of this idea appears in Stanford’s lecture notes on commitment schemes, which lay out the “binding” and “hiding” properties that make the whole dance trustworthy. Translated into JavaScript, the entire mechanism — hashing, seed-mixing, verification — can fit comfortably inside a fraction of a 13KB budget, especially using the browser’s native crypto.subtle digest functions.

How Web3 Casinos Wrestle With the Same Puzzle

The fascinating part is that crypto casinos are solving an identical problem at a much larger scale. A no-KYC dice site handling thousands of wagers a minute can’t simply ask players to take its word for anything. It publishes server seed hashes, lets players set client seeds, and exposes a verification page — the exact pattern a js13k dice game would use, just dressed up with blockchain settlement.

Decentralized games push this even further by trying to remove the trusted server entirely. Pulling that off on a public chain is genuinely hard, because every validator can see the same data, and miners or sequencers could theoretically influence outcomes. Researchers tackling randomness in on-chain games explore how multiple participants can jointly generate a result no single party can rig. The on-chain rankings mentioned earlier reward exactly the projects that get this right, which is why transparency sits alongside raw volume as a scoring metric.

What a Tiny Game Can Teach a Whole Industry

A 13KB dice or slot game is the perfect sandbox for these ideas precisely because the constraint strips everything down to essentials. There’s no room for marketing fluff or hidden complexity — just a hash, two seeds, and a number anyone can recompute. Build one for a jam and the abstract phrase “provably fair” suddenly becomes concrete code a player can audit in their own browser console.

That’s the quiet thread connecting a weekend coding challenge to a sprawling Web3 economy. Both start with the same humble question a developer asks the first time a digit pops out of a function: can you actually trust this number? Answer it honestly in 13 kilobytes, and the rest is just scale.

🔙 Back to Articles list.