DEV Community

Cover image for Math.random() vs crypto.getRandomValues(): Which One Should a Yes/No Picker Use?
Yes or No Random Generator
Yes or No Random Generator

Posted on

Math.random() vs crypto.getRandomValues(): Which One Should a Yes/No Picker Use?

I recently built a small yes/no picker, and one question came up early: which random function should I use?

Both Math.random() and crypto.getRandomValues() work in the browser. Here is how they differ, and when it matters.

The quick version

// Option 1: Math.random()
const pick1 = Math.random() < 0.5 ? "Yes" : "No";

// Option 2: crypto.getRandomValues()
function pick2() {
  const a = new Uint32Array(1);
  crypto.getRandomValues(a);
  return a[0] % 2 === 0 ? "Yes" : "No";
}
Enter fullscreen mode Exit fullscreen mode

Both give you roughly a 50/50 split. For a toy project, either is fine.

What is different?

Math.random()

  • Fast and simple.
  • Uses a pseudo-random generator. Its output is good enough for games and animations, but it is not designed to be unpredictable.
  • Fine for a casual "heads or tails".

crypto.getRandomValues()

  • Uses the browser's cryptographically secure random source.
  • Harder to predict, and the quality is more consistent across browsers.
  • Slightly more code, but still tiny.

For a yes/no picker, Math.random() would not break anything. I chose crypto.getRandomValues() anyway, because it costs almost nothing and removes any doubt about fairness.

Is % 2 biased?

A common worry is modulo bias. It appears when the range of random values does not divide evenly by the number of options.

A Uint32 has 2^32 possible values. That number is even, so % 2 splits it into two equal halves. For two options, there is no modulo bias.

If you had three options, % 3 would be slightly uneven, and you would need a rejection loop to stay perfectly fair:

function randInt(n) {
  const a = new Uint32Array(1);
  const limit = Math.floor(0x100000000 / n) * n;
  do {
    crypto.getRandomValues(a);
  } while (a[0] >= limit);
  return a[0] % n;
}
Enter fullscreen mode Exit fullscreen mode

A tiny reusable picker

function randInt(n) {
  const a = new Uint32Array(1);
  const limit = Math.floor(0x100000000 / n) * n;
  do {
    crypto.getRandomValues(a);
  } while (a[0] >= limit);
  return a[0] % n;
}

function pickYesNo() {
  return randInt(2) === 0 ? "Yes" : "No";
}

document.querySelector("#go").addEventListener("click", () => {
  document.querySelector("#answer").textContent = pickYesNo();
});
Enter fullscreen mode Exit fullscreen mode
<button id="go">Ask</button>
<p id="answer"></p>
Enter fullscreen mode Exit fullscreen mode

Quick test: is it really 50/50?

let yes = 0;
for (let i = 0; i < 100000; i++) {
  if (pickYesNo() === "Yes") yes++;
}
console.log((yes / 1000).toFixed(1) + "% yes");
Enter fullscreen mode Exit fullscreen mode

You should see a number close to 50%, such as 49.8% or 50.2%.

Takeaway

  • For casual use, either function works.
  • For anything where fairness or unpredictability matters, use crypto.getRandomValues().
  • With two options, % 2 is safe. With other counts, use a rejection loop.

If you want to see this in action, I use the same approach in a free yes or no generator that has card, wheel and quick modes. If you prefer a one-click version, there is also a simple yes or no button.

What would you add? Let me know in the comments.

Top comments (0)