Spinness uses crypto.getRandomValues() for every tool on the site — there is no
Math.random() anywhere in the codebase, including tools where fairness is not the main concern.
This is the same cryptographic random source browsers use to generate security keys and TLS sessions.
The section below explains how it works and why we use it everywhere.
One source of randomness, used everywhere
Spinness uses a single method for every tool on the site — including visual
and entertainment features like color generation and word shuffling:
crypto.getRandomValues(). No tool falls back to Math.random().
crypto.getRandomValues — cryptographically secure
crypto.getRandomValues() is provided by the browser's Web Crypto API.
It draws entropy from the operating system's secure random source —
/dev/urandom on Linux, CryptGenRandom on Windows.
The results are statistically unpredictable and suitable for security-sensitive uses.
Where we use it: every tool on Spinness — Wheel of Names spins, coin flips, team generator draws, color generation, word shuffling, and every other tool that returns a random result.
Math.random — not used by Spinness
Math.random() is JavaScript's built-in pseudorandom number generator.
It produces a deterministic sequence from an internal seed state. The output is
statistically well-distributed and indistinguishable from random for most purposes —
but it is not cryptographically secure.
Why we don't use it: even for entertainment tools where security is not the primary concern, drawing from one audited source everywhere is simpler to reason about and verify than maintaining two separate code paths.
Uniform distribution
Raw values from both sources are mapped to a target range using rejection sampling rather than the naive modulo approach. Modulo introduces a slight bias toward lower values when the range does not divide evenly into the output space. Rejection sampling discards out-of-range outputs and draws again until a value falls cleanly within the target, which eliminates the bias entirely.
Source code
All randomness logic lives in src/lib/random/. The functions are:
randomInt(max), randomIntRange(min, max),
pickOne(array), pickMany(array, n), and
shuffle(array). Each uses crypto.getRandomValues as
the entropy source. There are no Math.random() calls anywhere in the Spinness codebase.
Standards and references
The cryptographic random number generation used by Spinness follows the principles defined in NIST Special Publication 800-90A Rev.1 (Recommendation for Random Number Generation Using Deterministic Random Bit Generators). The browser's crypto.getRandomValues() API is documented on MDN Web Docs: Crypto.getRandomValues().
For a deeper understanding of why cryptographically secure randomness matters for fairness-sensitive applications, see the NIST standards above. The key property — computational indistinguishability from true randomness — is what separates crypto.getRandomValues() from Math.random() in security-relevant contexts.
How to verify statistical randomness: NIST SP 800-22
NIST Special Publication 800-22 Rev.1a defines 15 statistical tests for evaluating whether a sequence of bits appears random — tests for frequency, block frequency, runs, longest-run-of-ones, serial, and others. These tests are used to validate that a PRNG does not produce detectable patterns.
The OS-level entropy sources that back crypto.getRandomValues() are designed to pass all 15 NIST SP 800-22 tests. Math.random(), by contrast, is not required to pass these tests and browser implementations vary. This is the practical difference between the two sources: one is validated against a published cryptographic standard, one is not.
Frequently asked questions
Is crypto.getRandomValues truly unpredictable?
"Unpredictable" is the right word — "truly random" is philosophically harder to pin down. crypto.getRandomValues draws entropy from the operating system, which mixes hardware events, disk activity, network timing jitter, and other noise into its seed state. A determined adversary with full knowledge of the algorithm still cannot predict the next output because the seed is hidden and constantly changing. That is what "cryptographically secure" means in practice, and it is the same source applications use to generate cryptographic keys.
Can I use Spinness results for official draws and giveaways?
Yes. Tools that use crypto.getRandomValues — the Wheel of Names, Team Generator, Coin Flip, and others — produce results that are statistically indistinguishable from true random. For formal public giveaways, we recommend screenshotting the result or recording your screen so you have an audit trail if anyone asks how the winner was picked. The randomness itself is not the weak link; documentation is.
Does Spinness ever use Math.random() instead of crypto.getRandomValues()?
No. Every tool on Spinness — including visual and entertainment features like color generation and word shuffling — draws from crypto.getRandomValues(). There is no Math.random() call anywhere in the codebase; using the cryptographic source everywhere keeps the implementation simple and consistent, even where fairness is not the primary concern.
What is rejection sampling and why does it matter for fairness?
Here is the problem it solves: if you take a large random number and map it to a small range using modulo, the lower values in the target range appear slightly more often because the large range rarely divides evenly. The bias is small — a fraction of a percent — but for a draw where fairness matters, that is enough to make the result technically unfair. Rejection sampling discards any output that falls in the "overflow" zone and draws again, keeping every outcome equally likely regardless of range size.