# How to Pick a Random Number Fairly (and Why Math.random Is Wrong)

> A practical guide to random numbers: the difference between Math.random and crypto.getRandomValues, why fairness matters for giveaways and raffles, how to roll dice and shuffle a list in your browser, and the one case where randomness is genuinely hard.

URL: https://uttir.com/blog/how-to-pick-a-random-number-fairly
Published: 2026-08-29
Updated: 2026-08-17
Author: Uttir
Reading time: 6 min
Tags: random, cryptography, raffle, dice, shuffle, security

## Quick answer

For a giveaway, a raffle, a coin flip, or a quick "pick a number from 1 to 100", the right tool is the browser's built-in cryptographically secure RNG (crypto.getRandomValues). It is unbiased, unpredictable, and fast. For most uses, you do not need the cryptographic part — but using it costs nothing extra, and it removes any doubt about fairness. A browser-based random number generator rolls the dice in your browser, and the result is reproducible only by the person running the tool. For cryptographic keys, nonces, and password salts, the same API is what every TLS connection uses.

Random numbers are one of those things that look trivial — "pick a number from 1 to 100" — but get interesting fast. The wrong choice of randomness can make a giveaway predictable, a raffle riggable, a password crackable, or a statistical test fail. This post covers the difference between weak and strong randomness, when each is appropriate, how to roll dice and shuffle lists fairly in your browser, and the one case where getting randomness right is genuinely hard.

## Math.random vs. crypto.getRandomValues

Most programming languages ship a "random" function that is fast, convenient, and not actually random in any cryptographic sense. In JavaScript, that function is `Math.random`. It is a pseudo-random number generator (PRNG): a deterministic algorithm that produces a sequence of numbers that *look* random but are entirely predictable if you know the seed. Most implementations use a 64-bit internal state, which means an attacker who has seen enough of the sequence can reconstruct the seed and predict every future value.

For most uses — games, animations, picking a background color, shuffling a playlist — `Math.random` is fine. The values look random, they pass basic statistical tests, and the consequences of an attacker guessing them are nil.

For other uses — generating a password, picking a lottery winner that needs to be auditable, generating a cryptographic key, rolling a nonce that must be unique — `Math.random` is the wrong choice. The right choice is the browser's `crypto.getRandomValues`, which is a cryptographically secure random number generator (CSPRNG). It draws from the operating system's entropy pool (which is fed by unpredictable physical processes: mouse movements, keyboard timings, disk activity, hardware RNG on modern CPUs), and the output passes the statistical tests that distinguish CSPRNGs from PRNGs.

The [Random Number Generator](/random-number-generator) on this site uses `crypto.getRandomValues` for all draws. The cost is essentially zero (the same API is used for every TLS connection), and the result is unpredictable and unbiased.

## Unbiased means uniform, not "no pattern"

A common source of bugs is the modulo bias. If you want a random number from 0 to 99 and you call `crypto.getRandomValues(new Uint8Array(1))[0] % 100`, the result is biased toward the lower numbers — because 256 is not evenly divisible by 100, the values 0-55 have a slightly higher chance of appearing than 56-99.

The fix is the rejection method: ask for random bytes, and reject any value that would cause the bias. For 0-99, ask for a random byte. If the byte is 200 or higher (200-255 = 56 values), reject it and try again. The accepted values are 0-199, which is 200 values evenly divisible by 100. Each of the 100 possible results gets exactly 2 of the 200 values. No bias.

The Random Number Generator implements the rejection method for every range. The math is invisible to the user, but the result is correctly uniform.

## Common cases that need a fair draw

### Giveaway winner

"Pick a random number from 1 to N where N is the number of entries." The tool that does this well uses a CSPRNG and the rejection method, and shows the user the seed or hash that produced the result so the draw is auditable. The browser-based tool is auditable in a different way: the source code is visible, the random bytes come from the browser's own CSPRNG, and the result can be verified by anyone who runs the same tool with the same input. For most giveaways, this level of auditability is more than enough.

