Have you ever struggled with creating a secure password that meets all the requirements? Or wondered if your current password is strong enough to withstand attacks? In this tutorial, I'll walk you through building a sleek Password Strength Analyzer that provides instant visual feedback and security insights.

What We're Building

We'll create an interactive web app that:

  • Analyzes password strength in real-time
  • Shows detailed feedback on security criteria
  • Checks if your password has been exposed in data breaches
  • Estimates how long it would take to crack the password
  • Looks amazing with a modern UI and smooth animations

You can try the live demo of Password Generator.

The Tech Stack

This project uses vanilla JavaScript, CSS, and HTML - no frameworks needed! We'll integrate with the "Have I Been Pwned" API to check for compromised passwords, and use the Web Crypto API for secure hashing.

The UI Design

Let's start with the overall structure. Our app has three main components:

  1. Input section with password field and strength meter
  2. Criteria checklist (length, characters, etc.)
  3. Security analysis panel with crack time estimates

Here's the HTML structure that brings it all together:

</span>
 lang="en">

     charset="UTF-8">
     name="viewport" content="width=device-width, initial-scale=1.0">
    Password Strength Analyzer
     rel="stylesheet" href="styles.css">
     rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
     href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&family=Orbitron&display=swap" rel="stylesheet">


     class="container">
         class="theme-toggle">
             class="fas fa-moon">
        

         class="card">
             class="header-glow">
                Password Strength Analyzer
                 class="subtitle">Secure Your Digital Life
            

             class="main-content">
                 class="left-panel">
                     class="input-container">
                         type="password" id="password" placeholder="Enter your password" aria-label="Password input">
                         class="toggle-visibility" aria-label="Toggle password visibility">
                             class="fas fa-eye">
                        
                    

                     class="strength-meter">
                         class="progress-container">
                             class="progress-bar" id="strength-bar">
                                 class="glow-effect">
                            
                             class="strength-particles">
                        
                         class="strength-score" id="strength-score">
                             class="score-number">0%
                        
                    

                     class="copy-btn" id="copy-btn">
                         class="fas fa-copy">
                        Copy Password
                    
                

                 class="right-panel">
                     class="complexity-breakdown">
                         class="criteria" data-criteria="length">
                             class="check"> class="fas fa-check">
                             class="criteria-text">8+ Characters
                             class="tooltip">Minimum length for good security
                        
                         class="criteria" data-criteria="uppercase">
                             class="check"> class="fas fa-check">
                             class="criteria-text">Uppercase
                             class="tooltip">A-Z characters
                        
                         class="criteria" data-criteria="lowercase">
                             class="check"> class="fas fa-check">
                             class="criteria-text">Lowercase
                             class="tooltip">a-z characters
                        
                         class="criteria" data-criteria="numbers">
                             class="check"> class="fas fa-check">
                             class="criteria-text">Numbers
                             class="tooltip">0-9 digits
                        
                         class="criteria" data-criteria="symbols">
                             class="check"> class="fas fa-check">
                             class="criteria-text">Symbols
                             class="tooltip">!@#$%^&* etc.
                        
                    
                

                 class="feedback-panel" id="feedback">
                         class="feedback-header">
                            Security Analysis
                              class="crack-time-container">
                                 class="crack-time-label">Crack Time:
                                 id="crack-time">Instant
                            
                             id="emoji-feedback">😐
                        
                         id="feedback-text">Start typing to analyze
                    
            
        
         class="background-effects">
             class="particle-layer">
             class="gradient-overlay">
        
    
    <span class="na">src="script.js">





    Enter fullscreen mode
    


    Exit fullscreen mode
    




The HTML creates a card-based layout with a glowing header and animated background effects. We have separate panels for different functionality, making the interface intuitive and organized.
  
  
  Styling the Interface
The styling is where the magic happens! We're creating a dark-themed interface with glowing accents and smooth animations:

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Poppins', sans-serif;
    background: #0d1b2a;
    min-height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    overflow-y: auto; 
    transition: all 0.5s ease;
}

