How to Generate a Truly Random Number (and Which Kind You Actually Need)
Updated: 2026-09-29
You want a number nobody could have predicted. The usual answers to this search get two things wrong: that a browser can measure physical randomness, and that Math.random() is fine until you need security. Neither is true. This guide separates the three mechanisms that all get called “random”, works modulo bias out with arithmetic you can check on paper, names the right function in Python, C++, JavaScript and MATLAB, and says what the linked generator does — and where it stops.
The short answer
Choose the generator by what it costs you if somebody guesses your number. If guessing costs anything — an account, a key, a fair draw — use your platform's cryptographically secure generator: crypto.getRandomValues in a browser, Python's secrets module, or the OS entropy source they both sit on. If guessing costs nothing — a dice roll in a game, a Monte Carlo run — an ordinary seeded pseudorandom generator is faster and reproducible, which is what you want there.
Two rules catch most real mistakes. Never use Math.random() or C's rand() to make a secret. And never narrow a wider random value to your range with the remainder operator and call it finished, because that introduces modulo bias — the reason the linked tool redraws values instead of trusting a single remainder.
- Secret, token, key, or a draw someone could contest: the platform CSPRNG, always.
- Simulation, sampling, or anything you want to run twice with the same result: a seeded generator.
- Physical true randomness: hardware or an entropy service, not reachable from a web page.
- The linked tool is the browser case: uniform integers over an inclusive range, from the OS CSPRNG, with rejection sampling, up to 500 per run.
Three different things all called random
One word covers three mechanisms with three different guarantees. Read them as a ladder of unpredictability.
True randomness comes from physics
A true random number generator samples a physical process whose outcome is not settled by anything you could measure: thermal noise in a resistor, the timing of radioactive decay, atmospheric noise caught by a radio receiver, oscillator jitter. Hardware exists for this, chip makers expose it as an instruction or a peripheral, and public services stream sampled atmospheric noise to anyone who asks. The result is unpredictable in principle — no amount of computing recovers it. Raw sources are not uniform, though: their electronics leave slightly more of one symbol than the other, so hardware generators condition the samples through a randomness extractor before anyone calls the output random.
Cryptographically secure pseudorandomness
A CSPRNG is a deterministic algorithm holding a secret internal state, seeded once from real entropy and then advanced by computation. The operating system keeps that seed from sources it can observe — interrupt timing, keyboard and mouse arrival intervals, hardware noise where a driver exposes it — in a kernel-guarded pool. Each call to crypto.getRandomValues draws from that pool, or from a generator the OS seeds from it.
The guarantee is practical rather than philosophical: an attacker who watches a long stretch of output still cannot work out the state, so they cannot predict the next value or reconstruct earlier ones. That is the property keys and tokens need, and it is why this tier, not the physics tier, is what everyday software reaches for.
Ordinary pseudorandomness
A plain generator is the same construction with the security requirement dropped: a deterministic recurrence over a fixed state, chosen for speed and statistical quality. Python's random module implements the Mersenne Twister, which distributes beautifully and is entirely predictable once an observer collects enough consecutive outputs to solve for its state.
These are the right tools for simulation: fast, testable against statistical batteries, and settable — same seed, same stream. That is a feature in a Monte Carlo experiment and a disaster in a session token, and it is the distinction people most often get backwards.
What a browser can honestly give you
Plainly first: a web page cannot harvest physical entropy. JavaScript cannot sample a noise source, has no instruction for reading hardware randomness, and cannot measure electron timing. Anything a browser hands you came from an algorithm whose state was seeded by the operating system.
What a browser can give you is the OS CSPRNG, wrapped as crypto.getRandomValues, which fills a typed array with bytes or 32-bit words from the kernel's pool. Web Crypto exposes exactly one primitive and it is the cryptographic one, so there is no weaker call to stumble into, and the linked tool's page describes it as the same primitive used for passwords and encryption keys.
For every purpose a reader of this page has, the CSPRNG-backed answer is the right answer. A raffle draw, a session identifier, a password minted on your laptop: none need radioactive decay, and all are served by a generator an attacker cannot predict. Pages advertising “true randomness” from a tab are being sloppy or dishonest; the accurate claim is “unpredictable to anyone who has not already compromised your operating system”, and that is enough.
Why Math.random() is not for secrets
The objection is mechanical. Math.random() is a deterministic function of internal state: the engine holds a block of numbers, applies a fixed recurrence to advance them, returns a formatted result, and consults nothing external in between. The sequence is implied by the state, so the only question is whether an observer can find it.
For a long time the answer was comfortably yes. The generators browsers used were small-state statistical designs whose output leaks structure: a handful of consecutive values is enough to set up equations whose solution is the state. With the state you run the recurrence forwards to predict every later draw and backwards to recover every earlier one — the documented failure mode that made browsers abandon these designs.
There is a second layer to it. You never see the state, so you cannot check that the state was ever unpredictable, and a generator lazily seeded from a coarse clock has a search space an attacker enumerates.
crypto.getRandomValues is built the other way round: observing output does not help you reconstruct state, so guessing your number costs work proportional to the range rather than to a few samples.
Modulo bias, with numbers you can check
The part most articles get wrong or skip is arithmetic you can check by hand. Say your source emits a uniform byte — one of 256 values, 0 through 255, each with probability 1 in 256 — and you want a uniform digit from 0 to 9. The tempting one-liner is the remainder after dividing by 10.
Count how many byte values land on each digit. 256 divided by 10 is 25 with a remainder of 6; exactly, 256 = 25 × 10 + 6. Each digit gets 25 values and 6 are left over at the top of the range, and those fall on the lowest digits: outcomes 0 through 5 get 26 supporting values each, outcomes 6 through 9 get 25. The total checks out, since 6 × 26 is 156, 4 × 25 is 100, and 156 + 100 = 256.
A favoured digit has probability 26/256, which is 0.1015625; an unlucky digit 25/256, which is 0.09765625; a fair draw would be 0.1 each. The favoured digits are 26/25 times as likely as the others, and 26/25 is exactly 1.04 — four percent more likely.
When the bias stops being academic
Four percent is mild. Bias gets serious when your range is a large fraction of the source, because then there are few complete passes and the leftover block is proportionally huge. The linked generator draws 32-bit words, so its source has 2^32 values, which is 4,294,967,296.
Friendly case: a six-sided die, min 1 and max 6, range size 6. Divide: 4,294,967,296 = 6 × 715,827,882 + 4. A bare remainder favours four faces — faces 1 through 4 come from 715,827,883 inputs each, faces 5 and 6 from 715,827,882. About one part in a billion, which is why no board game has been rigged this way.
Adversarial case: a lottery index from 0 to 2,147,483,646, a range of 2^31 − 1 values. Twice that range is 4,294,967,294, which is 2 short of 4,294,967,296, so a remainder gives the first two tickets 3 chances each and every other ticket 2 — a 50% advantage, since 3/2 is 1.5 exactly.
Rejection sampling: the fix
Discard the awkward tail instead of folding it. In the byte case, 250 = 25 × 10 divides evenly, so keep only inputs 0 through 249 and redraw whenever you draw 250 or higher. Every accepted input then maps to a digit through exactly 25 values, and 25/250 is exactly 0.1. The cost is a 6/256 chance of a redraw per attempt, which is negligible and terminates with certainty.
The linked generator does the same at 32 bits. It computes limit = Math.floor(4294967296 / range) * range — the largest multiple of your range that fits in the source — draws a word, redraws while the value is at or above that limit, then returns min + (x % range). For the die, limit is 6 × 715,827,882, which is 4,294,967,292, so exactly the four highest inputs are rejected, and the redraw chance is 4 out of 4,294,967,296, or 1 in 1,073,741,824. Conditioned on acceptance, each face has 715,827,882 supporting inputs and 6 × 715,827,882 = 4,294,967,292, so each face is exactly one sixth. The tool's own copy states it without the algebra: a drawn value outside the largest multiple of your range that fits in 32 bits is discarded and redrawn, and this removes modulo bias.
Uniform, seeded, reproducible: three separate ideas
“Random number” also hides a shape question: the distribution, the seed, and whether a run can be repeated.
Uniformity is a property of the distribution
A uniform draw means every value in your range had the same chance — the die, the raffle, the random index. A non-uniform draw shapes the odds deliberately: normal measurement noise, exponential gaps between arrivals, weighted sampling where item A is three times as likely as item B. Simulation spends most of its effort on the non-uniform case, and the standard technique is to start from a uniform stream and transform it.
The linked tool is uniform-only over an integer range: a bell curve, a weighted pick or a decimal is a different job.
What a seed actually is
The seed is the initial state of a deterministic generator. Same generator, same seed, same sequence of outputs, forever. Setting a seed adds no randomness; it fixes the starting point of a computation so the run can be repeated.
That is why seeded generators are beloved in simulation: a colleague reruns your experiment and gets your numbers, a paper publishes its seed for review. Predictability is the point, and an unseeded CSPRNG gets in the way because you cannot show the sequence behind your conclusion.
Why reproducibility is the wrong goal for secrets
Turn that property around and it is the vulnerability: a reproducible secret generator is a guessable one. Anyone who learns or guesses the seed regenerates every value you emitted, in order, and a secret anyone can regenerate is not a secret.
So tools that mint keys and tokens refuse to expose seeds. The linked generator has no seed input and no way to replay a run; each draw is fresh from the OS pool, and the state lives in the kernel rather than the page.
In Python, C++, JavaScript, and MATLAB
Every language variant of this search has the same answer under the syntax: use the platform's cryptographically secure generator, not the toy one. In two of these languages the standard library does not fully provide one.
Python splits the job deliberately. random is the pseudorandom module — Mersenne Twister based, fine for games and simulation, documented as unsuitable for security. For secrets use the secrets module: secrets.randbelow(n) returns a uniform value in 0 through n−1 from the OS entropy source, and secrets.token_urlsafe covers tokens. For a reproducible simulation, seed with random.seed(1234) and stay in random.
C++ has no CSPRNG in its standard library, which is the honest reply to the autocomplete. std::random_device is the intended handle on non-deterministic entropy and the right thing to seed an engine with, but its behaviour is implementation-defined: on some toolchains it passes through to the OS source, on others it falls back to a seeded pseudorandom engine, it can block while the kernel pool fills early in boot, and the uniformity of individual results is not guaranteed. std::mt19937 is explicitly a statistical generator, and legacy C rand() is neither uniform under a remainder nor unpredictable. When it matters, call the platform's own entropy API or a vetted crypto library.
JavaScript in a browser is one call: crypto.getRandomValues into a typed array, which is what the linked tool uses, and the same global exists in Node and Deno. For an unbiased integer, pair it with rejection sampling rather than multiplying a float.
MATLAB deserves a line because its searchers ask for “a random real number” and “real random numbers”. Its general-purpose functions — rand, randi, randn, with rng controlling the seed — are pseudorandom by design: deterministic, seedable, statistically excellent, meant for reproducible numerical work. A real entropy source is a different thing, and getting one means reaching an operating-system facility or an external service.
The rule covering all four: the platform CSPRNG when prediction hurts you, the seeded statistical generator when prediction helps you. There is no in-between.
What you need randomness for, and what breaks when it is weak
Keys, passwords, and anything derived from them. Weakness here is arithmetic: an attacker enumerates the plausible space, and each independent uniform draw from m values adds log2(m) bits — a byte adds 8 bits because 2^8 = 256, while ten values add a little over 3, since log2(10) is about 3.32. Four digits is under 14 bits, which is ten thousand guesses to enumerate; thirty-two bytes is 256 bits, which nobody enumerates. A generator with reconstructible state, or a seed taken from a clock, collapses that figure to the size of the seed.
Tokens: session identifiers, reset links, API keys. Two ways to lose. Predictability lets an attacker forge the next token instead of finding yours. Collision is subtler: tokens come from a finite pool, so issuing many short ones eventually gives two users the same string, and one person's authenticated session becomes somebody else's. Size the token for the volume you issue, and draw it from a CSPRNG.
Draws and lotteries — raffles, class selection, audit sampling. The requirement is defensibility rather than secrecy: afterwards someone asks whether the process could have favoured a participant, and you need an answer. Modulo bias is the classic way a well-intentioned draw turns unfair, and predictability is the way it is fixed in advance. Use the cryptographic generator with rejection sampling, keep the entry list and the output, and do not let the winner have written the script.
Sampling and simulation — Monte Carlo integration, bootstrapping, load-test traffic. Here you want statistical quality and reproducibility, and a CSPRNG is wrong in both directions: slower, and unable to replay a run. Use a seeded statistical generator and record the seed. For purely cosmetic randomness, nothing breaks.
Common failure modes
Four patterns account for most broken randomness in real code, and all four look fine in a test run.
Rolling your own shuffle. The tempting version — for each position, swap with an element chosen from anywhere in the array — is biased, making some orderings likelier than others. The correct Fisher–Yates walk swaps each element with a uniformly drawn position at or after it, and if the order protects anything, those draws need the CSPRNG. The sloppy version cannot be tuned into correctness: choosing an index for each of n positions produces n^n equally likely paths, which divide evenly among the n! orderings only if n! divides n^n, and for a 52-card deck it does not — 52^52 has only 2 and 13 as prime factors, while 52! includes 3.
rand() % n, or any remainder over a non-dividing source. The low outcomes collect the leftovers, the skew is invisible in casual use, and it is decisive when the range is a large fraction of the source. Rejection sampling is the fix, not a wider source — 10 does not divide 65,536 either.
Seeding from the clock. A timestamp seed leaves a search space you can enumerate: an attacker who knows your token appeared during a given minute tries the roughly 60 seeds in that minute and replays each generator forwards. Clock seeding also collides — two processes started together get the same seed and emit identical streams.
Reusing a seed, or one stream for two purposes. Same seed, same sequence, so two components seeded alike produce values that match, and recovering one recovers the other. Draw independently, per purpose, from the CSPRNG, and if your design needs two values to differ, check that they differ instead of hoping independent draws miss each other.
The linked generator: what it does and where it stops
The tool behind this button is the browser case, built the way the sections above argue. You set an inclusive minimum and maximum and an integer count; it draws every value with crypto.getRandomValues into a Uint32Array and applies rejection sampling — limit = Math.floor(4294967296 / range) * range, redraw while the word is at or above that limit — then returns min + (x % range). No value's likelihood depends on where it sits in your range.
The controls are deliberately small. Endpoints are inclusive, so 1 to 6 is a die, and max equal to min is allowed. Negatives are fine, and the endpoint inputs are unbounded text. Results print one per line or comma-separated, and a Copy button puts them on the clipboard. An optional no-duplicates mode keeps each value in a set and redraws until it fills, and errors when you ask for more values than the range contains. What it will not do is the honest part.
- Integers only — no decimals or floats; a uniform real in an interval is a different tool.
- Uniform only — no weighted, normal or exponential draws.
- Up to 500 numbers per run; the tool's own advice for needing more is to run it a few times and combine the outputs.
- No seed control and no reproducible runs, by design — a run you can replay is a run someone else can replay.
- No exclusion lists; you cannot draw 1 to 100 while skipping multiples of 5.
- Ranges spanning more than 2^32 values are not supported, since the rejection bound assumes a 32-bit source.
- Nothing stored, exported or downloaded: close the tab and the numbers are gone.
- The generation logic itself makes no request — crypto, DOM and clipboard APIs only — but the site loads third-party scripts, so the page is not network-free and does not claim to be.
Frequently asked questions
Can you actually generate a truly random number on a website?
Not in the physics sense, and no page honestly can: a browser tab cannot sample thermal noise, radioactive decay or atmospheric noise. What it can do is call the operating system's cryptographically secure generator through crypto.getRandomValues, which returns bytes from a pool the kernel seeded from real entropy. That is unpredictable against any attacker who cannot read your kernel's state, and for keys, tokens, draws and sampling it is the right answer.
Why is Math.random() not good enough for a password or a token?
Because it is deterministic: it advances an internal state by a fixed recurrence and consults nothing external, so the whole stream is implied by that state, and generators of this class have been reconstructed from a handful of observed outputs. Browser implementations have hardened since, but the specification still does not require unpredictability, so you would be resting a secret on a promise nobody made. Use crypto.getRandomValues.
What is modulo bias, briefly?
Reducing a wider random value to your range with the remainder operator favours the low outcomes whenever the source size is not a multiple of your range, because the leftover inputs land on them. Reducing a uniform byte to 0 through 9 gives six digits 26 supporting inputs each and four digits 25 each, so those six are 26/25 as likely as the rest — 4% more likely. Rejection sampling fixes it: discard inputs at or above the largest multiple of your range that fits the source, and draw again.
Is crypto.getRandomValues a hardware random number generator?
No. A hardware generator transduces a physical process into bits and needs conditioning to become uniform; crypto.getRandomValues returns bytes from the operating system's entropy pool, which the kernel mixes from sources the machine exposes — hardware noise where a driver provides it, timing events among others — and serves through a cryptographic construction. Both are unpredictable to an outside attacker, and the OS interface is the one your software can reach.
How do I get a random number in Python?
Two modules, chosen by threat. For anything secret, import secrets and use secrets.randbelow(n) for a uniform value below n, or secrets.token_urlsafe for a token; it reads the OS entropy source. For a game or a simulation, use the random module, a Mersenne Twister: statistically excellent, seedable with random.seed so the run repeats exactly, and explicitly not for security.
How do I get a truly random number in C++?
C++ has no CSPRNG in its standard library. std::random_device is the standard handle on non-deterministic entropy, but its behaviour is implementation-defined: it may block, it may be a pass-through to the OS source or a fallback pseudorandom engine depending on the toolchain, and individual results are not guaranteed uniform. Seed std::mt19937 from it for statistical work; for security material use the platform's entropy API or a vetted crypto library.
What about a real random number in MATLAB?
The general-purpose functions there — rand, randi, randn, with rng for seeding — are pseudorandom by design: deterministic and reproducible, which is what numerical work wants. They are not entropy sources. Real entropy means getting it from an operating-system facility or an external service and bringing it in.
Can the linked generator give me decimals, or numbers to skip?
No. Integers only, uniformly, across an inclusive range, with no exclusion lists and no weighting. A random real number in an interval, a weighted pick or a normal distribution is a different tool.
Can I seed the linked generator to get the same numbers again?
No, and deliberately. There is no seed input, so a run cannot be replayed: every value comes fresh from the OS CSPRNG. Reproducibility is what you want in a simulation and must not have in a secret, because anyone who can regenerate a sequence can replay it. Use a seeded statistical generator when you need repeatable sequences.
Why does it stop at 500 numbers, and are my numbers stored?
500 is the batch limit for one run; the tool's own suggestion for needing more is to run it several times and combine the outputs. Nothing is stored — no history, no export, no file download, no server copy — the page says you close the tab and the numbers are gone. The generation logic performs no request of its own, though the site loads third-party scripts, so treat the offline property as belonging to the generation rather than the whole page.