Dice rolls, shuffled quiz questions, random colors, loot drops, "pick a student": they all start with Math.random(). On its own it only gives a decimal between 0 and 1, so this lesson is mostly about turning that decimal into the kind of randomness you actually need.
Math.random() // e.g. 0.7281947...
Math.random() // e.g. 0.0412508...
Every call returns a new decimal that is at least 0 and less than 1. It can be exactly 0, but never exactly 1.
Math.random() // 0 to 0.999...
Math.random() * 6 // 0 to 5.999...
Math.floor(Math.random() * 6) // 0, 1, 2, 3, 4, or 5
Math.floor(Math.random() * 6) + 1 // 1, 2, 3, 4, 5, or 6 (a die!)
Math.round(Math.random() * 6) gives 0 to 6, and 0 and 6 come up only half as often as the others, because only half a unit rounds to each end. Math.floor gives every number an equal chance.
function randInt(min, max) { // both ends included
return Math.floor(Math.random() * (max - min + 1)) + min;
}
randInt(1, 6) // die roll
randInt(1, 100) // 1 to 100
randInt(-10, 10) // negatives work too
The + 1 is what makes max reachable. Without it, randInt(1, 6) could never return 6.
There are two common versions of the random-integer function, and mixing them up is the classic off-by-one bug. The only difference is the + 1:
// min included, max EXCLUDED: good for array indexes
function randBelow(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
// min and max both INCLUDED: good for dice and 1-to-100 picks
function randInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
console.log(randBelow(0, 4)); // 0, 1, 2, or 3
console.log(randInt(0, 4)); // 0, 1, 2, 3, or 4
Here is what each common expression can produce:
| Expression | Smallest | Largest |
|---|---|---|
| Math.random() | 0 | just under 1 |
| Math.random() * 10 | 0 | just under 10 |
| Math.floor(Math.random() * 10) | 0 | 9 |
| Math.floor(Math.random() * 11) | 0 | 10 |
| Math.floor(Math.random() * 10) + 1 | 1 | 10 |
| Math.floor(Math.random() * 100) + 1 | 1 | 100 |
Strictly speaking, JavaScript has no separate integer type: these are ordinary numbers that happen to have no decimals.
const randFloat = (min, max) => Math.random() * (max - min) + min;
randFloat(98.0, 99.5).toFixed(1) // e.g. "98.7"
const students = ["Ava", "Ben", "Cal", "Dee"];
const pick = arr => arr[Math.floor(Math.random() * arr.length)];
console.log(`Next up: ${pick(students)}`);
Multiplying by arr.length gives indexes 0 to length - 1, exactly the valid positions.
const heads = Math.random() < 0.5; // 50% true
const critical = Math.random() < 0.1; // 10% true
if (critical) console.log("Critical hit!");
When options should not be equally likely, walk through them subtracting weights:
const loot = [
{ item: "coin", weight: 70 },
{ item: "potion", weight: 25 },
{ item: "sword", weight: 5 }
];
function weightedPick(options) {
const total = options.reduce((sum, o) => sum + o.weight, 0);
let roll = Math.random() * total;
for (const o of options) {
if (roll < o.weight) return o.item;
roll -= o.weight;
}
}
weightedPick(loot); // "coin" about 70% of the time, "sword" about 5%
Randomness is hard to eyeball. Run it many times and count:
const counts = [0, 0, 0, 0, 0, 0];
for (let i = 0; i < 60000; i++) {
counts[randInt(1, 6) - 1]++;
}
console.log(counts);
Math.random() is fine for games and classroom tools, but its numbers can be predicted. Never use it for passwords, tokens, or anything security-related. Browsers provide crypto.getRandomValues() and crypto.randomUUID() for that.
crypto.randomUUID() // e.g. "3b241101-e2bb-4255-8caf-4136c566a962"
For shuffling a whole array fairly, use the Fisher-Yates shuffle from the Array Sort lesson.
Which gives a random whole number from 5 to 10, both included?
Math.floor(Math.random() * 10) + 5Math.floor(Math.random() * 6) + 5Math.floor(Math.random() * 5) + 5Math.round(Math.random() * 5) + 5Can this ever log undefined?
const colors = ["red", "green", "blue"];
console.log(colors[Math.floor(Math.random() * colors.length) + 1]);
colors[3] is undefined. It also never picks "red". Drop the + 1: array indexes start at 0.Which is the right tool to generate a password-reset token?
Math.random()Math.floor(Math.random() * 1e16)crypto.randomUUID()Date.now()Math.random() can be predicted, and so can a timestamp. Use the crypto methods for anything secret.rollDice(n) so it rolls n six-sided dice and returns the totalpick to generate a random "adjective + animal" team name from two arraysMath.round(Math.random() * 6), and see the uneven ends"#" + randInt(0, 0xFFFFFF).toString(16).padStart(6, "0")