
A version 4 UUID looks unique because it almost certainly is, and the UUID generator can tell you exactly how almost. Out of the 128 bits in a UUID, 6 are fixed by the format itself, leaving 122 bits of actual randomness, which is the number every collision question comes back to. This guide works through what those 122 bits mean, does the birthday-paradox math with real numbers, and shows what the generator's code does to make sure the randomness is real rather than decorative.
What the 32 hex digits actually contain
A UUID is written as 32 hexadecimal digits split into five groups, like f725714a-aa09-4f39-9ce1-a14b9ee4a730. Two of those digits are not random at all. The first digit of the third group is always 4, marking this as version 4 under RFC 9562, the current specification for UUIDs (it obsoletes the older RFC 4122, which first defined version 4). The first digit of the fourth group is constrained to 8, 9, a or b, which marks the variant field: those four values are the only ones whose top two bits read 10. Every other digit, all 30 of them, is free. That is 4 bits of version, 2 bits of variant, and 122 bits of randomness, out of 128 total.
A worked example
Take the example UUID above, f725714a-aa09-4f39-9ce1-a14b9ee4a730. Reading it group by group: f725714a and aa09 are fully random, 4f39 starts with the fixed version digit followed by three random ones, 9ce1 starts with a variant digit (9, one of the four legal values) followed by three random ones, and the final group, a14b9ee4a730, is fully random again. Count the free hex digits: 8 + 4 + 3 + 3 + 12 = 30, and 30 hex digits at 4 bits each is 120, plus the 2 free bits hiding in the version and variant groups, gives the full 122.
Where the randomness comes from
The generator calls crypto.randomUUID, the browser's built-in UUID function, which draws from the operating system's cryptographic entropy source rather than a predictable pseudo-random formula. On the rare older browser without that function, the tool's code falls back to filling 16 bytes from crypto.getRandomValues directly and setting the version and variant bits by hand: b[6] = (b[6] & 0x0f) | 0x40 forces the top nibble of byte 6 to 4, and b[8] = (b[8] & 0x3f) | 0x80 forces the top two bits of byte 8 to 10. Both paths produce byte-identical results, and neither one touches Math.random or a server. The UUIDs never leave the browser tab that generated them.
That fallback is worth pausing on, because it explains why the fixed bits are safe to rely on even when the fast path is unavailable. crypto.getRandomValues fills the 16-byte buffer with 128 fully random bits first, and only then does the code overwrite 6 of them. Nothing about the version and variant bits reduces the pool of possible UUIDs in a way that helps an attacker guess one; those bits are fixed positions known to everyone in advance, so they never carried any secrecy to begin with. The randomness that matters, all 122 bits of it, is untouched.
The collision math
The relevant question is never "could two random UUIDs collide," which is trivially yes, but "how many would you need to generate before a collision becomes plausible." That is the birthday paradox: with a pool of N equally likely values, generating n of them gives roughly a n2 / (2N) chance of at least one repeat, as long as n stays well below the square root of N. For a v4 UUID, N is 2122, so the formula becomes approximately n2 / 2123. Plugging in three scales of n:
| UUIDs generated (n) | n² | Collision probability |
|---|---|---|
| 109 (one billion) | 1018 | about 9.4 × 10-20 |
| 1012 (one trillion) | 1024 | about 9.4 × 10-14 |
| 1015 (one quadrillion) | 1030 | about 9.4 × 10-8 |
Even at a quadrillion UUIDs, the odds of a single collision anywhere in that entire batch sit at roughly one in ten million. Solving the same formula for a 50% chance of one collision gives n around 2.7 × 1018, about 2.7 quintillion UUIDs. No real system generates a meaningful fraction of that number in a lifetime, which is why production databases use v4 UUIDs as primary keys without a uniqueness check.
It helps to put a quadrillion draws in perspective, since it is the largest number in the table and the one most likely to sound achievable. A system would need to generate roughly 31.7 million UUIDs every second, nonstop, for a full year, to reach 1015 of them. The tool itself caps a single click at 1,000 UUIDs, so reaching even the smallest row in the table, one billion, would take a million clicks. The math is not defending against a real workload; it is showing that the randomness has enough room in it that a workload large enough to matter does not exist yet.
Where v4 is the wrong choice
This tool only makes version 4, and that is the right version for most identifiers, but not all of them. If two systems need to derive the same identifier from the same input, deterministically, version 4's randomness is a liability rather than a feature; a name-based version like v5, which hashes a namespace and a name instead of drawing randomly, is the correct tool there. If insert order matters for database performance, fully random v4 keys scatter across a B-tree index and hurt write locality, which is the reason the newer, sortable version 7 exists: it embeds a timestamp in the leading bits so IDs generated later sort later, at the cost of leaking rough creation time in the identifier itself. Neither of those is available from this generator, and reaching for v4 in the wrong situation is a design mistake the collision math cannot fix.
What to do
- Use v4 UUIDs freely as primary keys, API identifiers, or session tokens; the collision odds are not a real constraint at any scale one browser tab can generate.
- Store them lowercase and hyphenated, the canonical text form, unless a specific system requires otherwise.
- Reach for v5 instead when two independent systems need to compute the identical identifier from the same input without coordinating.
- Consider v7 for a high-write database table where insert order and index locality matter more than hiding creation time.