Text Randomizer
Shuffles lines, words, characters or paragraphs with Fisher-Yates, because sort with a random comparator is measurably skewed and browser-dependent.
Alice Nakamura
Ben Osei
Carla Duarte
Daniyal Khan
Eva Lindqvist
Farid Rahman
Grace Mbeki
Hanna Virtanen
Not shuffled yet the order is chosen in your browser, so this is the input
Lines 8
Method
algorithm Fisher-Yates, which gives every order equal probability
not sort( () => Math.random() - 0.5 ), which is measurably skewed
source crypto.getRandomValues, by rejection sampling
`array.sort( () => Math.random() - 0.5 )` is the shuffle everyone knows
and it does not work. A comparator has to be consistent: asking it the
same question twice must give the same answer, and a sort is entitled to
rely on that. A random comparator breaks it, so the output depends on
the sort algorithm and is skewed in a way that differs between browsers.
Fisher-Yates walks from the end, swapping each item with a uniformly
chosen one at or below its position. That gives all n! orders equal
probability. Choosing the swap index from the whole array each time
instead is the classic naive version, and it favours some orders over
others.
About one item in n stays in its original place in a fair shuffle, so a
shuffle where nothing moved is not necessarily broken and a shuffle
where everything moved is not necessarily better. The count above is
there so you can see the number rather than judge by eye.
Characters means grapheme clusters here, not code units, so shuffling
text with emoji or combining accents in it moves whole characters.
Shuffling by UTF-16 unit takes an emoji apart into surrogate halves and
produces replacement squares.
Shuffling is not anonymising. The same words in a different order carry
most of the same information, and a shuffled list of customer names is
still a list of customer names.
For a giveaway or anything with a prize, the randomness matters more
than the algorithm. This uses `crypto.getRandomValues`, so the sequence
is not predictable from previous output the way `Math.random` is.
The result cannot be reproduced, on purpose. There is no seed, because a
reproducible shuffle is a different tool with different uses, and a draw
that can be replayed is a draw that can be gamed.
Output is valid and updates as you type.
Fix the highlighted fields to update the output.
array.sort( () => Math.random() - 0.5 ) is the best-known way to shuffle a list in JavaScript, and
it is not a shuffle.
A comparator has to be consistent: asked the same question twice it must give the same answer, and a sort is entitled to rely on that. A random comparator breaks the contract, so what comes out depends on the sorting algorithm. In V8 the first elements stay near the front far more often than chance; another engine is skewed differently. The result looks shuffled and is not.
Fisher-Yates is three lines and correct: walk from the end, swapping each item with a uniformly chosen one at or below its position. That gives every possible order the same probability, and it is what this uses.
How to use
- Paste the list.
- Choose what to shuffle: lines, words, characters or paragraphs.
- Tick the box to hold a header row in place.
Example
Eight names, shuffled by line:
Grace Mbeki
Alice Nakamura
Daniyal Khan
Eva Lindqvist
Ben Osei
Farid Rahman
Hanna Virtanen
Carla Duarte
Lines 8
In a different place 7 of 8
expected about 7, since one item in n stays put by chance
Method
algorithm Fisher-Yates, which gives every order equal probability
not sort( () => Math.random() - 0.5 ), which is measurably skewed
source crypto.getRandomValues, by rejection sampling
Seven of eight moved, and one staying put is expected rather than a sign of a problem.
Pitfalls
The one-line sort shuffle is broken and looks fine. That is what makes it durable: the output is plausible, so nobody checks. It only shows up when somebody measures the distribution over thousands of runs, which is what the test suite here does.
The naive Fisher-Yates variant is also biased. Choosing the swap index from the whole array each time instead of from the unshuffled part produces n^n equally likely paths over n! orders, which cannot divide evenly, so some orders come up more often. The direction of the walk matters.
One item in n stays put in a fair shuffle. A shuffle where nothing moved is not necessarily broken, and one where everything moved is not necessarily better. The count is printed so you can look at the number rather than judge by eye.
Characters means grapheme clusters here. Shuffling by UTF-16 code unit takes an emoji apart into surrogate halves and produces replacement squares, and moves a combining accent onto the wrong letter. Whole characters move as one piece.
Shuffling is not anonymising. The same words in a different order carry most of the same information, and a shuffled list of customer names is still a list of customer names.
A shuffle by word destroys sentence structure and keeps the vocabulary. For scrambling text that still has to be read as a sentence, this is the wrong tool. For an ordering draw it is the right one.
There is no seed, on purpose. A reproducible shuffle is a different tool with different uses, and a draw that can be replayed is a draw that can be gamed.
Compatibility
Everything runs in the browser: nothing is uploaded and nothing is stored, which is the point for a list of names.
The randomness is crypto.getRandomValues, and the swap index is drawn by rejection sampling so that
every position is equally likely. Using a modulo there would bias the shuffle in a subtler way than the
sort trick does.
Characters are split with Intl.Segmenter grapheme granularity, with a fallback that keeps combining
marks and zero-width-joiner sequences together where the segmenter is missing.
The distribution is checked in the test suite over 40,000 shuffles of a four-item list, asserting that each item lands in each position within a percentage point and a half of a quarter. The sort-comparator version fails that test comfortably, which is why the assertion is there.
Frequently asked questions
What is the correct way to shuffle an array in JavaScript?
i from the last index down to 1, draw j as a uniform integer in 0..i, and swap
a[ i ] with a[ j ]. Three lines. Walking downwards and choosing j from 0..i rather than from the
whole array is what makes it uniform.Is Math.random good enough for a shuffle?
crypto.getRandomValues, which is what this does.