Passwords should really be eliminated with secure methods like FIDO2, however, if you have to use them remember while complex random strings are secure, they're nearly impossible to remember. That's why I decided to build a password generator that creates memorable yet secure passwords using three random words (stored locally in the code) separated by customizable numbers and special characters.
The Problem with Traditional Password Generators
Most password generators create strings like Kx9$mL2#vR8
- highly secure but completely unmemorable. This leads people to write passwords down or reuse simple ones, defeating the security purpose entirely.
Three-Word Password Generation
I developed a password generator that creates passwords like Mountain47&Butterfly92*Sunshine
- still highly secure due to length and complexity, but much more memorable because they use real words.
Visual Results
This is the visual look of the website:
How the Algorithm Works
Let’s look at how the algorithm works to generate your password.
1. Word Selection Strategy
The generator uses a local dictionary of over 300 words organized by length (3-10 characters). When you select a target word length, the algorithm:
function getWordsByLength(targetLength) {
// First try to get words of exact length
let filtered = wordList.filter(word => word.length === targetLength);
// If no exact matches, get words within 1 character of target
if (filtered.length === 0) {
filtered = wordList.filter(word =>
Math.abs(word.length - targetLength) <= 1
);
}
// If still no matches, get words within 2 characters of target
if (filtered.length === 0) {
filtered = wordList.filter(word =>
Math.abs(word.length - targetLength) <= 2
);
}
// Fallback to all words if nothing found
return filtered.length > 0 ? filtered : wordList;
}
This ensures you get words as close as possible to your desired length rather than just random short words.
2. Separator Generation
The separators between words are dynamically generated based on your preferences:
function generateSeparator(numCount, specCount) {
let separator = '';
// Add numbers
for (let i = 0; i < numCount; i++) {
separator += getRandomInt(0, 9);
}
// Add special characters
for (let i = 0; i < specCount; i++) {
separator += getRandomElement(specialChars);
}
// Shuffle the separator for randomness
return separator.split('').sort(() => Math.random - 0.5).join('');
}
This creates unique separators like 47&
, 923*
, or 1$+
depending on your settings.
3. Password Assembly
The final password is assembled by:
- Selecting three unique random words
- Capitalizing the first letter of each word
- Generating two unique separators
- Combining everything together
function generatePassword() {
const maxWordLength = parseInt(wordLengthSlider.value);
const numCount = parseInt(numberCountSlider.value);
const specCount = parseInt(specialCountSlider.value);
// Get filtered words
const availableWords = getWordsByLength(maxWordLength);
// Select three unique random words
const words = [];
for (let i = 0; i < 3; i++) {
let word;
do {
word = getRandomElement(availableWords);
} while (words.includes(word) && availableWords.length > 3);
words.push(word);
}
// Capitalize first letter of each word
const capitalizedWords = words.map(word =>
word.charAt(0).toUpperCase() + word.slice(1)
);
// Generate unique separators
const separator1 = generateSeparator(numCount, specCount);
const separator2 = generateSeparator(numCount, specCount);
// Combine everything
return `${capitalizedWords[0]}${separator1}${capitalizedWords[1]}${separator2}
${capitalizedWords[2]}`;
}
Password Strength
- Length: Typically 20-40+ characters
- Character variety: Uppercase, lowercase, numbers, special characters
- Entropy: High due to word selection randomness and separator complexity
- Memorability: Significantly higher than random strings
Example Password
Password: Mountain47&Butterfly92*Sunshine
- Length: 35 characters
- Contains: 3 words + 6 numbers + 2 special chars
UX Features
The interface provides three sliders for instant customization:
- Target Word Length (3-10 characters)
- Numbers per separator (1-4 digits)
- Special characters per separator (1-4 symbols)
Performance Consideration's
The generator is lightning-fast because:
- Uses vanilla JavaScript (no frameworks)
- Lightweight word list stored in memory
- Efficient filtering algorithms
- No external API calls required
Word Database Structure
I organized the word list by length categories for optimal performance:
const wordList = [
// 3-4 letter words
'cat', 'dog', 'sun', 'moon', 'star', 'fire',
// 5 letter words
'apple', 'beach', 'cloud', 'dance', 'eagle',
// ... continuing through 10-letter words
'grasshopper', 'cheesecake', 'refrigerator'
];
The local code includes over 300 words spanning, however, as this dictionary is local, you can customize it of your organizational requirements:
- Nature: Mountain, Waterfall, Sunshine
- Animals: Elephant, Butterfly, Dolphin
- Food: Chocolate, Pineapple, Sandwich
- Objects: Umbrella, Bicycle, Telescope
- Emotions: Happiness, Peaceful, Wonderful
Copy-to-Clipboard Implementation
I implemented The ability to copy the password to your clipboard straight from the tool:
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
showCopiedFeedback();
} catch (err) {
// Fallback for older browsers
const textArea = document.createElement('textarea');
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
showCopiedFeedback();
}
}
HTML Code
Here's the complete, production-ready code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Smart Password Generator</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: white;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.container {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 24px;
padding: 40px;
max-width: 500px;
width: 100%;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
}
h1 {
text-align: center;
color: #2d3748;
margin-bottom: 8px;
font-size: 2rem;
font-weight: 600;
}
.subtitle {
text-align: center;
color: #718096;
margin-bottom: 32px;
font-size: 0.95rem;
}
.password-display {
background: #f7fafc;
border: 2px solid #e2e8f0;
border-radius: 16px;
padding: 20px;
margin-bottom: 24px;
position: relative;
transition: all 0.3s ease;
}
.password-display:hover {
border-color: #667eea;
}
.password-text {
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 1.1rem;
word-break: break-all;
color: #2d3748;
min-height: 28px;
display: flex;
align-items: center;
}
.copy-btn {
position: absolute;
top: 12px;
right: 12px;
background: #667eea;
color: white;
border: none;
border-radius: 8px;
padding: 8px 12px;
font-size: 0.8rem;
cursor: pointer;
transition: all 0.2s ease;
opacity: 0;
}
.password-display:hover .copy-btn {
opacity: 1;
}
.copy-btn:hover {
background: #5a67d8;
transform: translateY(-1px);
}
.copy-btn.copied {
background: #48bb78;
opacity: 1;
}
.controls {
display: grid;
gap: 20px;
margin-bottom: 24px;
}
.control-group {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
background: #f7fafc;
border-radius: 12px;
border: 1px solid #e2e8f0;
}
.control-label {
font-weight: 500;
color: #4a5568;
font-size: 0.9rem;
}
.control-input {
display: flex;
align-items: center;
gap: 12px;
}
input[type="range"] {
width: 80px;
height: 6px;
border-radius: 3px;
background: #e2e8f0;
outline: none;
-webkit-appearance: none;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: #667eea;
cursor: pointer;
transition: all 0.2s ease;
}
input[type="range"]::-webkit-slider-thumb:hover {
transform: scale(1.1);
background: #5a67d8;
}
.value-display {
min-width: 24px;
text-align: center;
font-weight: 600;
color: #667eea;
}
.generate-btn {
width: 100%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 16px;
padding: 16px 24px;
font-size: 1.1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.generate-btn:hover {
transform: translateY(-2px);
box-shadow: 0 12px 24px rgba(102, 126, 234, 0.3);
}
.generate-btn:active {
transform: translateY(0);
}
.generate-btn::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: left 0.5s;
}
.generate-btn:hover::before {
left: 100%;
}
@media (max-width: 480px) {
.container {
padding: 24px;
margin: 10px;
}
h1 {
font-size: 1.6rem;
}
.control-group {
flex-direction: column;
gap: 12px;
text-align: center;
}
}
</style>
</head>
<body>
<div class="container">
<h1>Smart Password Generator</h1>
<p class="subtitle">Generate secure passwords using three random words</p>
<div class="password-display">
<div class="password-text" id="passwordText">Click generate to create your password</div>
<button class="copy-btn" id="copyBtn">Copy</button>
</div>
<div class="controls">
<div class="control-group">
<span class="control-label">Target Word Length</span>
<div class="control-input">
<input type="range" id="wordLength" min="3" max="10" value="6">
<span class="value-display" id="wordLengthValue">6</span>
</div>
</div>
<div class="control-group">
<span class="control-label">Numbers</span>
<div class="control-input">
<input type="range" id="numberCount" min="1" max="4" value="2">
<span class="value-display" id="numberCountValue">2</span>
</div>
</div>
<div class="control-group">
<span class="control-label">Special Characters</span>
<div class="control-input">
<input type="range" id="specialCount" min="1" max="4" value="2">
<span class="value-display" id="specialCountValue">2</span>
</div>
</div>
</div>
<button class="generate-btn" id="generateBtn">Generate Password</button>
</div>
<script>
// Expanded diverse word list covering nature, animals, food, emotions, activities, and more
const wordList = [
// 3-4 letter words
'cat', 'dog', 'sun', 'moon', 'star', 'fire', 'wind', 'rain', 'snow', 'rock', 'tree', 'fish',
'bird', 'lake', 'hill', 'road', 'book', 'cake', 'game', 'home', 'lion', 'bear', 'frog', 'duck',
'rose', 'leaf', 'sand', 'wave', 'gold', 'blue', 'warm', 'cold', 'soft', 'loud', 'fast', 'slow',
'love', 'hope', 'fear', 'joy', 'pain', 'calm', 'wild', 'free', 'door', 'window', 'chair', 'desk',
// 5 letter words
'apple', 'beach', 'cloud', 'dance', 'eagle', 'flame', 'grace', 'happy', 'magic', 'night',
'ocean', 'peace', 'river', 'storm', 'tiger', 'water', 'brave', 'dream', 'earth', 'frost',
'heart', 'light', 'piano', 'queen', 'smile', 'truth', 'whale', 'world', 'youth', 'amber',
'coral', 'grape', 'honey', 'ivory', 'jasper', 'lemon', 'mango', 'olive', 'pearl', 'quilt',
'robin', 'sugar', 'tulip', 'velvet', 'waltz', 'zebra', 'chess', 'flute', 'horse', 'knife',
'mouse', 'peach', 'bread', 'sheep', 'house', 'pizza', 'grape', 'plant', 'shirt', 'boots',
// 6 letter words
'castle', 'dragon', 'forest', 'guitar', 'hammer', 'island', 'jungle', 'kettle', 'legend', 'marble',
'nature', 'orange', 'palace', 'quartz', 'rabbit', 'silver', 'temple', 'unique', 'violet', 'wisdom',
'yellow', 'branch', 'butter', 'cherry', 'flower', 'garden', 'lovely', 'mother', 'nephew', 'pickle',
'stream', 'turkey', 'vegetable', 'window', 'banana', 'candle', 'doctor', 'feather', 'giraffe', 'kitten',
'monkey', 'nephew', 'pickle', 'squirrel', 'turtle', 'valley', 'winter', 'zipper', 'bottle', 'cookie',
'father', 'golden', 'insect', 'jacket', 'ladder', 'magnet', 'nephew', 'pocket', 'powder', 'sister',
// 7 letter words
'balance', 'captain', 'diamond', 'evening', 'freedom', 'gateway', 'harmony', 'journey', 'kitchen',
'library', 'mystery', 'morning', 'rainbow', 'science', 'thunder', 'victory', 'whisper', 'crystal',
'perfect', 'station', 'village', 'healthy', 'bedroom', 'chicken', 'dolphin', 'elephant', 'feather',
'giraffe', 'hamster', 'jewelry', 'kitchen', 'ladybug', 'musical', 'nothing', 'orchard', 'penguin',
'quickly', 'raccoon', 'sparrow', 'tornado', 'umbrella', 'vanilla', 'walrus', 'bicycle', 'blanket',
'cabinet', 'dancing', 'embrace', 'farming', 'growing', 'holiday', 'imagine', 'jogging', 'laundry',
// 8 letter words
'mountain', 'treasure', 'elephant', 'sandwich', 'birthday', 'peaceful', 'starlight', 'universe',
'adventure', 'beautiful', 'chocolate', 'discovery', 'forgotten', 'guardian', 'hospital', 'infinite',
'kindness', 'learning', 'midnight', 'official', 'paradise', 'question', 'romantic', 'sunshine',
'vacation', 'wildlife', 'xylophone', 'yearning', 'butterfly', 'dinosaur', 'flamingo', 'honeybee',
'jellyfish', 'kangaroo', 'laughter', 'medicine', 'necklace', 'purchase', 'sandwich', 'umbrella',
'vitamins', 'windmill', 'breakfast', 'cinnamon', 'doorbell', 'envelope', 'firework', 'greeting',
'hedgehog', 'icecream', 'jukebox', 'keyboard', 'lobster', 'magazine', 'notebook', 'sandwich',
// 9 letter words
'adventure', 'beautiful', 'butterfly', 'challenge', 'education', 'fantastic', 'gardening',
'happiness', 'important', 'knowledge', 'landscape', 'marketing', 'nightfall', 'operation',
'pineapple', 'questions', 'raspberry', 'something', 'tradition', 'valentine', 'wonderful',
'breakfast', 'community', 'delicious', 'everybody', 'furniture', 'gathering', 'handshake',
'invention', 'jellybean', 'knowledge', 'lemonade', 'moonlight', 'newspaper', 'orchestra',
'parachute', 'quicksand', 'rhinoceros', 'snowflake', 'telephone', 'underwear', 'vegetable',
'waterfall', 'xylophone', 'yesterday', 'zucchini', 'adventure', 'beautiful', 'crocodile',
'dragonfly', 'evergreen', 'firetruck', 'gentleman', 'hamburger', 'icicle', 'jellyfish',
// 10 letter words
'basketball', 'everything', 'friendship', 'generation', 'helicopter', 'incredible', 'lighthouse',
'playground', 'restaurant', 'strawberry', 'understand', 'volleyball', 'watermelon', 'background',
'candlelight', 'dictionary', 'everything', 'fingernail', 'grasshopper', 'hummingbird', 'impossible',
'jabberwocky', 'kaleidoscope', 'lawnmower', 'magnificent', 'nutcracker', 'overwhelmed', 'peppermint',
'quarantine', 'rhinoceros', 'skateboard', 'tablespoon', 'underwater', 'vegetables', 'windshield',
'xylophone', 'yellowbird', 'zookeeper', 'automobile', 'broomstick', 'cheesecake', 'dragonfly',
'earthquake', 'fingertips', 'grandmother', 'helicopter', 'icebreaker', 'jackhammer', 'keyboard',
'linebacker', 'motorcycle', 'newsletter', 'observatory', 'porcupine', 'quicksilver', 'refrigerator'
];
const specialChars = ['!', '@', '#', '$', '%', '&', '*', '+', '=', '?'];
// DOM elements
const passwordText = document.getElementById('passwordText');
const copyBtn = document.getElementById('copyBtn');
const generateBtn = document.getElementById('generateBtn');
const wordLengthSlider = document.getElementById('wordLength');
const wordLengthValue = document.getElementById('wordLengthValue');
const numberCountSlider = document.getElementById('numberCount');
const numberCountValue = document.getElementById('numberCountValue');
const specialCountSlider = document.getElementById('specialCount');
const specialCountValue = document.getElementById('specialCountValue');
// Update value displays
wordLengthSlider.addEventListener('input', (e) => {
wordLengthValue.textContent = e.target.value;
});
numberCountSlider.addEventListener('input', (e) => {
numberCountValue.textContent = e.target.value;
});
specialCountSlider.addEventListener('input', (e) => {
specialCountValue.textContent = e.target.value;
});
// Generate random number
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// Get random element from array
function getRandomElement(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
// Filter words by length - get words close to target length
function getWordsByLength(targetLength) {
// First try to get words of exact length
let filtered = wordList.filter(word => word.length === targetLength);
// If no exact matches, get words within 1 character of target
if (filtered.length === 0) {
filtered = wordList.filter(word =>
Math.abs(word.length - targetLength) <= 1
);
}
// If still no matches, get words within 2 characters of target
if (filtered.length === 0) {
filtered = wordList.filter(word =>
Math.abs(word.length - targetLength) <= 2
);
}
// Fallback to all words if nothing found
return filtered.length > 0 ? filtered : wordList;
}
// Generate separator with numbers and special characters
function generateSeparator(numCount, specCount) {
let separator = '';
// Add numbers
for (let i = 0; i < numCount; i++) {
separator += getRandomInt(0, 9);
}
// Add special characters
for (let i = 0; i < specCount; i++) {
separator += getRandomElement(specialChars);
}
// Shuffle the separator
return separator.split('').sort(() => Math.random() - 0.5).join('');
}
// Generate password
function generatePassword() {
const maxWordLength = parseInt(wordLengthSlider.value);
const numCount = parseInt(numberCountSlider.value);
const specCount = parseInt(specialCountSlider.value);
// Get filtered words
const availableWords = getWordsByLength(maxWordLength);
// Select three random words
const words = [];
for (let i = 0; i < 3; i++) {
let word;
do {
word = getRandomElement(availableWords);
} while (words.includes(word) && availableWords.length > 3);
words.push(word);
}
// Capitalize first letter of each word
const capitalizedWords = words.map(word =>
word.charAt(0).toUpperCase() + word.slice(1)
);
// Generate separators
const separator1 = generateSeparator(numCount, specCount);
const separator2 = generateSeparator(numCount, specCount);
// Combine everything
const password = `${capitalizedWords[0]}${separator1}${capitalizedWords[1]}${separator2}${capitalizedWords[2]}`;
return password;
}
// Copy to clipboard
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
copyBtn.textContent = 'Copied!';
copyBtn.classList.add('copied');
setTimeout(() => {
copyBtn.textContent = 'Copy';
copyBtn.classList.remove('copied');
}, 2000);
} catch (err) {
// Fallback for older browsers
const textArea = document.createElement('textarea');
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
copyBtn.textContent = 'Copied!';
copyBtn.classList.add('copied');
setTimeout(() => {
copyBtn.textContent = 'Copy';
copyBtn.classList.remove('copied');
}, 2000);
}
}
// Event listeners
generateBtn.addEventListener('click', () => {
const password = generatePassword();
passwordText.textContent = password;
});
copyBtn.addEventListener('click', () => {
const password = passwordText.textContent;
if (password && password !== 'Click generate to create your password') {
copyToClipboard(password);
}
});
// Generate initial password
window.addEventListener('load', () => {
const password = generatePassword();
passwordText.textContent = password;
});
</script>
</body>
</html>