Calculators & Utilities

Free Password Generator Widget

Let visitors create strong, random passwords right on your site.

A lightweight password generator with a length slider, character-set toggles for uppercase, lowercase, digits, and symbols, plus a live entropy strength meter and one-click copy. It is great for SaaS tools, security blogs, and IT or developer sites that want a handy utility their audience will actually use. Install it with a single script tag — passwords are generated locally in the browser, nothing leaves the page.

Live demo

Embed this widget

Paste either snippet into your page — a Custom HTML block on WordPress, Shopify, or Squarespace works too. Both snippets update live as you customize, so what you copy is exactly what you configured.

Hosted (recommended)html
<script src="https://hayneslab.dev/widgets/password-generator.js" async></script>
<!-- Free Password Generator widget by haynes lab — https://hayneslab.dev/tools/widgets/password-generator -->
Inline (self-contained)html
<!-- Free Password Generator widget by haynes lab — https://hayneslab.dev/tools/widgets/password-generator -->
<script>
/*!
 * haynes lab — Password Generator widget (free)
 * Docs & embed code: https://hayneslab.dev/tools/widgets/password-generator
 */
(function () {
  'use strict';

  var PREFIX = 'hlw-password-generator';
  var STYLE_ID = 'hlw-password-generator-styles';

  var SET_DEFS = [
    { key: 'upper', label: 'ABC', name: 'Uppercase letters', chars: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' },
    { key: 'lower', label: 'abc', name: 'Lowercase letters', chars: 'abcdefghijklmnopqrstuvwxyz' },
    { key: 'digits', label: '123', name: 'Digits', chars: '0123456789' },
    { key: 'symbols', label: '#$&', name: 'Symbols', chars: '!@#$%^&*()-_=+[]{};:,.<>?/~' }
  ];

  function findScript() {
    return document.currentScript ||
      document.querySelector('script[src*="widgets/password-generator.js"]');
  }

  function injectStyles() {
    if (document.getElementById(STYLE_ID)) return;
    var css =
      '.' + PREFIX + ', .' + PREFIX + ' * { box-sizing: border-box; }' +
      '.' + PREFIX + ' { position: relative; max-width: 480px; margin: 0 auto; padding: 24px 24px 32px;' +
      ' font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;' +
      ' font-size: 16px; line-height: 1.5; color: #1f2937; background: #ffffff;' +
      ' border: 1px solid #e5e7eb; border-radius: 12px; }' +
      '.' + PREFIX + '__title { margin: 0 0 14px; font-size: 18px; font-weight: 600; color: #111827; }' +
      '.' + PREFIX + '__output { display: flex; gap: 8px; margin-bottom: 16px; }' +
      '.' + PREFIX + '__field { flex: 1; min-width: 0; padding: 10px 12px; font-size: 15px;' +
      ' font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: #111827;' +
      ' background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 8px; }' +
      '.' + PREFIX + '__copy { flex: 0 0 auto; padding: 10px 14px; font-size: 14px; font-weight: 600;' +
      ' color: #ffffff; background: var(--hlw-accent); border: 0; border-radius: 8px; cursor: pointer; }' +
      '.' + PREFIX + '__copy:hover { filter: brightness(0.92); }' +
      '.' + PREFIX + '__copy:focus-visible { outline: 2px solid var(--hlw-accent); outline-offset: 2px; }' +
      '.' + PREFIX + '__row { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }' +
      '.' + PREFIX + '__lenlabel { font-size: 14px; color: #374151; white-space: nowrap; }' +
      '.' + PREFIX + '__lenval { font-weight: 600; color: #111827; min-width: 2ch; text-align: right; }' +
      '.' + PREFIX + '__range { flex: 1; accent-color: var(--hlw-accent); }' +
      '.' + PREFIX + '__toggles { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }' +
      '.' + PREFIX + '__toggle { display: inline-flex; align-items: center; gap: 6px; padding: 7px 12px;' +
      ' font-size: 13px; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;' +
      ' color: #374151; background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 999px;' +
      ' cursor: pointer; }' +
      '.' + PREFIX + '__toggle[aria-pressed="true"] { color: var(--hlw-accent);' +
      ' border-color: var(--hlw-accent); background: #ffffff; }' +
      '.' + PREFIX + '__toggle:focus-visible { outline: 2px solid var(--hlw-accent); outline-offset: 2px; }' +
      '.' + PREFIX + '__generate { width: 100%; padding: 11px 16px; margin-bottom: 14px; font-size: 15px;' +
      ' font-weight: 600; color: #ffffff; background: var(--hlw-accent); border: 0;' +
      ' border-radius: 8px; cursor: pointer; }' +
      '.' + PREFIX + '__generate:hover { filter: brightness(0.92); }' +
      '.' + PREFIX + '__generate:focus-visible { outline: 2px solid var(--hlw-accent); outline-offset: 2px; }' +
      '.' + PREFIX + '__meter { height: 6px; background: #e5e7eb; border-radius: 999px; overflow: hidden;' +
      ' margin-bottom: 6px; }' +
      '.' + PREFIX + '__meterfill { height: 100%; width: 0; border-radius: 999px;' +
      ' transition: width 0.25s ease, background-color 0.25s ease; }' +
      '.' + PREFIX + '__meterlabel { font-size: 12px; color: #6b7280; }' +
      '.' + PREFIX + '__credit { position: absolute; right: 10px; bottom: 6px; font-size: 11px;' +
      ' display: inline-flex; align-items: center; gap: 4px; color: #9ca3af; text-decoration: none; }' +
      '.' + PREFIX + '__credit svg { display: block; }' +
      '.' + PREFIX + '__credit:hover { color: #6b7280; text-decoration: underline; }' +
      '@media (max-width: 560px) { .' + PREFIX + ' { padding: 18px 16px 32px; } }';
    var style = document.createElement('style');
    style.id = STYLE_ID;
    style.textContent = css;
    document.head.appendChild(style);
  }

  function el(tag, className, text) {
    var node = document.createElement(tag);
    if (className) node.className = className;
    if (text != null) node.textContent = text;
    return node;
  }

  function logoSvg() {
    var svgNS = 'http://www.w3.org/2000/svg';
    var svg = document.createElementNS(svgNS, 'svg');
    svg.setAttribute('viewBox', '0 0 32 32');
    svg.setAttribute('width', '13');
    svg.setAttribute('height', '13');
    svg.setAttribute('aria-hidden', 'true');
    svg.setAttribute('focusable', 'false');
    function circle(cx, cy, r, attrs) {
      var c = document.createElementNS(svgNS, 'circle');
      c.setAttribute('cx', cx);
      c.setAttribute('cy', cy);
      c.setAttribute('r', r);
      for (var key in attrs) c.setAttribute(key, attrs[key]);
      svg.appendChild(c);
    }
    circle(16, 16, 16, { fill: '#0f0a0a' });
    circle(16, 16, 12, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.3 });
    circle(16, 16, 9, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.5 });
    circle(16, 16, 6, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1.2, opacity: 0.9 });
    circle(16, 16, 3, { fill: '#06b6d4' });
    circle(24.5, 7.5, 2, { fill: '#f97316' });
    circle(7, 16, 1.8, { fill: '#f97316' });
    circle(20, 20, 1.5, { fill: '#f97316' });
    return svg;
  }

  function randomIndex(max) {
    if (window.crypto && window.crypto.getRandomValues) {
      var buf = new Uint32Array(1);
      window.crypto.getRandomValues(buf);
      return buf[0] % max;
    }
    return Math.floor(Math.random() * max);
  }

  function build(script) {
    var length = parseInt(script.getAttribute('data-hl-length'), 10);
    if (isNaN(length) || length < 4) length = 16;
    if (length > 64) length = 64;
    var accent = script.getAttribute('data-hl-accent') || '#f97316';

    injectStyles();

    var root = el('div', PREFIX);
    root.setAttribute('role', 'region');
    root.setAttribute('aria-label', 'Password generator');
    root.style.setProperty('--hlw-accent', accent);

    root.appendChild(el('h3', PREFIX + '__title', 'Password Generator'));

    var output = el('div', PREFIX + '__output');
    var field = el('input', PREFIX + '__field');
    field.type = 'text';
    field.readOnly = true;
    field.setAttribute('aria-label', 'Generated password');
    var copyBtn = el('button', PREFIX + '__copy', 'Copy');
    copyBtn.type = 'button';
    output.appendChild(field);
    output.appendChild(copyBtn);
    root.appendChild(output);

    var row = el('div', PREFIX + '__row');
    row.appendChild(el('span', PREFIX + '__lenlabel', 'Length'));
    var range = el('input', PREFIX + '__range');
    range.type = 'range';
    range.min = '4';
    range.max = '64';
    range.value = String(length);
    range.setAttribute('aria-label', 'Password length');
    var lenVal = el('span', PREFIX + '__lenval', String(length));
    row.appendChild(range);
    row.appendChild(lenVal);
    root.appendChild(row);

    var togglesWrap = el('div', PREFIX + '__toggles');
    var sets = SET_DEFS.map(function (def, i) {
      return { label: def.label, name: def.name, chars: def.chars, on: i < 3 };
    });
    sets.forEach(function (set) {
      var btn = el('button', PREFIX + '__toggle', set.label);
      btn.type = 'button';
      btn.setAttribute('aria-pressed', set.on ? 'true' : 'false');
      btn.setAttribute('aria-label', 'Include ' + set.name);
      btn.addEventListener('click', function () {
        set.on = !set.on;
        btn.setAttribute('aria-pressed', set.on ? 'true' : 'false');
        generate();
      });
      togglesWrap.appendChild(btn);
    });
    root.appendChild(togglesWrap);

    var generateBtn = el('button', PREFIX + '__generate', 'Generate Password');
    generateBtn.type = 'button';
    root.appendChild(generateBtn);

    var meter = el('div', PREFIX + '__meter');
    var meterFill = el('div', PREFIX + '__meterfill');
    meter.appendChild(meterFill);
    root.appendChild(meter);
    var meterLabel = el('div', PREFIX + '__meterlabel');
    root.appendChild(meterLabel);

    function activeSets() {
      return sets.filter(function (s) { return s.on; });
    }

    function updateMeter() {
      var len = parseInt(range.value, 10);
      var pool = activeSets().reduce(function (n, s) { return n + s.chars.length; }, 0);
      var bits = pool ? Math.round(len * Math.log2(pool)) : 0;
      var pct = Math.max(0, Math.min(100, (bits / 128) * 100));
      var label, color;
      if (!pool) { label = 'Select at least one character set'; color = '#9ca3af'; }
      else if (bits < 45) { label = 'Weak'; color = '#ef4444'; }
      else if (bits < 65) { label = 'Fair'; color = '#f59e0b'; }
      else if (bits < 90) { label = 'Strong'; color = '#22c55e'; }
      else { label = 'Very strong'; color = '#16a34a'; }
      meterFill.style.width = pct + '%';
      meterFill.style.backgroundColor = color;
      meterLabel.textContent = pool
        ? label + ' — about ' + bits + ' bits of entropy'
        : label;
    }

    function generate() {
      var on = activeSets();
      var len = parseInt(range.value, 10);
      if (!on.length) { field.value = ''; updateMeter(); return; }
      var pool = on.map(function (s) { return s.chars; }).join('');
      var pw = '';
      for (var i = 0; i < len; i++) pw += pool.charAt(randomIndex(pool.length));
      field.value = pw;
      copyBtn.textContent = 'Copy';
      updateMeter();
    }

    range.addEventListener('input', function () {
      lenVal.textContent = range.value;
      generate();
    });
    generateBtn.addEventListener('click', generate);
    copyBtn.addEventListener('click', function () {
      if (!field.value) return;
      function done() { copyBtn.textContent = 'Copied!'; }
      function fallback() {
        field.select();
        try { document.execCommand('copy'); done(); } catch (e) { field.focus(); }
      }
      if (navigator.clipboard && navigator.clipboard.writeText) {
        navigator.clipboard.writeText(field.value).then(done, fallback);
      } else {
        fallback();
      }
    });

    var credit = el('a', PREFIX + '__credit');
    credit.appendChild(logoSvg());
    credit.appendChild(document.createTextNode('Powered by haynes lab'));
    credit.href = 'https://hayneslab.dev/tools/widgets/password-generator';
    credit.target = '_blank';
    credit.rel = 'noopener';
    root.appendChild(credit);

    if (script.parentNode && script.parentNode.nodeName !== 'HEAD') {
      script.insertAdjacentElement('afterend', root);
    } else {
      document.body.appendChild(root);
    }

    generate();
  }

  function init() {
    var script = findScript();
    if (!script) {
      console.warn('[haynes lab password-generator] Could not locate its own script tag. Nothing rendered.');
      return;
    }
    build(script);
  }

  if (document.body) {
    init();
  } else {
    document.addEventListener('DOMContentLoaded', init);
  }
})();

</script>

Configuration

Every option is an attribute on the script tag — the same attributes the customizer above exposes. All attributes are optional unless marked required — defaults apply when omitted.

AttributeDefaultDescription
data-hl-length"16"Initial password length, clamped between 4 and 64 characters.
data-hl-accent"#f97316"Color for the generate/copy buttons, slider, and active toggles.

Frequently asked questions

How do I install the Password Generator?

Paste the script snippet where you want the generator to appear — it renders inline right after the tag and creates a first password automatically.

Can I change the default length and colors?

Yes — set data-hl-length for the starting password length (4–64) and data-hl-accent for the highlight color on buttons, the slider, and active toggles.

Does it work on WordPress, Shopify, or Squarespace?

Yes — it is a single vanilla JS file with no dependencies, so it works on WordPress, Shopify, Squarespace, or any site that can embed a script tag.

More free widgets

Need a custom widget?

We build custom widgets, integrations, and whole products. Tell us what your site needs and we'll scope it.

Get in Touch