A password generator looks like a very simple tool.
Choose a length.
Select some character types.
Click Generate.
Done.
But while building my own password generator, I started thinking about a more important question:
Where does the password actually get generated?
If a password is generated on a server, the generated value has to reach that server and then be returned to your browser. TLS can protect the connection while the data is in transit, but the server itself still has access to the generated password.
That creates an additional trust requirement.
So when I built the password generator for EvoTechTool, I decided not to have a server generate the password at all.
The generation happens entirely inside the browser.
No password-generation API.
No backend request.
No account required.
And it can still generate passwords when you're offline.
Here's how I built it.
First problem: Math.random()
JavaScript gives us Math.random().
It's convenient, and it's perfectly useful for many things: animations, games, randomized UI elements, and other situations where cryptographic security isn't required.
But password generation is different.
Math.random() is not designed to provide cryptographically secure random values.
For security-sensitive randomness, browsers provide the Web Crypto API:
crypto.getRandomValues()
MDN describes crypto.getRandomValues() as providing cryptographically strong random values. The implementation uses a pseudorandom generator seeded with sufficient entropy rather than relying on a "truly random" process for every value.
That's exactly the kind of randomness I wanted for this project.
A simple example looks like this:
const values = new Uint32Array(10);
crypto.getRandomValues(values);
console.log(values);
The browser fills the array with cryptographically strong random values.
But there's another problem.
The modulo bias problem
A straightforward implementation might do this:
const index = randomValue % charset.length;
It looks perfectly reasonable.
But there's a subtle statistical problem.
If the number of possible random values isn't evenly divisible by the size of the character set, some characters can be selected slightly more often than others.
That's called modulo bias.
The difference can be tiny, but when you're deliberately building a security-focused tool, I don't see much reason to introduce a bias when it can be avoided.
So I used rejection sampling.
The basic idea is simple:
- Generate a cryptographically secure random value.
- Calculate the largest range that can be divided evenly by the character-set size.
- Reject values outside that range.
- Use modulo only on the remaining values.
For example:
function randomIndex(max) {
const maxUint32 = 0x100000000;
const limit = Math.floor(maxUint32 / max) * max;
const array = new Uint32Array(1);
let value;
do {
crypto.getRandomValues(array);
value = array[0];
} while (value >= limit);
return value % max;
}
Now the generated index is selected without the modulo bias.
The performance difference is negligible for the small number of random values needed to generate a normal password.
The actual password generation
With that in place, generating the password becomes fairly straightforward:
function generatePassword(length, charset) {
let password = '';
for (let i = 0; i < length; i++) {
const index = randomIndex(charset.length);
password += charset[index];
}
return password;
}
The important part isn't the loop.
It's where the randomness comes from.
Instead of:
Math.random()
the generator uses:
crypto.getRandomValues()
And instead of asking a server to generate the password, everything happens locally.
Why client-side generation?
There are a few reasons I prefer this architecture for a password generator.
1. No password-generation request
The browser doesn't need to send the generated password to an API.
The password is created locally.
2. Less trust required
A server-side generator means you have to trust the service handling the generated password.
A local generator removes the password-generation server from the architecture entirely.
That's not a guarantee that the entire website is magically secure — browsers, extensions, devices, malware, screenshots, clipboard access, and other parts of the environment still matter.
But it does remove one unnecessary place where the generated password could be exposed.
3. You can inspect it yourself
This is something I really like about client-side tools.
Open DevTools and go to the Network tab.
Generate a password.
You can inspect what the page is doing instead of simply trusting a statement like "your password never leaves your browser."
For a genuinely local implementation, generating a password doesn't require a request to a password-generation API.
What I put into the tool
The password generator in EvoTechTool currently supports:
- Passwords up to 128 characters
- Uppercase letters
- Lowercase letters
- Numbers
- Symbols
- Excluding ambiguous characters such as
l,I,1,O, and0 - Password strength information
- Entropy calculation
- Offline generation
The interface is intentionally simple.
I didn't want the security-related part of the tool buried under unnecessary features.
What about password strength?
This part is interesting because a password can look complicated without actually being very random.
For a uniformly random password, a simplified entropy calculation is:
entropy = length × log₂(character_set_size)
For example, increasing the password length increases the possible number of combinations exponentially.
But entropy calculations have an important limitation:
They describe the randomness of the generation process, not a guarantee about how long a real attacker will take to crack the password.
The actual difficulty of cracking a password depends on things such as the attack model, hashing algorithm, rate limiting, hardware, and whether the password was actually generated uniformly.
So I treat the crack-time figure in the tool as an estimate rather than a promise.
Random passwords vs. human-created passwords
This is also why I prefer generated passwords for accounts where I don't need to memorize the password.
Humans are surprisingly predictable when creating passwords.
We tend to use:
- familiar words
- names
- dates
- patterns
- substitutions like
@fora - predictable capitalization
- repeated structures
A randomly generated password doesn't have those human patterns.
NIST's current Digital Identity Guidelines allow passwords to be randomly assigned and emphasize the value of password managers and randomly generated unique passwords.
NIST also notes that password managers can help users create unique, long, complex passwords for different accounts.
So you don't need to memorize a different random password for every website.
Let the password manager handle that part.
One important distinction
There's a difference between password generation and password storage.
This article is about generating passwords.
If you're building an application that stores users' passwords, that's a completely different problem.
You should never store users' passwords as plaintext. Modern password-storage guidance recommends appropriate password hashing algorithms such as Argon2id, bcrypt, or PBKDF2 rather than reversible encryption or plaintext storage.
In other words:
Generating a password securely and storing a password securely are two different engineering problems.
How to use a password generator properly
If you're using a password generator, I'd follow a few simple rules.
1. Use a unique password for every account
If one password is compromised, you don't want the same password protecting five other accounts.
2. Use a password manager
You don't need to memorize dozens of random passwords.
A password manager can generate and store unique credentials for you.
3. Check whether the generator is actually client-side
Don't blindly trust a website's privacy claim.
Open DevTools.
Watch the Network tab.
Generate a password and see what requests are made.
4. Don't paste sensitive passwords into random websites
Even if a generator claims to be secure, think about the trust model before using it.
For a client-side generator, the ideal architecture is simple:
Your Browser
│
├── Generate random values
│
├── Build password
│
└── Display password
No password-generation server
That's the architecture I wanted for my own tool.
Try it
I built the password generator as part of EvoTechTool, a collection of small browser-based tools focused on being free, private, and requiring no signup.
Password Generator:
https://evotechtool.pages.dev/password-generator.html
The project is built around a simple idea:
Free · Private · No Signup
For the password generator specifically, the goal is straightforward:
Generate the password where it belongs — inside your browser.
If you're building a password generator yourself, I'd pay particular attention to two things:
Use a cryptographically secure random source such as crypto.getRandomValues(), and consider whether the password actually needs to leave the browser.
The UI can always be improved later.
The security model should come first.
What do you think about client-side password generation?
Do you prefer generating passwords entirely in the browser, or do you see situations where a server-side generator makes more sense?
Top comments (0)