SnippetJavaScript

Random integer in a range

An inclusive-bounds random integer helper built on Math.random, with a note on why it's unsuitable for anything security-sensitive.

Last updated

Snippetjavascript
function randomInt(min, max) {
  const lo = Math.ceil(min);
  const hi = Math.floor(max);
  return lo + Math.floor(Math.random() * (hi - lo + 1));
}

// randomInt(1, 6) -> an integer from 1 to 6, inclusive, like a die roll

How it works

Math.random() alone returns a float in [0, 1), so getting a usable integer means scaling and flooring it: multiplying by (hi - lo + 1) and flooring spreads the result evenly across every whole number from 0 to hi - lo, and adding lo shifts that range up to start at lo instead of 0. The +1 is what makes max INCLUSIVE — leaving it off would make max reachable in the float but never as a floored integer.

Math.ceil(min) and Math.floor(max) guard against non-integer bounds: pass randomInt(1.5, 6.5) and you still get a clean integer between 2 and 6, rather than a fractional edge leaking into the result.

Edge cases to know

  • →Math.random() is NOT cryptographically secure — its output is predictable enough to reconstruct in some engines. Never use this for tokens, passwords, or anything security-sensitive; use crypto.getRandomValues() (browser and Node) for that instead.
  • →If min > max it does not error — it silently returns values below min (randomInt(10, 5) yields 6 to 9). There is no signal at all, so guard the order of your bounds if they aren't both hard-coded.
  • →The distribution is uniform enough for games and sampling, but Math.random()'s underlying PRNG quality varies by engine — not a concern for typical UI use, but worth knowing if you're doing anything statistical.

Part by part

Math.ceil(min)
Rounds the lower bound up, so a fractional minimum cannot produce a result below what the caller asked for.
Math.floor(max)
Rounds the upper bound down, for the mirror reason. Together the two make the function safe with non-integer arguments.
(hi - lo + 1)
The count of possible values. The plus one is what makes the range inclusive at both ends — without it the maximum can never be returned.

Common mistakes

  • Math.floor(Math.random() * (max - min)) + min

    Missing the plus one, so the maximum is never produced. A die roll written this way returns 1 to 5 forever, and the bias is easy to miss in casual testing.

    lo + Math.floor(Math.random() * (hi - lo + 1))
  • Math.round(Math.random() * (max - min)) + min

    Rounding makes the two end values half as likely as the rest, because they are reached from only one side of the rounding boundary. The distribution looks fine until you count.

  • randomInt(10, 5)

    Reversed arguments produce a negative range, so the multiplication yields a negative offset and the result falls below the intended minimum — here, values from 6 to 9 rather than an error. The function does not validate its inputs.

Questions

Is this suitable for security?

No. Math.random is not cryptographically secure and its output can be predicted from enough samples. Use crypto.getRandomValues for tokens, passwords, session identifiers or anything an attacker benefits from guessing.

Is the distribution actually uniform?

Close enough for games and sampling. The floating-point mapping introduces a bias too small to matter at ordinary range sizes, which is not true of the crypto equivalents where modulo bias needs handling explicitly.

How do I pick a random array element?

randomInt(0, arr.length - 1) as the index. The minus one matters because the range is inclusive here, unlike the exclusive convention most array helpers use.

What if min equals max?

It returns that value every time, which is correct — a range of one has one member. Reversed arguments are the case worth guarding, since those fail silently rather than obviously.

Related in Snippets