Home
| Colors: |
March 2026

Get a Random Number with crypto.getRandomValues() in JavaScript

The Road to Random

I'm adding some randomizing functions to bitty. I wanted to use `crypto.getRandomValues()` instead of `Math.random()`. While it's unlikely to matter, I figure using the better tool makes sense.

Here's the core functions:

JavaScript

function randomInt(min, max) {
  const seeker = new Uint32Array(1);
  crypto.getRandomValues(seeker);
  const base = seeker[0];
  const modder = Math.abs(max - min) + 1;
  let result = (base % modder) + Math.min(min, max);
  return result;
}

JavaScript

function randomFloat(min, max) {
  const seeker = new Uint32Array(1);
  crypto.getRandomValues(seeker);
  const base = seeker[0] / (0xFFFFFFFF + 1);
  const distance = Math.abs(max - min);
  let result = (base * distance) + Math.min(min, max);
  return result;
}

Snake Eyes

You pass the functions your minimum and maximum values. They spit out a random value. The numbers are inclusive. To simulate rolling a six sided die, you'd do this:

const roll = randomInt(1, 6);

Negative Space

I don't generally need random negative numbers. That's no reason to avoid them. The functions handles them just fine:

randomInt(-2, 2);

Min/Max

The Math.min(min, max) part of the `result` lines is a nice-to-have. It allows you to flip the inputs without breaking the function. I find that useful for negative numbers. Both of these operate on within the same range:

randomInt(-1, -6);
randomInt(-6, -1);

How's That Now?

The crypto.getRandomValues(seeker) function requires a different approach than Math.random(). It works by filling an array with random numbers instead of providing a single random output like the Math function.

The approach here makes an array with only one item and populates it with the random number. Then, it uses the remainder operator (%) to get a value in the min/max distance. Finally, the min value is added back in to shift it into the right place.

The float version works by using bytes. It divides the incoming number by 0xFFFFFFFF + 1. That places the result between 0 and 1. We multiply that by the distance between the min and max numbers using the same approach generally used with Math.random().

More Code is Better (in this case)

The crypto functions are notably longer than their Math.random() counterparts. Take this standard integer approach, for example:

function randomIntViaMathRandom(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

That doesn't matter. The entire point of baking things into functions is to encapsulate the complexity.

-a

end of line

Endnotes

Here's the code I used to make sure swaping the min and max worked properly:

test code

JavaScript

function randomIntMathCheck(min, max, base) {
  const modder = Math.abs(max - min) + 1;
  let result = (base % modder) + Math.min(min, max);
  return result;
}


function runTest(values) {
  const result = randomIntMathCheck(values[0], values[1], values[2]);
  if (result === values[3]) {
    console.log(`PASS: ${values}`);
  } else {
    console.error(`FAIL: Got ${result} instead of ${values[3]} for ${values}`);
  }
}

JavaScript

function runTests() {
  [
    [0, 4, 0, 0],
    [0, 4, 1, 1],
    [0, 4, 2, 2],
    [0, 4, 3, 3],
    [0, 4, 4, 4],
    [0, 4, 5, 0],
    [0, 4, 6, 1],
    [0, 4, 7, 2],
    [0, 4, 8, 3],
    [0, 4, 9, 4],
    [0, 4, 10, 0],

    [1, 4, 0, 1],
    [1, 4, 1, 2],
    [1, 4, 2, 3],
    [1, 4, 3, 4],
    [1, 4, 4, 1],
    [1, 4, 5, 2],
    [1, 4, 6, 3],
    [1, 4, 7, 4],
    [1, 4, 8, 1],

    [4, 1, 0, 1],
    [4, 1, 1, 2],
    [4, 1, 2, 3],
    [4, 1, 3, 4],
    [4, 1, 4, 1],
    [4, 1, 5, 2],
    [4, 1, 6, 3],
    [4, 1, 7, 4],
    [4, 1, 8, 1],

    [-2, 2, 0, -2],
    [-2, 2, 1, -1],
    [-2, 2, 2, 0],
    [-2, 2, 3, 1],
    [-2, 2, 4, 2],
    [-2, 2, 5, -2],
    [-2, 2, 6, -1],
    [-2, 2, 7, 0],
    [-2, 2, 8, 1],
    [-2, 2, 9, 2],
    [-2, 2, 10, -2],

    [2, -2, 0, -2],
    [2, -2, 1, -1],
    [2, -2, 2, 0],
    [2, -2, 3, 1],
    [2, -2, 4, 2],
    [2, -2, 5, -2],
    [2, -2, 6, -1],
    [2, -2, 7, 0],
    [2, -2, 8, 1],
    [2, -2, 9, 2],
    [2, -2, 10, -2],

  ].forEach((test) => {
    runTest(test);
  });
}


runTests();

References

Some languages use % as a modulo operator. It's similar, but not quite the same as the remainder. Check the link for details.