body.dark {
    background: #1a1a2e;
}

.container {
    position: relative;
    padding: 30px;
    z-index: 1;
    width: 100%;
    max-width: 1200px; 
}

.card {
    background: rgba(255, 255, 255, 0.05);
    border-radius: 25px;
    padding: 2.5rem;
    width: 100%;
    box-shadow: 0 15px 40px rgba(0, 0, 0, 0.2), inset 0 0 0 1px rgba(255, 255, 255, 0.1);
    backdrop-filter: blur(15px);
    position: relative;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    min-height: 0; 
}

.header-glow {
    text-align: center;
    margin-bottom: 2rem;
    position: relative;
}

h1 {
    color: #fff;
    font-size: 2rem;
    font-weight: 600;
    text-shadow: 0 0 10px rgba(0, 123, 255, 0.3);
}

.subtitle {
    color: rgba(255, 255, 255, 0.7);
    font-size: 0.9rem;
    margin-top: 5px;
}

.main-content {
    display: flex;
    gap: 2rem;
    flex-wrap: wrap; 
}

.left-panel, .right-panel {
    flex: 1;
    min-width: 300px; 
}

.input-container {
    position: relative;
    margin-bottom: 1.5rem;
}

#password {
    width: 100%;
    padding: 14px 50px 14px 20px;
    border: none;
    border-radius: 12px;
    background: rgba(255, 255, 255, 0.1);
    color: #fff;
    font-size: 1.1rem;
    transition: all 0.3s ease;
    box-shadow: 0 0 15px rgba(0, 0, 0, 0.2);
}

#password:focus {
    outline: none;
    background: rgba(255, 255, 255, 0.15);
    box-shadow: 0 0 20px rgba(0, 123, 255, 0.3);
}

.toggle-visibility {
    position: absolute;
    right: 15px;
    top: 50%;
    transform: translateY(-50%);
    background: none;
    border: none;
    cursor: pointer;
    color: rgba(255, 255, 255, 0.7);
    transition: color 0.3s ease;
}

.toggle-visibility:hover {
    color: #fff;
}

.strength-meter {
    display: flex;
    align-items: center;
    gap: 15px;
    margin-bottom: 1.5rem;
}

.progress-container {
    flex: 1;
    position: relative;
}

.progress-bar {
    height: 12px;
    background: rgba(255, 255, 255, 0.1);
    border-radius: 6px;
    overflow: hidden;
    position: relative;
}

.progress-bar::after {
    content: '';
    position: absolute;
    height: 100%;
    width: var(--strength, 0%);
    background: linear-gradient(90deg, #ff4d4d, #ffcd39, #28a745);
    transition: width 0.6s ease;
}

.glow-effect {
    position: absolute;
    top: -50%;
    left: 0;
    width: 100%;
    height: 200%;
    background: linear-gradient(transparent, rgba(255, 255, 255, 0.2), transparent);
    transform: rotate(30deg);
    animation: glow 3s infinite;
}

.strength-score {
    font-size: 1.2rem;
    font-weight: 700;
    color: #fff;
    text-shadow: 0 0 10px rgba(0, 123, 255, 0.3);
}

.score-number {
    font-family: 'Orbitron', sans-serif;
}

.complexity-breakdown {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.criteria {
    display: flex;
    align-items: center;
    gap: 10px;
    position: relative;
    color: rgba(255, 255, 255, 0.8);
    transition: all 0.3s ease;
}

.criteria:hover {
    transform: translateX(5px);
}

.check {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.1);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    color: transparent;
    transition: all 0.4s ease;
    position: relative;
}

.check.active {
    background: #28a745;
    color: #fff;
    box-shadow: 0 0 15px rgba(40, 167, 69, 0.5);
}

.check.active::after {
    content: '';
    position: absolute;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    background: rgba(40, 167, 69, 0.3);
    animation: pulse 2s infinite;
}

.tooltip {
    position: absolute;
    background: rgba(0, 0, 0, 0.9);
    color: #fff;
    padding: 8px 12px;
    border-radius: 8px;
    font-size: 0.9rem;
    top: -40px;
    left: 50%;
    transform: translateX(-50%);
    opacity: 0;
    pointer-events: none;
    transition: all 0.3s ease;
}

.criteria:hover .tooltip {
    opacity: 1;
    top: -50px;
}

.feedback-panel {
    background: rgba(255, 255, 255, 0.05);
    padding: 20px;
    border-radius: 15px;
    position: relative;
    overflow: hidden;
    flex-grow: 1;
    width: 100%;
}

.feedback-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 10px;
}

