Published July 3, 2026

Build a Password Generator in JavaScript and a Local Password Vault in Python

Passwords are something we use every day, which makes them a great subject for a practical coding project. In this tutorial, we will build the project in three stages:

  1. A quick password generator using HTML and JavaScript

  2. An enhanced generator with customization options

  3. A local Python password vault that encrypts saved credentials

The first two projects can be published as tools on a website. The third project is intentionally local and is best treated as an educational demonstration of what goes into building password-management software.

Important: The Python vault is a learning project, not a replacement for an established, professionally audited password manager. Do not use it as the only storage location for critical personal, financial, school, or business credentials.


Part 1: Build a Quick Password Generator

The first version has one job: create a random 12-character password and let the user copy it.

It uses a single HTML file containing the page structure, styling, and JavaScript. That makes it easy to test locally or upload to a website.

What the tool includes

  • A read-only box that displays the password

  • A Generate button

  • A Copy button

  • Uppercase letters, lowercase letters, numbers, and symbols

  • The browser’s Web Crypto API for random values

The basic HTML

<main class="card">
  <h1>Password Generator</h1>
  <p>Create a random 12-character password.</p>

  <input
    id="password"
    type="text"
    readonly
    aria-label="Generated password"
  >

  <div class="buttons">
    <button id="generateButton">Generate</button>
    <button id="copyButton">Copy</button>
  </div>

  <p id="message" aria-live="polite"></p>
</main>

The input is set to readonly, so visitors can copy the generated password without accidentally changing it.

The JavaScript

const passwordBox = document.getElementById('password');
const message = document.getElementById('message');

function randomIndex(max) {
  const numbers = new Uint32Array(1);
  crypto.getRandomValues(numbers);
  return numbers[0] % max;
}

function generatePassword() {
  const characters =
    'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%&*';

  let password = '';

  for (let i = 0; i < 12; i++) {
    password += characters[randomIndex(characters.length)];
  }

  passwordBox.value = password;
  message.textContent = '';
}

The characters string contains every character the generator is allowed to use. The loop runs 12 times, chooses one character during each pass, and adds it to the password.

We use crypto.getRandomValues() instead of Math.random(). The Web Crypto API provides cryptographically strong random values and is a better choice when generating passwords.

Add the Copy button

async function copyPassword() {
  if (!passwordBox.value) {
    generatePassword();
  }

  try {
    await navigator.clipboard.writeText(passwordBox.value);
    message.textContent = 'Copied!';
  } catch {
    passwordBox.select();
    document.execCommand('copy');
    message.textContent = 'Copied!';
  }
}

document
  .getElementById('generateButton')
  .addEventListener('click', generatePassword);

document
  .getElementById('copyButton')
  .addEventListener('click', copyPassword);

generatePassword();

Calling generatePassword() at the bottom creates a password as soon as the page loads.

Publish Part 1

The complete version includes simple responsive styling and can run as a standalone webpage.

Download: password-generator-part-1.html

You can upload the file as its own page or move the HTML, CSS, and JavaScript into your existing website template.


Part 2: Build an Enhanced Password Generator

The first version is useful, but visitors may want more control. In Part 2, we add:

  • A password-length slider

  • Lowercase, uppercase, number, and symbol options

  • A basic strength indicator

  • A check that prevents all character options from being turned off

  • A guarantee that each selected character type appears at least once

Add the controls

<div class="row">
  <label for="length">
    Length: <strong id="lengthValue">16</strong>
  </label>

  <input
    id="length"
    type="range"
    min="8"
    max="32"
    value="16"
  >
</div>

<div class="options">
  <label>
    <input id="lowercase" type="checkbox" checked>
    Lowercase
  </label>

  <label>
    <input id="uppercase" type="checkbox" checked>
    Uppercase
  </label>

  <label>
    <input id="numbers" type="checkbox" checked>
    Numbers
  </label>

  <label>
    <input id="symbols" type="checkbox" checked>
    Symbols
  </label>
</div>

The slider controls password length, while the checkboxes decide which groups of characters are available.

Organize the character sets

const sets = {
  lowercase: 'abcdefghijklmnopqrstuvwxyz',
  uppercase: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
  numbers: '0123456789',
  symbols: '!@#$%&*?'
};

Keeping the character sets in an object makes it easier to match each checkbox with the correct characters.

Generate a customized password

function randomCharacter(characters) {
  return characters[randomIndex(characters.length)];
}

function generatePassword() {
  const selectedSets = Object.keys(sets)
    .filter(id => document.getElementById(id).checked)
    .map(id => sets[id]);

  if (selectedSets.length === 0) {
    message.textContent = 'Choose at least one character type.';
    passwordBox.value = '';
    strength.textContent = '';
    return;
  }

  const length = Number(lengthSlider.value);
  const allCharacters = selectedSets.join('');

  let password = selectedSets
    .map(randomCharacter)
    .join('');

  while (password.length < length) {
    password += randomCharacter(allCharacters);
  }

  passwordBox.value = shuffle(password);
  message.textContent = '';
  updateStrength(length, selectedSets.length);
}

The first characters are selected directly from each enabled group. This guarantees that a password with all four options enabled contains at least one lowercase letter, uppercase letter, number, and symbol.

The rest of the password is filled from the combined character list.

Shuffle the password

function shuffle(text) {
  const characters = [...text];

  for (let i = characters.length - 1; i > 0; i--) {
    const j = randomIndex(i + 1);

    [characters[i], characters[j]] =
      [characters[j], characters[i]];
  }

  return characters.join('');
}

Without this step, the required characters would always appear at the beginning in a predictable order. Shuffling places them throughout the generated password.

Add a simple strength label