### Raffle and lottery

Same as a giveaway, but the stakes are usually higher. The right approach is the same: CSPRNG, rejection method, public source code. The legal requirements for lotteries vary by jurisdiction, but the technical approach is the same.

### Coin flip

"Heads or tails" is a random draw from a set of 2 outcomes. A single random byte, modulo 2, would have a slight bias (since 256 is not even). The fix is the same: rejection method, or just take the high bit of a random byte (which is exactly 50/50 because 128 is even). The Random Number Generator includes a "coin flip" preset that does this.

### Dice roll

"Roll a six-sided die" is a random draw from 1 to 6. The same approach: rejection method on a random byte, accepting values 0-251 (252 is divisible by 6), then mapping to 1-6. The Random Number Generator includes a "dice" preset for one die and for multiple dice (3d6, 4d6, etc.).

### Shuffling a list

The Fisher-Yates shuffle is the standard. Walk the list from the end to the beginning; at each position, swap the current item with a randomly chosen item from the positions 0 to current. The result is an unbiased permutation of the original list, assuming the random choices are unbiased. The Random Number Generator's "shuffle" feature implements Fisher-Yates with the rejection method for the random choices.

### Password or token

A 16-character random password is 16 draws from a 94-character pool (printable ASCII). A 256-bit API token is 32 draws from a 256-value pool (random bytes). For both, the CSPRNG is the right choice. The [Password Generator](/password-generator) uses `crypto.getRandomValues` internally for the same reason.

## When strong randomness matters

The use cases where `crypto.getRandomValues` is required, and `Math.random` is a real vulnerability:

	
- **Passwords and passphrases.** A predictable password is not a password. The Password Generator uses CSPRNG; any generator that uses `Math.random` is a security risk.
	
- **API keys and session tokens.** A predictable token is a session-takeover. The CSPRNG is the only acceptable source.
	
- **Cryptographic nonces.** A reusable nonce breaks the encryption. CSPRNG.
	
- **Password reset tokens.** Same as API keys. CSPRNG.
	
- **UUIDs.** UUIDs are 128-bit values where uniqueness is the security property. Using `Math.random` would give unique-looking values that are predictable, which is bad for security tokens but fine for non-security identifiers. The [UUID Generator](/uuid-generator) uses CSPRNG.

## When Math.random is fine

The use cases where predictability is not a problem and `Math.random` is acceptable:

	
- **Visual effects.** Animation timings, particle effects, randomly chosen background colors, the "surprise me" feature in a tool that picks a default option.
	
- **Non-security sampling.** "Pick a random quote to show" or "pick a random testimonial to feature". The quotes are already public; the only property needed is "looks random".
	
- **Game logic (single-player).** "Roll a die in a single-player game against the computer." The computer is not trying to cheat, so the source of randomness does not matter.
	
- **Statistical simulations.** Monte Carlo methods, simulations, sampling from a distribution. For most research-grade work, even `Math.random` is fine; for cryptographic or financial simulations, the CSPRNG is required.

## The one case where randomness is genuinely hard

Cryptographic key generation, specifically, requires more than just a CSPRNG. The browser's `crypto.getRandomValues` is suitable for session tokens, nonces, and password salts, but the generated key is only as good as the entropy that went into the CSPRNG. For a high-stakes key (a long-term encryption key, a CA signing key), the right answer is a hardware security module (HSM) or a dedicated hardware RNG (TPM, YubiKey) that produces entropy from a trusted source.

For personal use — encrypting your own files, your own notes, your own messages — the browser's CSPRNG is fine. The threat model is "someone with access to my computer", not "a nation-state attacker with physical access to my CPU's entropy source". For the personal threat model, the browser is the right tool.

## How to use the random number generator

	
- Open the [Random Number Generator](/random-number-generator).
	
- Set the range (min and max, inclusive).
	
- Choose how many numbers to draw (one for a single pick, many for a raffle).
	
- Or pick a preset: coin flip, dice roll, shuffle a list.
	
