Most tutorials for a password generator javascript project make the same mistake. Instead, they reach for Math.random(), which is fast and easy to use, but was never built for security. It is predictable enough that an attacker who studies its pattern can eventually guess future output. This guide covers how to build a proper password generator in JavaScript, Python, and a few other common languages. Each example uses the actual cryptographic randomness that language provides for exactly this purpose.
You will also find a quick way to sanity check your own generator. There is also an honest answer to whether you should use this code for real accounts or just as a learning exercise.
The One Mistake Almost Every DIY Password Generator Makes
Math.random() in JavaScript, and the plain random module in Python, both generate numbers using a method called a pseudorandom number generator. That method works fine for games, simulations, or shuffling a playlist, when nothing is at stake. It was never designed, however, to resist someone actively trying to predict its output. That resistance is exactly what a password needs to withstand real guessing attempts. Given enough observed output, the internal pattern becomes guessable in a way that matters for anything security related, since the same starting conditions always produce the same sequence.
Both languages provide a separate, cryptographically secure option built for this exact purpose. JavaScript offers the Web Crypto API, and Python offers the secrets module. However, neither takes more effort to use than the insecure version. There is no real tradeoff for choosing the safer option from the start. Once you know which function to call, the secure version becomes just as easy to reach for as the one most tutorials default to.
Building a Password Generator in JavaScript
The Web Crypto API’s getRandomValues function pulls from your operating system’s secure random source, rather than a predictable formula. The example below builds a full password using that function instead of Math.random(). It works the same way whether you run it in a browser or a compatible server environment. This makes it a reasonable default for almost any JavaScript project that needs to generate a credential.
function generatePassword(length = 16) {
const charset =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*";
const randomValues = new Uint32Array(length);
crypto.getRandomValues(randomValues);
return Array.from(randomValues, (n) => charset[n % charset.length]).join("");
}
console.log(generatePassword(16));
This runs directly in a browser console or inside a Node environment that supports the Web Crypto API. Adjust the charset string, for example, if a target site restricts certain symbols. Change the length argument to match whatever the site requires instead. The function returns a new result every time it runs, so calling it once per account keeps every password unique. Because nothing about one call influences the next, there is no need to reset or reseed anything between uses. This is one area where the secure approach is actually simpler than the alternative.
Building a Password Generator in Python
Python’s secrets module exists specifically for security sensitive tasks like this one. It draws from the same operating system source as JavaScript’s Web Crypto API, just through a different interface. This makes it the correct default any time a script needs to generate something unpredictable, whether that is a password, a token, or a session identifier. Reaching for it becomes automatic once the habit is formed, and it costs nothing extra to do.
import secrets
import string
def generate_password(length=16):
charset = string.ascii_letters + string.digits + string.punctuation
return ''.join(secrets.choice(charset) for _ in range(length))
print(generate_password(16))
Swap string.punctuation for a smaller custom set, for example, if a particular site rejects certain symbols. The secrets.choice function is the important piece here. It replaces the insecure random.choice function that many older tutorials still use without mentioning why it matters. Because the difference in code is so small, there is rarely a good reason to reach for the insecure version instead. A single import statement is the entire cost of doing this correctly, which makes the excuse for skipping it fairly thin.
Quick Reference for PowerShell, Laravel, and OpenSSL
A few other environments come up often enough to cover briefly. Also, the full explanation above applies the same way to each of them. Anyone expanding a password generator javascript project into other languages will find the same core principle repeating everywhere, regardless of the syntax involved. The tools change, but the underlying requirement for true randomness never does, no matter which platform comes next.
# PowerShell
$bytes = New-Object byte[] 16
[System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
[Convert]::ToBase64String($bytes)
# Laravel (PHP)
Str::random(16);
# OpenSSL command line
openssl rand -base64 16
Laravel’s Str::random helper already uses secure randomness internally, so it needs no extra configuration. Instead, the OpenSSL command works directly from a terminal, without writing any code at all. That makes it useful for a quick one off password during server setup. Each of these three options relies on the same underlying principle as the JavaScript and Python examples above, even though the syntax looks completely different on the surface. Before reaching for any of them, always check whether the language has its own dedicated secure random function first.
Testing Whether Your Generator Is Actually Random
Run your generator a few dozen times and look at the output as a set. Instead, do not judge any single password on its own. Every character position should show real variety across runs, instead of certain letters or numbers showing up far more often than others. This kind of spot check takes only a couple of minutes and catches the most obvious problems before they cause any real harm.
If you notice an obvious pattern, such as the same character repeatedly appearing in the same position, something is likely wrong. The code is probably reusing a seed value or falling back to an insecure random source. Checking this early, before the code touches any real account, catches most mistakes before they matter. A pattern that shows up across dozens of outputs is not a coincidence worth ignoring, since true randomness should never repeat that consistently by chance. When in doubt, generate a larger sample and check again before trusting the result.
When to Just Use an Existing Generator Instead
Building your own is a truly useful way to understand how randomness and password strength actually work in practice. For a real account you care about, though, an existing, well tested generator carries less risk. Code you wrote for a class project or a weekend experiment has not been reviewed the way a widely used tool has. According to the NIST digital identity guidelines, the quality of the randomness source matters as much as the password’s length and format. That standard applies whether the code came from a textbook example or a production library.
Before anything else, treat your own code as a learning project first. Once you understand why secrets and Web Crypto matter, using a trusted generator for anything protecting real accounts is the more sensible choice. A small bug in personal code is easy to miss and expensive to discover later, often only after something has already gone wrong.
Frequently Asked Questions
Is Math.random() safe to use for a password generator?
No, Math.random() is not cryptographically secure and can be predictable under certain conditions. Use the Web Crypto API’s getRandomValues function instead for anything security related.
What is the difference between Python’s random and secrets modules?
The random module is fast but predictable, which makes it fine for games but unsuitable for passwords. The secrets module draws from a cryptographically secure source built specifically for security sensitive code.
Can I use this code for a real production application?
The JavaScript and Python examples here use secure randomness correctly, so the core logic is sound. However, review any production use carefully, and consider a well established library if the password generator is a critical part of a larger system.
Why does my generated password sometimes look less “random” than expected?
Short passwords or a small character set can produce results that look repetitive purely by chance, even with correct randomness. Increasing the length or the character set size usually resolves the visual pattern without changing the underlying method.
Does Laravel’s Str::random function need any extra setup?
No, it uses secure randomness internally without additional configuration. It is ready to use as soon as the Str helper is available in your project.
Is the OpenSSL command line method secure enough for real passwords?
Yes, since OpenSSL draws from the same type of secure random source used across the other examples here. It works well for a quick password during server setup or scripting.
Build It to Learn, Use a Generator for the Rest
Understanding how a password generator actually creates randomness makes you a better judge of which tools deserve your trust. Building one yourself, using the secure methods covered here, is a truly useful exercise for exactly that reason. It also makes the difference between Math.random() and getRandomValues far more concrete than reading about it ever could. Once you have seen both in action, the choice between them stops being an abstract rule and becomes an obvious one.
Try this password generator now for anything protecting a real account of yours. For more on how length affects strength regardless of which method created the password, see Is a 12 Character Password Actually Long Enough Today. If you prefer a passphrase over a character string, The Passphrase Trick That Makes Passwords Easy to Recall covers that option in detail. Either way, the underlying lesson from this whole exercise carries over regardless of which tool you end up trusting.