Note
BubbleRand: a deliberately lazy sampler
A goofy idea for when you want “kinda random,” not cryptographic or even fair.
The premise is simple: take a distribution (an array of numbers), randomly bubble sort small sections a handful of times, then grab the middle value. BubbleRand is intentionally slow, intentionally biased, and perfect for gamey chaos.
Think of it as “analog randomness.” You can tune how many passes you do, how wide the subranges are, and how many swaps you allow per pass.
BubbleRand in motion
The box highlights a random slice that gets bubble-sorted in a random direction. After a few passes, we grab the middle value as the output.
- #10.12
- #20.83
- #30.47
- #40.65
- #50.29
- #60.91
- #70.54
- #80.06
- #90.78
- #100.33
- #110.71
- #120.18
- #130.59
- #140.25
- #150.88
Quick randomness check
I ran a small comparison (20000 samples, 30 bins) between Math.random, crypto randomness, and BubbleRand. The charts below show the histogram distribution and a few simple stats.
Math.random
crypto.getRandomValues
BubbleRand (Math.random)
Notes: BubbleRand randomizes sort direction per pass. Chi-square is relative to a uniform distribution (lower is closer to uniform). Generated 2026-01-29.
How to read the numbers
Mean
For a uniform random generator over [0, 1), the mean should hover near 0.5. Small drift is normal with finite samples.
Std Dev
A perfectly uniform source has a standard deviation of about 0.2887. Values near 0.28–0.30 are healthy for this sample size.
Chi-square
Lower is closer to uniform. With 30 bins, a “good” value often lands in the 15–60 range for small runs (1k–5k samples). Larger runs should tighten toward ~30. Spikes higher than that can indicate bias—or just bad luck in a small run.
These are simple smoke tests, not cryptographic validation. For serious randomness, use dedicated suites (Dieharder, TestU01) and much larger sample sizes.
Algorithm sketch
- Start with a numeric distribution array.
- Pick a random slice window (start + length).
- Bubble-sort only that slice, but cap the swaps.
- Repeat 10 times (or any number you like).
- Return the middle value of the entire array.
Pseudo-code
function bubbleRand(values, passes = 10) {
const arr = values.slice()
for (let i = 0; i < passes; i++) {
const start = randInt(0, arr.length - 2)
const len = randInt(2, Math.min(12, arr.length - start))
const end = start + len
bubbleSlice(arr, start, end, maxSwaps = len * 2)
}
return arr[Math.floor(arr.length / 2)]
}Why bother?
It’s not about correctness. It’s about texture. Think of loot tables, ambient behaviors, or procedural variation that should feel “alive” but not purely random.