- The result appears instantly, with the option to copy or re-roll.

For the shuffle preset, paste a list (one item per line) and the tool returns the same items in a random order. The result uses Fisher-Yates with the rejection method, so every permutation is equally likely.

## Common mistakes to avoid

	
- **Using `Math.random` for a security-sensitive draw.** The draw may look fair but is predictable to an attacker who has seen enough of the sequence.
	
- **Using a single random byte for a multi-value choice without rejection.** The result is biased toward the lower values. The fix is the rejection method.
	
- **Picking a "random" number from your head.** Human randomness is heavily biased (people pick 7, 17, 23, 42 more often than other numbers in 1-100). The right answer is a CSPRNG, not a gut feeling.
	
- **Re-using the same seed for multiple draws.** If two draws use the same seed, the results are correlated. CSPRNGs are designed to avoid this; PRNGs with a fixed seed are not.
	
- **Not considering reproducibility.** For some uses (debugging a simulation, replaying a game), reproducibility matters. The CSPRNG is not reproducible; if you need a reproducible "random" sequence, you want a seeded PRNG. For the personal-use cases (giveaway, raffle, password), reproducibility is a feature, not a bug — you want each draw to be unique.

The Random Number Generator handles all of these correctly. The CSPRNG is the source, the rejection method is the unbiased-mapping function, the Fisher-Yates is the shuffle, and the results are not reproducible across calls. For most uses, this is exactly the right behavior.

## Key takeaways

- Math.random vs. crypto.getRandomValues
- Unbiased means uniform, not "no pattern"
- Common cases that need a fair draw
- When strong randomness matters
- When Math.random is fine

## Frequently asked questions

### Is this free to use?

Yes. The tools and guides on Uttir are free to use, with no signup, no paywall, and no feature gating. There is no email gate, no trial period, and no premium tier. The site is supported by unobtrusive on-page ads that never interfere with the tool itself.

### Do I need to sign up or create an account?

No. Uttir does not have accounts, login, or email signup. Open the tool or the post and use it.

### Does this upload my data to a server?

Uttir processes your data entirely in your browser using JavaScript. Your text, files, and inputs are never uploaded to a server. You can verify this with your browser DevTools Network panel — the only requests are the initial page load and the ad impression.

### What tool should I use after reading this?

The most relevant tool on Uttir for this is the Random Number Generator at [/random-number-generator](/random-number-generator). Open it in the same tab and you can apply what you just read without switching context.

## Related tools

- [Random Number Generator](https://uttir.com/random-number-generator) — Pick a random number, roll dice, or shuffle a list. Cryptographically secure, runs locally.
- [Random Picker / Wheel of Names](https://uttir.com/random-picker) — Pick a random item from a list with a spinning wheel. Great for giveaways, classroom activities, and team decisions. Runs in your browser, no upload.
- [Random Name Generator](https://uttir.com/random-name) — Generate random first and last names for mockups, test data, or placeholder content. Filter by gender (any, male, female, neutral) and region (English, Spanish, French, German, Japanese, Chinese, Arabic). Free, in your browser, no upload.
- [Password Generator](https://uttir.com/password-generator) — Create strong, random passwords with custom length and character sets — right in your browser.
- [UUID Generator](https://uttir.com/uuid-generator) — Generate cryptographically random UUID v4 identifiers, one or a thousand at a time.
- [UUID v7 Generator](https://uttir.com/uuid-v7-generator) — Generate RFC 9562 UUID v7 identifiers. Time-sortable, globally unique, generated locally. Batch of 1 to 1000.
- [Statistics Calculator](https://uttir.com/statistics-calculator) — Compute mean, median, mode, range, variance, standard deviation, and quartiles from a list of numbers.

---

For the full HTML article, visit https://uttir.com/blog/how-to-pick-a-random-number-fairly.
This file is the markdown rendering at https://uttir.com/blog/how-to-pick-a-random-number-fairly.md. See https://uttir.com/llms.txt for a site-wide summary.
