UUID Generator — v4, v7 and v1 Built From Your Browser's Own Random Bytes, With the Fields Read Back Out
Make v4, v7 or v1 UUIDs in your browser, from crypto.getRandomValues, and have every value taken apart again in front of you: the version nibble, the variant bits, the 48-bit or 60-bit instant carried inside a v7 or v1, the count of bits that are actually random. Write them out dashed, undashed, in braces or as urn:uuid:, in either alphabet. Put a list side by side with the ones your own browser's crypto.randomUUID() produces and compare the two. Paste UUIDs back in to be read, including the ones your library wrote in the other byte order. Nothing you generate, paste or save is sent anywhere.
| # | What the bytes say | Time carried | Random bits |
|---|
Side by side with your own browser's generator
| Checked | The generator | crypto.randomUUID() |
|---|
Read UUIDs you already have
| Input | Reading |
|---|
Sixteen bytes, four spellings
Sixteen bytes, thirty-two digits, and the six bits that are not yours
A UUID is sixteen bytes. Everything else - the dashes, the capitals, the urn: prefix - is a way of writing those sixteen bytes as text. Sixteen bytes are thirty-two hex digits, and the canonical spelling cuts them 8-4-4-4-12, which is why the hyphenated form is 36 characters: four of them are punctuation and none of them carry information.
Two of the digits belong to the specification rather than to you. The thirteenth digit is the version - a 4 for a random one, a 7 for a time-ordered one, a 1 for the oldest scheme still in use. The seventeenth holds the variant, and the RFC 4122 variant fixes its top two bits to 10, which leaves that digit able to read only 8, 9, a or b. A v4 therefore has 128 minus 4 minus 2 = 122 bits that are actually chosen at random, and that number is the whole basis for every uniqueness claim made anywhere here. The table under the output box recalculates it for whichever version you asked for, because for v7 and v1 it is smaller: a clock field is not entropy.
| Version | What occupies the 128 bits | Bits left to chance |
|---|---|---|
| v4 | 122 random, 6 fixed | 122 |
| v7 | 48-bit Unix millisecond, 4 version, 2 variant, then 12 + 62 random | 62 as they are written here, since the 12 are an ordering counter; 74 in a v7 that leaves them to chance |
| v1 | 60-bit 1582-referenced count, 4 version, 2 variant, 14 clock sequence, 48 node | 61, with one of the 48 spent announcing that the node is not a real address |
Where the bytes come from, and the promise that comes with them
The bytes come from window.crypto.getRandomValues, which the browser refills from the operating system’s entropy pool; Math.random() is not used and there is no fallback to the clock. The reason is not purity: Math.random() is a pseudo-generator whose entire state sits in the engine running it, so a value built from it can be reproduced by anyone who can observe two consecutive outputs of the same generator, and on some engines the seed is 32 bits wide - a fifth of the entropy a v4 is supposed to have, in a number a script can brute force in a laptop-minute.
What getRandomValues buys you is unpredictability. What it does not buy you is uniqueness, and the difference is where most UUID folklore goes wrong. Nobody checks. There is no registry, no central authority, no handshake: two services on opposite sides of the world can invent the same v4 this afternoon and neither will ever be told. The scheme works because 122 random bits are so wide that colliding is a statistical event nobody has ever observed, not because anything is coordinating it. The honest sentence is therefore: a random UUID is a value so unlikely to repeat that you may treat it as unique, provided every machine producing them had a working entropy source. That proviso is not decoration. When a platform’s random source restarts from the same state after every boot - which has happened in shipped products, in key stores and in embedded firmware - every “unique” identifier from those devices comes in identical pairs, and the UUID format is the innocent party. If your IDs are made on a fleet of cloned VMs or on hardware you did not choose, the thing to audit is the entropy source, not the string.
v7: a timestamp you can sort, and a secret you did not decide to tell
A v4 tells you nothing about when it was made. That is the design, and it is also the reason a table with a v4 primary key gets slow: the storage engine cannot append, it must insert at a position that looks random, so every write dirties a page nobody touched yesterday and the index stops fitting in memory. A v7 fixes that by putting a 48-bit Unix millisecond at the front of the value. Because those twelve hex digits are the first thing in the string, ordinary lexicographic sorting on the text - ORDER BY id, a sort in a shell, a string comparison in a language with no UUID type - gives you chronological order for free. The forty-eight bits run out around the year 10889, which is comfortably beyond any use a value made here can be put to.
Then there is the second consequence, and pages rarely mention it: a v7 is a timestamp, and a timestamp is a disclosure. Hand a v7 to anyone and you have told them the millisecond you created it, and if you create one per new account, you have also told them how many accounts you created and when your traffic spikes. The row under the output box decodes the instant for every value it made, in UTC, so you can see exactly what you are about to publish. If that bothers your use case, make a v4. If the ordering is what you need and the exposure is not acceptable, the answer is not a UUID with the clock hidden - those two wants are contradictory, and it is better to notice now.
Within one millisecond, chance is not enough to keep values in order, so the twelve bits under the version nibble are spent on a counter instead: the first value made in a millisecond gets 0, the next 1, and so on. That is why the “random bits” column for v7 reads 62 and not 74 - those twelve bits were not random, and what they bought is a provable ordering. A twelve-bit counter holds 4,096 values in one millisecond, which is far past what a press is allowed to ask for (500 per press), and if a session somehow does press Generate nine times inside the same millisecond the count wraps back to zero and the note under the box says so, because after a wrap two values are no longer guaranteed to be in order. A claim that cannot be kept is worse than no claim.
v1: the same clock cut into three pieces, and a node id nobody gives you
v1 is older than the RFC that describes it. Its timestamp is a 60-bit count of 100-nanosecond intervals since 15 October 1582, and it is scattered: the low 32 bits come first, the next 16 sit in the middle, and the top 12 hide under the version nibble. Sort a column of v1s as text and you get a meaningless order, because the beginning of the string is the end of the timestamp. That single oddity is why MySQL has always written its UUID() output with the fields rearranged, and why RFC 9562 later standardised that rearrangement as v6 - the same 60-bit count, nibbles rotated so the string sorts. Both are read back: paste a v1 and a v6 made from the same instant and the reading says they agree.
v1 was designed to be unique by construction rather than by probability: the timestamp plus the machine’s 48-bit network address, with a 14-bit clock sequence to cover the case where the clock goes backwards. A page in a browser cannot have that. There is no API that exposes a network address, and nothing here dresses something else up as one. What it does instead is draw a random node id and set the multicast bit in its first byte, which is the convention that announces “this address is not a real one, don’t route frames to it”. So the v1s here are structurally legal and weaker than a real v1 in exactly one way: their uniqueness rests on 47 random node bits plus 14 random clock-sequence bits - 61 bits, not a factory-assigned address - and every value draws a fresh node, so they do not identify a machine at all. Ask for v4 unless you specifically need a version-1 layout for a system that expects one.
Four spellings, one value, and the byte order libraries disagree about
The same sixteen bytes can be handed over as 0e63f398-3c1b-4f2a-9a7d-7f2c1d0b4a55, as 0E63F3983C1B4F2A9A7D7F2C1D0B4A55, as {0e63f398-...} or as urn:uuid:0e63f398-.... All four denote the identical value, and every one of them round-trips through the reading box. Capitals are a display choice: hex digits A-F and a-f mean the same four bits, though the URN spelling is specified in lower case, so a capitalised urn:uuid: value is a hint that whatever produced it was not reading the specification either - the box points that out when it sees one.
The disagreement that actually bites is not the text, it is the bytes. The canonical string puts the fields in big-endian order; the structure that GUIDs were built on, in COM and in everything downstream of it, holds the first three fields in the other order. So the same value can arrive as a 16-byte array whose first four bytes are reversed, and a library that assumes the wrong one will hand you a valid-looking, entirely different UUID. The check is mechanical: reverse bytes 0-3, then 4-5, then 6-7, and if the two arrays turn into each other, that was the problem. Both spellings are printed from the same bytes, so a comparison against whatever your language gave you is a comparison and not an act of trust.
For storage, the string form is the expensive one: 36 characters, 36 bytes in ASCII, more once a collation gets involved, and an index over it is that wide. A 16-byte column holds the same value at half the size, which is why a Postgres uuid and a MySQL BINARY(16) beat CHAR(36) on any table large enough to notice - though note that MySQL’s own UUID() function produces v1, not v4, which surprises people every few years.
What is refused, and the arithmetic done instead
Nothing you generate, paste or save is sent anywhere: the values are made in the tab and stay there. The whole of what it will not do, in one place:
- It cannot tell you whether a UUID is already in use. Uniqueness is probabilistic, not registered; no service holds the set of all UUIDs, and any page claiming to check one is inventing an answer.
- It cannot generate a v3 or a v5. Those are digests over a namespace constant and a name, and both of those are yours: they come from your system and are supposed to match what your code already computed. Guessing a plausible namespace would produce a value that is wrong in a way you cannot see, so v3 and v5 are read back instead, with the refusal stated in as many words.
- It cannot read your network card, so it will not make a v1 with your real address in it. See the multicast bit above.
- It will not pretend a v4 has a creation time. The inspector says “no time carried” rather than inventing one from the value’s own digits.
What it does instead is arithmetic you can check. The fourth number under the box is the birthday bound on the bits that are genuinely random: for n values with b random bits the chance that two of them collide is about n(n-1)/2 divided by 2^b. At 500 v4 values that is roughly one in ten to the thirty-second power, which is not a reassurance so much as a measurement - it tells you the scheme is fine, and it also tells you the number you should be worrying about is the entropy source, not the arithmetic. The session counter next to it is empirical: every value handed out since the tab was opened is kept and repeats are counted among them, and Clear does not wipe that count, because how many were made is a fact about the session rather than about the text in the box.
Questions
Q: Do the UUIDs I generate get sent anywhere? No. The bytes come from your browser’s own generator, the strings are assembled in this tab, and saving hands the file to your browser’s download machinery rather than to anybody else.
Q: Are they really unique?
Unpredictable, yes, from crypto.getRandomValues. Unique in the sense of “guaranteed never to have existed before” is not something any generator can promise - it is a probability, and what is printed beside the box is that probability rather than a promise. It goes bad only when a machine’s entropy source goes bad, which is a hardware and platform problem, not a format one.
Q: Which version should I use? v4 unless something specific pulls you elsewhere: a database primary key that should sort by insertion, or a key you generate on several machines at once - v7. A legacy system that expects a version-1 layout - v1, and read the node caveat first. v3 and v5 are for deriving a stable identifier from a name you already have, and no name will be guessed here.
Q: crypto.randomUUID() is undefined in my console. Is the generator broken?
It is the native method that is missing, and that is normal on a page which is not a secure context: browsers withhold randomUUID() over plain http, while getRandomValues keeps working. That is why the comparison table can still fill in its left column and leave the right one empty, and why a v4 still comes out on a URL where the built-in method cannot.
Q: Why does the note say a v7 is made of 62 random bits when I expected 74? Because the twelve bits under the version nibble were used as a counter to keep this batch in order. Ordered and maximally-random are different goals; the note tells you which one it bought.
Q: How many can I make at once, and can I paste a whole column in to be read? Five hundred per press, and up to 2,000 pasted lines per read. Both limits are about legibility, not about the format: the 501st value is no more unique than the first.