Skip to content
Let Me ChooseFor You

How the draws work

Randomness is the easy part. Proving you did not re-roll is the hard part, and it is the part everyone else skips.

The problem

Picture a giveaway announced on a livestream. The host types the entrants into a wheel, spins it, and reads out a name. The audience sees an animation and a result. What they cannot see is how many times the wheel was spun before the camera started, or whether the name that came up was the one that got announced.

Every randomiser on the internet has this problem, and almost none of them address it. Making the numbers more random does not help, because randomness was never what was in doubt.

The method: commit, then reveal

The fix is old and well understood. Before the draw, you publish something that locks in your randomness without revealing it. Afterwards, you reveal it. Anyone can then check the two are consistent.

  1. A seed is generated. When the page loads, your browser produces 256 bits of randomness from its cryptographic random number generator - the same source that secures encrypted connections.
  2. The seed is sealed. We immediately show you SHA-256 of that seed. A hash is a one-way function: it is easy to compute from the seed and effectively impossible to reverse. The commitment is on screen before you have even finished typing your list.
  3. The draw happens. The winning index comes from HMAC-SHA-256, keyed with the seed, over your exact list of entries. Change one character of one entry and the winner changes.
  4. The seed is revealed. Along with a receipt containing the list, the seed and the result.

Why that is hard to cheat

To change the winner after the draw, you would need a different seed - but the new seed would have a different hash, and the old hash is already on screen and in your receipt. Finding a second seed that hashes to the same value is a preimage attack on SHA-256, which nobody knows how to do.

To rig it beforehand, you would need to pick a seed that produces the winner you want - but the seed is committed before the entry list is finalised, and the result depends on both. Re-running the draw to get a better answer produces a fresh commitment, so a verifier comparing against the hash you published first will see it does not match.

A detail worth mentioning: modulo bias

The obvious way to turn a random number into a list index is to take it modulo the number of entries. It is also subtly wrong. A 32-bit random value has 4,294,967,296 possible results, and that does not divide evenly by, say, 100 - so the first 96 entries come out very slightly more often than the last 4.

The bias is small enough to ignore in a game and not small enough to ignore in a draw with real prizes. We use rejection sampling instead: values that fall in the uneven tail are discarded and a fresh one is drawn. The result is exactly uniform.

What this does not prove

This is the part other tools would leave out, so it is worth being blunt about it.

  • It does not prove the list was complete. If an organiser leaves someone off, the maths still verifies - against the wrong list.
  • It does not prove the commitment was published early. The proof is only as good as the moment the hash became public. For a serious draw, post the hash where it is timestamped - a public comment, a message in a channel - before entries close.
  • It does not make the entrants real. Fake entries are a moderation problem, not a cryptography one.

What it does prove is narrow and genuinely useful: the result you announced is the result the draw produced, and you did not run it twice.

Where the work happens

All of it runs in your browser using the standard Web Crypto API. Your list is never sent to a server, no account is involved, and no draw history is stored anywhere. The receipt is just your data encoded into a URL - which is why it works without us, and would keep working if this site disappeared.

Check a receipt