The problem: Math.random is predictable
JavaScript's Math.random() returns a number between 0 and 1. To the eye, the output looks random — no obvious pattern, no repeats in the short run. But "looks random" is not the same as "is random". Math.random is a pseudo-random number generator (PRNG), typically xorshift128+ or a similar algorithm, seeded from a predictable source and running entirely in memory. Its state can be observed and reconstructed. If an attacker can run JavaScript in the same browser tab (through an XSS vulnerability, a malicious extension, or a third-party script), they can call Math.random repeatedly, infer the internal state, and predict every future output — including the outputs that already happened. Your "random" password, generated moments ago, is now known.
For a UI test placeholder, this does not matter. Nobody is attacking your test data. For a password, an API key, a session token, or a CSRF nonce, it matters a lot. The standard solution is crypto.getRandomValues, which pulls entropy from the operating system's random source (on Linux, /dev/urandom; on Windows, CryptGenRandom) and is unpredictable even to an attacker running in the same process. The Random String Generator uses crypto.getRandomValues exclusively and advertises this in its UI.
Fastest path
Open the Random String Generator, pick a preset (API Key, Bearer Token, Password, PIN, Hex Color, UUID-like, Memorable, Phone Pattern) or configure manually. Toggle the character sets (uppercase, lowercase, digits, symbols), set the length, set the count (1 to 100 for batch). Toggle "Exclude ambiguous" to drop 0/O/l/1/I. Use the pattern mode for formatted strings like XXXX-000000. The tool shows the resolved charset, the entropy estimate, and a strength meter. Copy individual strings or all at once. Export as TXT or CSV.
CSPRNG vs PRNG: what the difference means
A PRNG (pseudo-random number generator) is a deterministic algorithm. Given the same seed, it produces the same sequence. Math.random is a PRNG — fast, but predictable if you can observe enough output. The seed in a browser comes from timestamps and other low-entropy sources, and the internal state is a 128-bit value that can be recovered from about 8 consecutive outputs.
A CSPRNG (cryptographically secure pseudo-random number generator) is a PRNG with two additional properties: it is computationally infeasible to predict future outputs from past outputs, and it is computationally infeasible to reconstruct the internal state from observed output. crypto.getRandomValues in browsers and crypto.randomBytes in Node.js are CSPRNGs. They are seeded from the operating system's entropy pool, which collects noise from hardware events (disk timings, keystroke intervals, network packet arrivals). The output is unpredictable even to an attacker with full access to the same machine.
The performance difference is small. crypto.getRandomValues of a 1,000-element array takes microseconds. For any security-relevant use — passwords, tokens, nonces, keys — the cost is negligible and the security gain is total. There is no reason to use Math.random for security.
Entropy: how to measure randomness
Entropy is measured in bits. An N-bit entropy source has 2^N equally likely outcomes. Brute-forcing it takes 2^(N-1) attempts on average.
The entropy of a random string is log2(charset_size) × length. For an alphanumeric string (62 characters: a-z, A-Z, 0-9):
| Length | Entropy | Brute-force time at 10^9 guesses/sec |
|---|---|---|
| 8 | 47 bits | 3 days |
| 12 | 71 bits | 75 years |
| 16 | 95 bits | 2 million years |
| 20 | 119 bits | 2 trillion years |
| 24 | 143 bits | beyond cosmic timescale |
The 128-bit threshold is the standard for cryptographic security. Below 128 bits, a sufficiently resourced attacker (nation-state, botnet) can brute-force the space in a practical timeframe. Above 128 bits, brute force is infeasible regardless of resources. A 22-character alphanumeric string (130 bits) clears the threshold; a 16-character one (95 bits) does not.
The tool's strength meter uses these thresholds: Very Weak (<60 bits), Weak (60-80), Fair (80-100), Strong (100-128), Very Strong (>128). For a password, aim for Strong or above. For an API key, aim for Very Strong — the cost of a longer key is zero, and the security gain is large.
Charset vs length: where the bits come from
Adding length adds bits faster than adding characters. Doubling the length adds 1 bit per character × the new length. Adding characters adds log2(new_charset_size) - log2(old_charset_size) per character.
For an 8-character string:
| Charset | Size | Entropy (8 chars) |
|---|---|---|
| Digits only | 10 | 26 bits |
| Lowercase | 26 | 38 bits |
| Alphanumeric | 62 | 48 bits |
| + 20 symbols | 82 | 51 bits |
| + all printable ASCII | 94 | 53 bits |
Going from digits-only (26 bits) to alphanumeric (48 bits) adds 22 bits — a 4-million-fold increase in brute-force difficulty. Going from alphanumeric to full printable ASCII adds only 5 bits. The biggest jump is from a small charset to a medium one. Beyond alphanumeric, symbols add little.
Length dominates. An 8-character string from the full printable ASCII set has 53 bits. A 12-character alphanumeric string has 71 bits — 18 bits more, a 260,000-fold increase. If you want more security, add length first, then characters.
Ambiguous characters: the human-readability tax
The characters 0 (zero) and O (letter O), l (lowercase L) and 1 (one), I (uppercase I) and l (lowercase L) look similar in many fonts. A random string containing O0l1I is hard to type accurately and hard to distinguish when reading. For tokens that humans transcribe (one-time codes, backup passwords, license keys), excluding these characters improves usability.
The tool's "Exclude ambiguous" toggle swaps each charset to a safe variant:
- Uppercase:
ABCDEFGHJKLMNPQRSTUVWXYZ(removes I, O) - Lowercase:
abcdefghjkmnpqrstuvwxyz(removes i, l, o) - Digits:
23456789(removes 0, 1) - Symbols:
!@#$%^&*-_=+(removes parentheses, brackets, quotes)
The entropy cost is small. Removing 6 characters from a 62-character alphabet leaves 56 characters; log2(56) = 5.81 bits per character vs log2(62) = 5.95. On a 16-character string, the difference is 2 bits — negligible compared to the usability gain. For a 32-character API key, the cost is 4 bits out of 186 — still negligible.
Pronounceable strings: trading entropy for memory
The tool's pronounceable mode alternates vowels and consonants to produce strings that can be spoken and remembered: tevanipolu, mirefodape. These are easier to memorize than 7K2mQ9xR but contain less entropy, because the vowel-consonant alternation constrains the character at each position.
A pronounceable 10-character string has roughly 5 vowels and 5 consonants. The entropy is 5 × log2(5) + 5 × log2(21) = 5 × 2.32 + 5 × 4.39 = 11.6 + 22.0 = 33.6 bits. A 10-character random alphanumeric string has 59 bits. The pronounceable string has about 56 percent of the entropy of the random string at the same length.
The trade-off is deliberate. For a password that a human must memorize (a master password, a hotel wifi code), pronounceable strings are worth the entropy cost. For an API key that is stored in a config file and never read by a human, use the full random mode — memorability does not matter, entropy does.
Pattern mode: formatted tokens
The tool's pattern mode generates strings that match a template. The default pattern is XXXXXX-000000, which produces strings like ABCDEF-123456. The placeholders are:
| Placeholder | Meaning |
|---|---|
X |
Uppercase letter |
x |
Lowercase letter |
0 |
Digit |
A |
Any character from the active charset |
a |
Lowercase or digit |
| Any other char | Literal separator (printed as-is) |
Pattern mode is useful for serial numbers, license keys, and formatted tokens. The entropy depends on which placeholders you use and the length of each. XXXXXX-000000 has 6 × 5.7 + 6 × 3.3 = 34 + 20 = 54 bits. Adding more X positions adds entropy; adding separators does not.
The risk of pattern mode is that the pattern itself is known to an attacker. If you publish that your license keys are XXXX-XXXX-XXXX, the attacker knows the structure and can brute-force only the variable parts. This is why product keys often include a checksum digit — it does not add entropy, but it rejects most random guesses before they are tested against the validation server.
No-repeat mode: permutations, not independent picks
Standard mode picks each character independently — the same character can appear twice. No-repeat mode draws without replacement: each character appears at most once. This is a permutation, not a sequence of independent picks.
The entropy of a permutation is log2(n!), where n is the number of characters drawn. For 8 characters drawn from a 62-character alphabet without replacement: log2(62 × 61 × 60 × 59 × 58 × 57 × 56 × 55) = log2(1.36 × 10^14) = 47 bits. With replacement: log2(62^8) = 48 bits. The no-repeat mode has slightly less entropy because the constraint reduces the space.
No-repeat mode is useful when the same character appearing twice would be confusing — a serial number where AA is ambiguous, or a code where repeats look like typos. For most security uses, independent picks are fine and have slightly more entropy.
The modulo bias
The tool's getRandomInt(max) helper does arr[0] % max on a Uint32Array value. This has a known issue: if max does not evenly divide 2^32, the first 2^32 mod max values are slightly more likely than the rest. For a 62-character alphabet, 2^32 mod 62 = 6, so the first 6 characters are about 1.0000000005 times as likely as the others. The bias is one part in a billion — negligible for any practical purpose.
The correct fix is rejection sampling: generate a value, reject it if it is in the biased range, try again. The tool does not implement this because the bias is too small to matter for the string lengths the tool generates. If you are implementing a CSPRNG wrapper for a cryptographic library, use rejection sampling. For a password generator, the modulo approach is fine.
Gotchas
- Math.random is not safe for security. Any tool that uses
Math.randomfor password or token generation is broken. The tool advertises its use ofcrypto.getRandomValues, but if you are building your own generator or evaluating another tool, check the source. The difference between secure and insecure is one function call and 30 years of cryptographic research. - Length beats charset. Adding characters to the charset adds a few bits; adding length adds bits proportional to the new length. A 16-character alphanumeric string (95 bits) is more secure than an 8-character string with all symbols (53 bits). If you need more security, add length first.
- Pronounceable strings have less entropy. The vowel-consonant alternation constrains each position, reducing entropy by about 40 percent compared to random mode at the same length. Use pronounceable mode for human memorization; use random mode for machine storage.
- No-repeat mode has slightly less entropy than independent picks. Drawing without replacement reduces the space. For 8 characters from 62, the difference is 1 bit. Independent picks are the default for a reason.
- Pattern mode exposes the structure. If you publish the pattern (XXXX-XXXX-XXXX), the attacker knows the format and brute-forces only the variable parts. Add a checksum or use a non-obvious pattern for product keys. Do not rely on pattern secrecy alone — security through obscurity is not security.
Summary
- Use
crypto.getRandomValues(CSPRNG) for any security-relevant random string.Math.randomis a PRNG with predictable state and is unsafe for passwords, tokens, keys, or nonces. The performance cost of CSPRNG is negligible. - Entropy is
log2(charset_size) × length. The 128-bit threshold is the standard for cryptographic security. A 22-character alphanumeric string clears it; a 16-character one does not. Add length first, then characters — doubling length adds far more entropy than adding symbols. - Ambiguous characters (0/O, l/1, I/l) hurt human readability. Excluding them costs 2-4 bits on a 16-character string — negligible for the usability gain. Pronounceable strings trade entropy (about 40 percent) for memorability; use them for human-typed passwords, not for machine-stored tokens.
- Pattern mode generates formatted tokens from templates. The entropy is in the variable positions, not the separators. No-repeat mode draws without replacement (permutations), which has slightly less entropy than independent picks. The modulo bias in many CSPRNG wrappers is one part in a billion and negligible in practice.
- Use the Random String Generator for CSPRNG-based token generation, the Password Generator for password-optimized output, the UUID Generator for RFC 4122 UUIDs, and the Password Strength Checker to verify a password's entropy.