function updateStrength(length, variety) {
  const score = length + variety * 3;

  strength.textContent =
    score >= 28 ? 'Strength: Strong' :
    score >= 20 ? 'Strength: Good' :
                  'Strength: Basic';
}

This is intentionally a simple educational indicator. A real password-strength system would consider many more factors and should not promise that a password is unbreakable.

Publish Part 2

Download: password-generator-part-2.html

This version is a better choice for a permanent tool page because it gives visitors more control while still remaining easy to understand.


Part 3: Build a Local Password Vault with Python

The first two tools generate passwords, but they do not save them. Part 3 explores a much larger question:

What would it take to build a program that generates and stores passwords?

For this version, we move away from a public website and build a local desktop application with Python and Tkinter.

The application can:

  • Create an encrypted vault protected by a master password

  • Generate passwords

  • Save a website, username, password, and notes

  • Search saved entries

  • Edit and delete entries

  • Copy a password to the clipboard

  • Clear the copied password from the clipboard after 30 seconds

  • Lock the vault without closing the application

Why make this version local?

An online password manager is not automatically unsafe, but it is much harder to build correctly. A public version would need much more than PHP and MySQL.

You would need to think carefully about:

  • Secure account authentication

  • HTTPS and secure session handling

  • Protection against cross-site scripting and request forgery

  • Rate limiting and brute-force protection

  • Encryption and key management

  • Database and server security

  • Backups that do not expose decrypted information

  • Password recovery and account recovery

  • Monitoring, updates, testing, and independent security review

OWASP’s security guidance emphasizes that cryptographic storage, key management, browser storage, and server security all require separate protections. A local design removes the public server and database, reducing the number of exposed components, but it does not make the application automatically safe.

Malware, a compromised computer, weak master passwords, insecure backups, clipboard history, and programming mistakes can still expose information.

How the local vault works

The application does not save the master password. Instead, it uses the master password and a random salt to derive a 256-bit encryption key with scrypt.

def derive_key(master_password, salt):
    kdf = Scrypt(
        salt=salt,
        length=32,
        n=2**17,
        r=8,
        p=1
    )

    return kdf.derive(
        master_password.encode("utf-8")
    )

Scrypt is a memory-hard key derivation function. The settings used in this project follow OWASP’s listed minimum scrypt configuration when Argon2id is unavailable.

The program uses Python’s secrets module to generate passwords.

def generate_password(length=20):
    groups = [
        string.ascii_lowercase,
        string.ascii_uppercase,
        string.digits,
        "!@#$%^&*()-_=+[]{};:,.?"
    ]

    characters = [
        secrets.choice(group)
        for group in groups
    ]

    combined = "".join(groups)

    characters.extend(
        secrets.choice(combined)
        for _ in range(length - len(characters))
    )

    secrets.SystemRandom().shuffle(characters)
    return "".join(characters)

Python recommends the secrets module rather than the standard random module when generating passwords, tokens, and other security-sensitive values.

The full vault contents are converted to JSON and encrypted with AES-256-GCM before being written to disk.

nonce = os.urandom(12)

plaintext = json.dumps({
    "entries": entries
}).encode("utf-8")

ciphertext = AESGCM(key).encrypt(
    nonce,
    plaintext,
    ASSOCIATED_DATA
)

AES-GCM is authenticated encryption. It protects the confidentiality of the vault and allows the application to detect invalid or altered ciphertext.

The saved JSON file contains items such as:

  • The format version

  • The scrypt settings

  • A random salt

  • A random nonce

  • Encrypted ciphertext

It does not contain readable website names, usernames, notes, or passwords.

Install the Python dependency

Python 3.10 or newer is recommended.

python3 -m pip install cryptography

Run the application

python3 local_password_vault.py

On the first launch, the application asks you to create a master password of at least 12 characters.

The encrypted vault is stored in the user’s home folder:

~/.local_password_vault/vault.json

Download Part 3

The complete project includes:

  • local_password_vault.py

  • requirements-password-vault.txt

  • LOCAL_PASSWORD_VAULT_README.md

Important limitations

This application demonstrates several important ideas, but it is not a complete commercial password manager.

It does not include:

  • Independent security auditing

  • Browser extensions or automatic form filling

  • Device synchronization

  • Secure cloud backup

  • Multi-factor authentication

  • Master-password recovery

  • Protection from malware running on an unlocked computer

  • Extensive accessibility, compatibility, and penetration testing

There is also no way to recover the vault if the master password is forgotten.

For real-world credentials, use a mature password manager with a strong security history and independent review.


What This Three-Part Project Teaches

This series starts with a small JavaScript tool and gradually introduces more advanced concepts.

Part 1 teaches:

  • Basic HTML controls

  • JavaScript functions

  • Loops and strings

  • DOM updates

  • Button events

  • The Clipboard API

  • Browser-generated random values

Part 2 teaches:

  • Range inputs and checkboxes

  • Objects and arrays

  • Filtering and mapping

  • Input validation

  • Character-category requirements

  • Shuffling

  • Simple user feedback

Part 3 teaches:

  • Python desktop interfaces with Tkinter

  • Local file storage

  • JSON serialization

  • Password-based key derivation

  • Authenticated encryption

  • Random salts and nonces

  • Searching, editing, and deleting records

  • Clipboard cleanup

  • Why security software requires careful design


Final Thoughts

A password generator is a small project that can become a useful tool almost immediately. Adding customization makes it more practical, and building a local encrypted vault shows how quickly the security requirements grow once an application begins storing sensitive information.

That progression is what makes this a valuable three-part coding project. It begins with something a beginner can build in minutes and ends with a realistic look at encryption, local storage, user interfaces, and responsible software development.

Build the first two versions for your website. Experiment with the third version locally. Most importantly, treat the password vault as a way to learn what secure software requires, not as a shortcut around using an established password manager.