How the race stays fair. Every lane sorts the identical shuffled array — same heat number, same size, same shape — and on every tick each unfinished lane replays exactly the same number of real operations. The finishing order is therefore just the order of total operations. The clock above the lanes counts operations, never seconds, because timing JavaScript would measure your browser rather than the algorithm.
What counts as an operation. One comparison between two elements, or one write into the array. A swap is two writes. Merge sort copies values into a separate buffer before merging; those copies are not counted, but every value written back into the array is, even when it lands where it already was. Insertion sort holds its key aside, so comparing against the key is a comparison, and putting it back costs a write only if it actually moved. Shuffling is never counted.
Why a "slow" sort can win. Big-O describes how the work grows with size, not what happens on every array. This bubble sort stops after a pass with no swaps, so already-sorted input costs it n − 1 comparisons and no writes (63 operations at 64 bars, tying insertion sort for first; try Sorted). Insertion sort does almost nothing when every bar is already near its final spot. Here "nearly sorted" means the bars start in order and then max(1, n/10) pairs of neighbours are swapped, so at most n/10 pairs are out of order and no bar is more than n/10 places from home. On that data insertion sort finished ahead of all five others in every heat we tried (seeds 1 to 200, 16 to 256 bars), at just over n comparisons. On random bars the n² algorithms never came out ahead. Try Nearly sorted, then Reversed.
Why this quicksort slows down. It uses the last element as the pivot (the Lomuto scheme). On sorted or reversed data every partition peels off a single element, so it makes exactly n(n − 1)/2 comparisons: 2,016 for 64 bars, as many as selection sort. Few unique hurts it too, though less sharply: with only five distinct values, long runs of equal bars split lopsidedly, and at 128 bars Quick makes 2.5 to 3.4 times Merge's comparisons (seeds 1 to 200). Real libraries choose pivots more carefully, or switch algorithm when partitions go lopsided.
Big-O at a glance, and what the race does not measure.
| Algorithm | Work |
| Bubble | O(n²); best case O(n) with the early exit |
| Insertion | O(n²); best case O(n) |
| Selection | O(n²) always — exactly n(n − 1)/2 comparisons |
| Merge | O(n log n) always |
| Quick | O(n log n) on average, O(n²) worst case |
| Heap | O(n log n) |
The race does not measure wall-clock time, cache effects or memory use. Production sorts are hybrids: V8's Array.prototype.sort uses TimSort, and C++ std::sort implementations typically use introsort.