h3 {
    color: #fff;
    font-size: 1.2rem;
}

#emoji-feedback {
    font-size: 2.5rem;
    transition: transform 0.3s ease;
}

#feedback-text {
    color: rgba(255, 255, 255, 0.9);
    margin-bottom: 10px;
    text-align: center;
}

.crack-time-container {
    display: flex;
    align-items: center;
    gap: 10px;
}

.crack-time-label {
    color: rgba(255, 255, 255, 0.7);
    font-size: 0.9rem;
}

#crack-time span {
    font-weight: 600;
    color: #00ddeb;
    text-shadow: 0 0 10px rgba(0, 221, 235, 0.3);
}

.copy-btn {
    width: 100%;
    padding: 14px;
    background: linear-gradient(45deg, #007bff, #00ddeb);
    color: #fff;
    border: none;
    border-radius: 12px;
    cursor: pointer;
    font-weight: 600;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    transition: all 0.3s ease;
    position: relative;
    overflow: hidden;
}

.copy-btn:hover {
    transform: translateY(-2px);
    box-shadow: 0 5px 20px rgba(0, 123, 255, 0.4);
}

.copy-btn::after {
    content: '';
    position: absolute;
    width: 200%;
    height: 200%;
    background: rgba(255, 255, 255, 0.1);
    transform: rotate(30deg);
    top: -50%;
    left: -50%;
    animation: shine 4s infinite;
}

.theme-toggle {
    position: absolute;
    top: 30px;
    right: 30px;
    cursor: pointer;
    font-size: 1.8rem;
    color: rgba(255, 255, 255, 0.7);
    transition: all 0.3s ease;
}

.theme-toggle:hover {
    color: #fff;
    transform: rotate(180deg);
}

.background-effects {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
}

.particle-layer {
    position: absolute;
    width: 100%;
    height: 100%;
    background: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800"%3E%3Ccircle fill="rgba(255,255,255,0.1)" cx="400" cy="400" r="2"/%3E%3C/svg%3E') repeat;
    animation: float 20s infinite linear;
}

.gradient-overlay {
    position: absolute;
    width: 100%;
    height: 100%;
    background: radial-gradient(circle at center, rgba(0, 123, 255, 0.1) 0%, transparent 70%);
    animation: pulse 10s infinite;
}

@keyframes glow {
    0%, 100% { transform: translateX(-100%) rotate(30deg); }
    50% { transform: translateX(100%) rotate(30deg); }
}

@keyframes pulse {
    0%, 100% { opacity: 0.5; }
    50% { opacity: 1; }
}

@keyframes shine {
    0% { transform: translateX(-100%) rotate(30deg); }
    100% { transform: translateX(100%) rotate(30deg); }
}

@keyframes float {
    0% { background-position: 0 0; }
    100% { background-position: 100px 100px; }
}

@media (max-width: 768px) {
    .main-content {
        flex-direction: column;
    }
    .left-panel, .right-panel {
        width: 50%%;
    }
    .card {
        padding: 1.5rem;
    }
}

@media (max-height: 600px) {
    .card {
        max-height: 100vh;
        overflow-y: auto; 
    }
}



    Enter fullscreen mode
    


    Exit fullscreen mode
    




Some key CSS features include:
A gradient progress bar that changes colors based on password strength
Pulsing animations for active criteria
Subtle particle background for visual depth
Responsive design that works on mobile and desktop
Smooth transitions between states

  
  
  The JavaScript Engine
Now for the brain of our application. The JavaScript handles:

document.addEventListener('DOMContentLoaded', () => {
    const passwordInput = document.getElementById('password');
    const strengthBar = document.getElementById('strength-bar');
    const strengthScore = document.getElementById('strength-score');
    const scoreNumber = strengthScore.querySelector('.score-number');
    const feedbackText = document.getElementById('feedback-text');
    const emojiFeedback = document.getElementById('emoji-feedback');
    const crackTime = document.getElementById('crack-time').querySelector('span');
    const toggleVisibility = document.querySelector('.toggle-visibility');
    const copyBtn = document.getElementById('copy-btn');
    const themeToggle = document.querySelector('.theme-toggle');
    const checks = document.querySelectorAll('.check');

    const commonPasswords = ['password123', '123456', 'qwerty', 'admin'];

    // Function to calculate SHA-1 hash (using subtle crypto)
    async function sha1(str) {
        const buffer = new TextEncoder().encode(str);
        const hashBuffer = await crypto.subtle.digest('SHA-1', buffer);
        const hashArray = Array.from(new Uint8Array(hashBuffer));
        return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
    }

    // Check if password has been pwned using HIBP API
    async function checkPwned(password) {
        if (!password) return { pwned: false, count: 0 };

        try {
            const hash = await sha1(password);
            const prefix = hash.slice(0, 5);
            const suffix = hash.slice(5).toUpperCase();

            const response = await fetch(`https://api.pwnedpasswords.com/range/${prefix}`, {
                headers: { 'User-Agent': 'PasswordStrengthChecker' }
            });
            const text = await response.text();

            const lines = text.split('\n');
            for (const line of lines) {
                const [hashSuffix, count] = line.split(':');
                if (hashSuffix === suffix) {
                    return { pwned: true, count: parseInt(count, 10) };
                }
            }
            return { pwned: false, count: 0 };
        } catch (error) {
            console.error('Error checking HIBP:', error);
            return { pwned: false, count: 0, error: true };
        }
    }

    function calculateStrength(password) {
        let score = 0;
        const criteria = {
            length: password.length >= 8,
            uppercase: /[A-Z]/.test(password),
            lowercase: /[a-z]/.test(password),
            numbers: /[0-9]/.test(password),
            symbols: /[^A-Za-z0-9]/.test(password)
        };

        if (criteria.length) score += 20;
        if (criteria.uppercase) score += 20;
        if (criteria.lowercase) score += 20;
        if (criteria.numbers) score += 20;
        if (criteria.symbols) score += 20;
        score += Math.min(20, password.length - 8) * 2;
        if (commonPasswords.includes(password.toLowerCase())) score = Math.min(score, 20);

        return Math.min(100, score);
    }

    function estimateCrackTime(score) {
        if (score < 20) return 'Instant';
        if (score < 40) return 'Seconds';
        if (score < 60) return 'Minutes';
        if (score < 80) return 'Hours';
        if (score < 95) return 'Days';
        return 'Centuries';
    }

    async function getFeedback(score, password) {
        if (!password) return 'Start typing to analyze';

        const pwnedResult = await checkPwned(password);
        if (pwnedResult.error) return 'Error checking breach status';
        if (pwnedResult.pwned) {
            return `WARNING: This password was found in ${pwnedResult.count} breaches!`;
        }
        if (commonPasswords.includes(password.toLowerCase())) return 'Warning: Common password detected!';
        if (score < 40) return 'Weak: Add more complexity';
        if (score < 60) return 'Fair: Include diverse characters';
        if (score < 80) return 'Good: Almost there!';
        return 'Excellent: Highly secure!';
    }

    function getEmoji(score, pwned) {
        if (pwned) return '⚠️';
        if (score < 20) return '😞';
        if (score < 40) return '😟';
        if (score < 60) return '😐';
        if (score < 80) return '🙂';
        return '😎';
    }

    async function updateUI() {
        const password = passwordInput.value;
        const score = calculateStrength(password);
        const pwnedResult = await checkPwned(password);

        // Update strength bar and score
        strengthBar.style.setProperty('--strength', `${score}%`);
        scoreNumber.textContent = score;
        strengthScore.style.color = pwnedResult.pwned ? '#ff4d4d' : score < 40 ? '#ff4d4d' : score < 80 ? '#ffcd39' : '#28a745';

        // Update criteria checks
        checks.forEach(check => {
            const criterion = check.parentElement.dataset.criteria;
            const isMet = {
                length: password.length >= 8,
                uppercase: /[A-Z]/.test(password),
                lowercase: /[a-z]/.test(password),
                numbers: /[0-9]/.test(password),
                symbols: /[^A-Za-z0-9]/.test(password)
            }[criterion];
            check.classList.toggle('active', isMet);
        });

        // Update feedback
        feedbackText.textContent = await getFeedback(score, password);
        emojiFeedback.textContent = getEmoji(score, pwnedResult.pwned);
        emojiFeedback.style.transform = `scale(${1 + score/200})`;
        crackTime.textContent = estimateCrackTime(score);

        // Flash warning if pwned
        if (pwnedResult.pwned) {
            feedbackText.style.color = '#ff4d4d';
            feedbackText.style.fontWeight = 'bold';
        } else {
            feedbackText.style.color = 'rgba(255, 255, 255, 0.9)';
            feedbackText.style.fontWeight = 'normal';
        }
    }

    // Debounce function to limit API calls
    function debounce(func, wait) {
        let timeout;
        return function executedFunction(...args) {
            const later = () => {
                clearTimeout(timeout);
                func(...args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    }

    // Event Listeners
    const debouncedUpdateUI = debounce(updateUI, 500); // Debounce to 500ms
    passwordInput.addEventListener('input', debouncedUpdateUI);

    toggleVisibility.addEventListener('click', () => {
        const type = passwordInput.type === 'password' ? 'text' : 'password';
        passwordInput.type = type;
        toggleVisibility.querySelector('i').classList.toggle('fa-eye');
        toggleVisibility.querySelector('i').classList.toggle('fa-eye-slash');
    });

    copyBtn.addEventListener('click', () => {
        navigator.clipboard.writeText(passwordInput.value);
        copyBtn.querySelector('span').textContent = 'Copied!';
        setTimeout(() => copyBtn.querySelector('span').textContent = 'Copy Password', 2000);
    });

    themeToggle.addEventListener('click', () => {
        document.body.classList.toggle('dark');
        themeToggle.querySelector('i').classList.toggle('fa-moon');
        themeToggle.querySelector('i').classList.toggle('fa-sun');
    });

    // Initial UI update
    updateUI();
});

// Custom property for strength bar animation
document.styleSheets[0].insertRule(`
    .progress-bar::after {
        width: var(--strength, 0%);
    }
`, 0);




    Enter fullscreen mode
    


    Exit fullscreen mode
    




Let's break down the key functions:
  
  
  Password Strength Algorithm
Our algorithm weighs multiple factors:

function calculateStrength(password) {
    let score = 0;
    const criteria = {
        length: password.length >= 8,
        uppercase: /[A-Z]/.test(password),
        lowercase: /[a-z]/.test(password),
        numbers: /[0-9]/.test(password),
        symbols: /[^A-Za-z0-9]/.test(password)
    };

    if (criteria.length) score += 20;
    if (criteria.uppercase) score += 20;
    if (criteria.lowercase) score += 20;
    if (criteria.numbers) score += 20;
    if (criteria.symbols) score += 20;
    score += Math.min(20, password.length - 8) * 2;
    if (commonPasswords.includes(password.toLowerCase())) score = Math.min(score, 20);

    return Math.min(100, score);
}



    Enter fullscreen mode
    


    Exit fullscreen mode
    




Each criteria adds 20 points, with bonus points for extra length. We also penalize common passwords regardless of their complexity.
  
  
  Breach Checking with HIBP API
We securely check if a password has been compromised using the k-anonymity model:

async function checkPwned(password) {
    if (!password) return { pwned: false, count: 0 };

    try {
        const hash = await sha1(password);
        const prefix = hash.slice(0, 5);
        const suffix = hash.slice(5).toUpperCase();

        const response = await fetch(`https://api.pwnedpasswords.com/range/${prefix}`);
        const text = await response.text();

        const lines = text.split('\n');
        for (const line of lines) {
            const [hashSuffix, count] = line.split(':');
            if (hashSuffix === suffix) {
                return { pwned: true, count: parseInt(count, 10) };
            }
        }
        return { pwned: false, count: 0 };
    } catch (error) {
        console.error('Error checking HIBP:', error);
        return { pwned: false, count: 0, error: true };
    }
}



    Enter fullscreen mode
    


    Exit fullscreen mode
    




This implementation only sends the first 5 characters of the password hash, protecting the user's privacy while still checking against the breach database.
  
  
  User Feedback System
We provide meaningful feedback based on password strength:

async function getFeedback(score, password) {
    if (!password) return 'Start typing to analyze';

    const pwnedResult = await checkPwned(password);
    if (pwnedResult.error) return 'Error checking breach status';
    if (pwnedResult.pwned) {
        return `WARNING: This password was found in ${pwnedResult.count} breaches!`;
    }
    if (commonPasswords.includes(password.toLowerCase())) return 'Warning: Common password detected!';
    if (score < 40) return 'Weak: Add more complexity';
    if (score < 60) return 'Fair: Include diverse characters';
    if (score < 80) return 'Good: Almost there!';
    return 'Excellent: Highly secure!';
}



    Enter fullscreen mode
    


    Exit fullscreen mode
    




We also estimate how long it would take to crack the password:

function estimateCrackTime(score) {
    if (score < 20) return 'Instant';
    if (score < 40) return 'Seconds';
    if (score < 60) return 'Minutes';
    if (score < 80) return 'Hours';
    if (score < 95) return 'Days';
    return 'Centuries';
}



    Enter fullscreen mode
    


    Exit fullscreen mode
    





  
  
  Performance Considerations
To prevent overwhelming the API with requests, we implement debouncing:

function debounce(func, wait) {
    let timeout;
    return function executedFunction(...args) {
        const later = () => {
            clearTimeout(timeout);
            func(...args);
        };
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
    };
}

const debouncedUpdateUI = debounce(updateUI, 500);



    Enter fullscreen mode
    


    Exit fullscreen mode
    




This ensures we only make API calls after the user has stopped typing for 500ms.
  
  
  User Experience Enhancements
Small details make this app truly delightful to use:

Visual password visibility toggle - A small eye icon lets users see what they're typing

Copy button with feedback - Easily copy your password with confirmation

Emoji reactions - Expressive emojis that change based on password strength

Theme toggle - Switch between dark and light modes

Tooltips - Helpful explanations when hovering over criteria

  
  
  Security Considerations
When building a password-related tool, security is paramount:
All password analysis happens client-side
We never store or transmit the full password
The HIBP API integration uses k-anonymity for privacy
We use the Web Crypto API for secure hashing

  
  
  Taking It Further
Here are some ways you could enhance this project:
Add a password generator
Implement zxcvbn for more sophisticated password analysis
Add local storage for settings (with appropriate warnings)
Create a browser extension version
Add internationalization support

  
  
  What I Learned
Building this project taught me several valuable lessons:
The importance of debouncing when working with APIs
How to implement the k-anonymity model for secure password checking
Techniques for creating smooth CSS transitions with JavaScript variables
The balance between security and user experience
How to provide meaningful feedback without overwhelming users

  
  
  Conclusion
Creating a secure password doesn't have to be a frustrating experience. With thoughtful UI design and helpful feedback, we can make security both accessible and engaging.This Password Strength Analyzer demonstrates how we can combine robust security practices with excellent UX to create tools people actually want to use.What security tools do you find most helpful? How would you improve this password analyzer? Share your thoughts in the comments!
  
  
  Resources

OWASP Authentication Guidelines
Have I Been Pwned API
NIST Password Guidelines
zxcvbn Password Strength Estimator