Programming

Password generator in JavaScript: a lesson

Try the live tool: Password generator →

Live tool

Password generator (crypto)

Runs in your browser; nothing is sent to a server.

What is JavaScript Password Generator?

A password generator takes random characters from the set you choose and puts them in a row. The main question is where the randomness comes from. A browser has two ways. Math.random is fast, but it is not for passwords, because its output can be predicted. crypto.getRandomValues gives bytes from the operating system's cryptographic source. Only the second one is used for passwords. Everything happens inside the page and nothing goes to a server.

What is JavaScript Password Generator used for?

This lesson shows secure random numbers, a form, and copying in JavaScript in one place.

  • Getting a strong password for a new account
  • A long string for Wi-Fi or an API key
  • Learning the difference between Math.random and crypto
  • A copy button with the Clipboard API

What can you do with JavaScript Password Generator?

The user picks a length (for example 8–64) and the sets: lowercase, uppercase, digits, symbols. The code joins the chosen sets into one string. Then it takes a random index for each character. The finished password can be copied with one button. On HTTPS the browser gives navigator.clipboard; without it, the old execCommand path remains. The password is not written to localStorage and is not sent anywhere. To try the finished tool, see /tools/password.

Is JavaScript Password Generator hard to learn?

The code is short, but there is a trap. If you simply take a random byte "% length", some characters come up more often than others. This is called modulo bias. The fix is rejection sampling: you throw away a number above the largest limit that divides evenly by the set length and take a new one. Also, if you want at least one character from each chosen set, you must shuffle afterwards, and the shuffle must use crypto too.

Advantages and limits

Advantage: no server, no trace, and the code works in every browser. Limit: keeping the password is your job — use a password manager for that. Length adds more strength than character types.

Basic ideas

If you know these four ideas, every line of the code is clear.

  • crypto.getRandomValues — cryptographic randomness
  • Math.random — for games, not passwords
  • Rejection sampling — against modulo bias
  • Entropy ≈ length × log2(set size)

A short example

A function that takes a random index without bias, and the loop that builds the password:

1. A random index without bias:

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

This function returns a random index from 0 to max-1.

limit is the largest number below 2^32 that max divides evenly.

crypto.getRandomValues writes a cryptographic random number into buf.

If the number is at or above limit, it is thrown away and a new one is taken. That way no index comes up more often than another.

At the end, % max puts the number in the 0…max-1 range.

2. Character sets:

const SETS = {
  lower: "abcdefghijklmnopqrstuvwxyz",
  upper: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
  digits: "0123456789",
  symbols: "!@#$%^&*()-_=+[]{};:,.?",
};

This object holds four character pools: lowercase, uppercase, digits, symbols.

Whichever ones the user ticks, the password is drawn only from those pools.

A bigger pool gives each character more options, so the password is harder to guess.

3. The loop that builds the password:

function makePassword(length, chosen) {
  const pool = chosen.map((k) => SETS[k]).join("");
  if (!pool) throw new Error("Choose at least one set");
  let out = "";
  for (let i = 0; i < length; i++) out += pool[randomIndex(pool.length)];
  return out;
}

chosen is the list of picked set names, for example ["lower", "digits"]. pool joins their characters into one string.

If nothing is picked, the function throws an error instead of returning an empty password.

The loop runs length times. Each time randomIndex picks one character from the pool and adds it to the end of out.

Result: the finished password string shown on screen, for example k7#Qm2…

4. At least one character from each set, then a shuffle:

function makeStrongPassword(length, chosen) {
  const chars = chosen.map((k) => SETS[k][randomIndex(SETS[k].length)]);
  const pool = chosen.map((k) => SETS[k]).join("");
  while (chars.length < length) chars.push(pool[randomIndex(pool.length)]);
  for (let i = chars.length - 1; i > 0; i--) {
    const j = randomIndex(i + 1);
    [chars[i], chars[j]] = [chars[j], chars[i]];
  }
  return chars.join("");
}

The first line takes one required character from each picked set. So if digits are picked, the password always has a digit.

while fills the remaining places from the combined pool.

The for loop is a Fisher–Yates shuffle: it swaps places at random so the required characters do not always sit at the start.

Result: a password with every picked type, in an order nobody can guess.

Common questions

  • Can I use Math.random? Not for passwords. Its output is not cryptographic and can be predicted.
  • Is the password sent to a server? No. It is computed in the browser and stored nowhere.
  • How many characters is good? At least 16. Length helps more than character types.
  • Where is the finished tool? On nibrascode.com/tools/password.