WordLock — Reference
This app generates passwords entirely in your browser. Nothing leaves your device — no network requests are made, no passwords are logged or stored on any server. Your settings and generation history are saved only in your browser's local storage.
Seven generator types are available, each optimized for a different use case, alongside the options they share, the appearance settings, and what the site is built with. Click any tile to jump to its documentation.
The site is also an installable web app: install it from your browser's menu and it runs in its own window and works fully offline — a generator that never talks to a server has no reason to require a network.
Simple
Generates a random string of characters from selected character sets.
Each position draws a character type uniformly among the enabled types, then a character from that type — see Strength & Entropy for why that is slightly different from a uniform draw over the combined pool. No placement guarantees are made — to enforce minimums per type, use the Advanced generator.
Back to topAdvanced
Like Simple, but with per-character-type minimum and maximum counts, plus a customizable symbol set.
Min / Max
Max — no more than this many lowercase letters will appear.
Set Min to 0 to make lowercase optional.
Min / Max
Min / Max
Min / Max + Symbol Set
All — activate every symbol.
Common — activate a curated subset:
! @ # $ % & * - _ + = ?None — deactivate all but one (at least one must remain).
Toggle individual chips to fine-tune. At least one symbol must remain active when the Symbols count is non-zero.
| selected), the set is kept as chosen. The strength breakdown states the measured cost from the counts actually drawn. Off by default.Min / Max
Min defaults to 0 — emoji is effectively off until you raise the minimum or the random fill selects it. Set Min to 0 and Max to 0 to exclude emoji entirely. See Shared Options — Emoji for the pool details.
Words
Combines random words from a built-in wordlist with separators, affixes, capitalization, and leet-speak substitutions.
🌟Flame-🌊River). Toggling it immediately rebuilds the password using the current words without re-rolling them. See Shared Options — Emoji.The lock icon beside the pills freezes the current prefix, separator, and suffix so that clicking a word pill or the "Generate" button replaces only the words without re-rolling the affixes. While locked, the strength breakdown prices the held values at 0 bits (“locked — reused, adds nothing”) — a value reused across passwords is a constant, not a secret.
Passphrase
Builds a phrase from a configurable sequence of word slots, each with a grammar role (adjective, adverb, noun, verb) and an optional thematic category.
| Type | Color | Available Categories |
|---|---|---|
| Adj | Blue | Random, Colors, Size, Texture, Mood, Weather, Time |
| Adv | Teal | Random, Manner, Intensity, Time, Place |
| Noun | Slate | Random, Animals, Vehicles, Food, Places, Nature, Tech, Jobs, Music, Sports |
| Verb | Amber | Random, Movement, Action, Nature, Cognition |
Each category draws from a curated list of ~100–220 words per category (2,400+ words total across all categories). The word pool is large enough that repeated words are rare even across many generations.
Slots can be added, removed, and reordered with the arrow buttons. The default structure is Adj + Noun + Verb.
Wireless
A WiFi-optimized passphrase generator with router-friendly defaults and an alliteration mode.
Wireless uses the same slot-based word engine as Passphrase but ships with presets suited for WiFi network names: memorable, typeable on a phone keyboard, and visually distinct. All Passphrase options are available here with independent settings storage.
Generated passwords are guaranteed to be at least 8 characters. If a result falls below this minimum, the generator automatically retries (up to 10 times) before accepting the output.
- The generator finds letters that exist in all active slot categories, then picks one at random.
- Every word pool is filtered to words beginning with that letter before a word is drawn.
- The active letter is shown as a badge on the toggle button (e.g. S).
- Clicking a word pill to swap a single word also respects the locked letter.
- Hitting "Generate WiFi Password" picks a new shared letter each time.
- If no letter is shared across all categories (rare), the constraint is silently dropped for that slot.
Alliteration produces passwords that are easier to dictate aloud: "Silent Sparrow Storm" is far easier to spell out over a phone than a random mix of words.
wifi.* namespace in localStorage.Mad Lib
Fills a pre-written sentence template with random words, then compresses it into a password.
| Name | Template structure |
|---|---|
| The Hero | The {adj} {noun} {adv} {verb} the {adj} {noun} |
| The Villain | A {adj} {noun} {adv} {verb} every {noun} |
| The Quest | {noun} {adv} {verb} beyond the {adj} {noun} |
| Science! | The {adj} {noun} {adv} {verb} any {noun} |
| Wise Proverb | A {adj} {noun} {adv} {verb} alone |
| Breaking News | {adj} {noun} {adv} {verb} the {noun} |
| Haiku-ish | {adj} {noun} {adv} {verb} |
| Epic Tale | {noun} and {noun} {adv} {verb} the {adj} world |
| Mystery | A {noun} {adv} {verb} the {adj} {noun} |
| Romance | The {adj} {noun} {adv} {verb} a {adj} {noun} |
| Sci-Fi | {adj} {noun} {adv} {verb} every {noun} |
| Fable | Once the {adj} {noun} {adv} {verb} alone |
Literal words in the template (e.g. "the", "beyond", "alone") are included verbatim in the readable phrase preview but are not compressed into the password — only the filled placeholders are joined with the separator.
Numbers
Generates a numeric PIN or code with configurable limits on repeated and sequential digits.
Numbers has no look-alike exclusion on purpose: an all-digit code has no letters for 0 or 1 to be confused with, so excluding them would spend entropy (3.32 → 3.00 bits per digit) buying nothing.
Back to topStrength & Entropy
Every generated password shows its entropy in bits, with a meter and a full breakdown. Here is exactly how those numbers are computed.
The bits count the generator's random choices, not the characters in the output. A fixed separator is chosen once, by you, in settings: every password you generate carries a space in the same positions, so across all possible outputs there is exactly one way those positions can read — and log2 1 = 0. A random symbol is drawn fresh at generation time from 18 candidates, so even an attacker who knows your settings must try all 18 — log2 18 ≈ 4.2 bits of real guessing work.
Assuming the attacker knows your settings is the standard, safe assumption — cryptography calls it Kerckhoffs's principle. Settings make poor secrets: there are only about fifteen separator choices to sweep, versus 17,576 possibilities per word, and settings are reused across every password you make while each password's draws are fresh. If an attacker genuinely doesn't know your settings, every figure shown is an underestimate — the safe direction to be wrong in. The same rule prices custom text, literal affixes, and leet at zero: anything you typed or toggled is part of the settings, not part of the draw.
A fixed separator still earns its place — it makes the password readable and typeable — it just does not make it harder to guess. That is the same trade as Title Case: worth choosing, and honestly priced at zero.
The math: N bits means 2N equally likely possibilities. An attacker sweeping them finds the password, on average, halfway through — 2N−1 guesses — so the displayed time is simply 2N−1 ÷ rate, in seconds.
Worked through for a five-word passphrase at 70.5 bits: 269.5 ≈ 8.3 × 1020 average guesses. Against the GPU that is 8.3 × 1020 ÷ 1011 ≈ 8.3 × 109 seconds ≈ 264 years; against bcrypt, the same guesses at 104/second stretch to about 109 years; online at 10/second, about 1012. And for the weak line: 40 bits is 239 ≈ 5.5 × 1011 average guesses ÷ 1011/second ≈ 5 seconds on one rig — which is why 40 is where the meter starts calling a password fair.
Because the time is 2 raised to the bit count, every added bit doubles it, and ten added bits multiply it by about a thousand. That is why one extra word (14.1 bits, ×18,000) or one extra random character (6.26 bits, ×77) moves the guess time more than any tweak to capitalization or substitutions ever could.
|) and every draw is priced from the reduced pool.
The breakdown states the total cost and its remedy — at typical settings the cost is
around 2–3 bits, less than the ~6 bits one extra character returns.
The Vault
Somewhere for a password to go after you generate it — encrypted with a passphrase you choose, stored on this device, and belonging to no account.
A generator is a moment-tool: you arrive, take a password, and leave. With the clipboard timer on, the only copy is gone thirty seconds later. The vault is where that password can go instead — a list of entries, encrypted as a single sealed blob in this browser's storage, unlocked with a passphrase only you know.
There is no account and no server. Nothing is uploaded, nothing is synchronised, and nobody — including us — can open your vault or reset your passphrase. That is the whole point, and it is also the whole cost: see Backups, because it means the only copy is the one you keep.
Because it would barely help. An attacker's cost rises linearly with the iteration count, so the strength it buys rises only logarithmically. Going from 600,000 to 10,000,000 is 16.7× the work, which is log2(16.7) ≈ 4.1 bits — less than one extra random lowercase letter. It would cost about a second of waiting every time you unlock on a fast computer, and several on a phone.
Adding one more word to your passphrase beats it outright, and costs nothing. A word from the generator's list is worth 14.1 bits — more than three times what a sixteen-fold slowdown buys. That is the same arithmetic the rest of this page runs on: bits come from the number of random choices made, and stretching only raises the price of each guess.
CryptoKey — a handle the page can encrypt with but cannot read the bytes of.
Locking discards it. The passphrase itself is never stored anywhere, in any form.
Within that same window, walking from the generator to the vault does not ask for the passphrase again. Making that work costs something specific and worth knowing: the vault key is written to browser storage, wrapped under a second key the browser holds, so the next page can pick it up. For the length of the window, that is a slightly weaker position than "the key exists only in memory".
Setting Vault lock to Every page switches this off entirely: nothing is stored, the key is never even made exportable, and every page asks. The same setting governs both the idle timeout and this window, so there is one number to reason about rather than two.
Question and answer pairs, masked and copyable exactly like the password, because that is what they are: a second password with a friendlier name. They are covered by the clipboard timer too.
The answers need not be true. Your real mother's maiden name and the street you grew up on are matters of public record; an invented answer stored here is strictly stronger, and the only reason people use the true one is that they have nowhere to keep the invented one. Now you do.
Every secret field in the vault has a Generate split button beside it: the left half runs whichever of the seven generators is in use and says which (Generate Words), and the arrow beside it opens the list to change that. It runs that generator with its own current settings — whatever the Words tab is set to is what the Words option produces here. There is deliberately no second copy of the options: one place to configure a generator, and it is that generator's tab.
Picking from the list sets the generator rather than running once with it, so there is one generator in use and the password row always shows which. The control is on every secret box for a plain reason: choosing a generator used to mean scrolling up to the password field, changing it there, and scrolling back.
The entropy comes back with the password and is stored on the entry, so something generated here carries the same exact figure as something filed with Keep — not an estimate. The two paths run the same code.
Turn grouping off and the list becomes one flat run in pure sort order. This matters more than it sounds. With grouping on, Weakest first means weakest-within-each-group, so the worst password in the whole vault can sit halfway down the page under a heading.
A four-entry vault at 128, 56, 26 and 56 bits sorts grouped as 56, 128, 26, 56 and flat as 26, 56, 56, 128. The first is right for finding an entry you already know about; the second is right for auditing. No single default serves both, which is why it is a switch rather than a decision.
Newest first (the default, because the thing you just saved is the thing you most likely want back), Oldest first, Name, and Weakest first.
Weakest-first is the audit view: it orders by the stored entropy so the entries worth changing come to the top. Entries with no recorded figure sort to the bottom, not the top — an unknown number is not evidence of a weak password, and putting the unknowns first would bury the ones you can actually act on. Anything without a date sorts as oldest, because an entry predating the date field genuinely is.
Lose this vault and you are locked out. The seed lives only here. If this browser's data goes and there is no backup, the password is gone and so is the second factor — which is exactly the situation the second factor exists to survive. Keep an encrypted backup, and keep the provider's own recovery codes somewhere else entirely.
And it weakens two-factor authentication. A one-time code is a second factor only while it is kept apart from the first. Storing the seed in the same vault as the password collapses two factors into one: whoever opens the vault has both, and an attacker who gets in needs nothing else.
It is still a real gain against the common case — a password leaked from the site's own database, where the attacker never touches your device. And it is the same trade every password manager offering this feature makes. The difference is that here it is said before you paste the secret rather than discovered afterwards. If you would rather not make the trade, keep the codes in a separate app; that is the stronger arrangement and always was.
otpauth:// link or the base32 secret. Most sites put both
behind a "can't scan the QR code?" link. The full link is better: it carries the
digit count, interval and hash algorithm, so nothing has to be assumed. A secret
with an invalid character is rejected rather than accepted and quietly mangled —
six plausible digits that never work is a miserable thing to debug.
If two entries share a password, both say so — a badge on the entry naming how many others use it, a summary line above the list, and a filter to show only those. Editing an entry warns as you type, before the password is filed under a second name, and names the entries it collides with.
Reuse is the failure that turns one breach into several: the site that leaks your password is rarely the site you cared about, and it hands over the one you did. It is also the only piece of password health a vault like this can assess with certainty — no guessing, no corpus, no network. It is comparing what is already in front of it.
Password1 and Password2 are two passwords, and so are
Password1 and password1. There is deliberately no fuzzy matching:
a near-miss flagged as reuse is a guess presented as a finding, and this page does not do
that anywhere else either.
A vault living in one browser profile is one clear site data away from gone, and no account exists to recover it from. The vault page keeps a quiet line of text — not a modal — noting how many entries have been added since your last backup.
Under it is an expandable list of the last five backups: when each was made, how many entries the vault held at the time, and which browser made it. The count is what makes an old one legible — "3 entries" against today's forty says more than the date does. This record lives inside the vault, not in this browser, so every machine that opens it sees the same history; a backup made in Chrome is not reported as missing by Edge. Only encrypted backups are recorded, because a plain-text export is not a backup. It is a note that a file was made, not proof it still exists.
.json file containing the sealed vault verbatim: still encrypted, safe to keep
in cloud storage or on a drive, and restorable only with the passphrase it was made with — not
necessarily the one your current vault uses. This is the backup, and it is the default.
JSON or CSV with your passwords in the clear, behind a confirmation that says so, with
PLAINTEXT in the filename. This exists for one purpose — moving to a different
password manager — and the file should be deleted the moment that is done.
We would rather not offer it. It ships anyway, because refusing every exit but "another copy of WordLock" is lock-in, and an escape hatch you cannot use is not an escape hatch. CSV is additionally lossy: a row is flat, so only the first web address survives and security questions are folded into the note.
This one passphrase is the only key to everything in the vault, so the page shows a live strength reading as you type it — and labels it at most N bits rather than a score. That wording is deliberate. Everywhere else on this site the entropy figure is exact, because the generator made every random choice itself and can count them. It cannot know what went into a passphrase you typed. A real attacker's dictionary knows words, names, dates and keyboard patterns that a few hundred lines of arithmetic do not, so the honest reading is a ceiling: it can tell you a passphrase is bad, never that one is good.
The reliable way out of that uncertainty is to generate a passphrase and use it, at which point the number stops being a guess.
How two keys open one vault. The short version: your passwords are not encrypted with your passphrase. They are encrypted with a random key, and it is that key which gets locked up — once per way in.
When the vault is created the browser generates a master key: 256 random bits, never typed by anyone and not derived from anything you chose. That key encrypts your entries, and it is the only key that ever touches them. The master key is then encrypted — wrapped — under a key derived from your passphrase, and the wrapped copy is what gets stored.
Making a recovery key wraps the same master key a second time, under a key derived from the sixteen words. So the stored vault holds three things: your entries encrypted under the master key, and two locked copies of that master key. Unlocking is the same operation either way — derive a key from whichever secret you have, use it to unwrap the master key, use the master key to read the entries.
What that shape buys, and why it is worth the extra step:
- Neither key reveals the other. Opening the passphrase copy gives you the master key and nothing else. There is no path from one locked copy to the other, so holding one does not help you find the second.
- Changing the passphrase does not invalidate the recovery key, and costs 32 bytes of work rather than re-encrypting the whole vault. The master key never changed; only the lock around one copy of it did.
- Removing the recovery key is deleting one field. Your entries and the master key are untouched — the second copy simply stops existing.
- Recovering deliberately breaks that pattern. It generates a brand new master key and re-encrypts under it, rather than re-wrapping the old one. That is the point: the forgotten passphrase’s copy does not survive, so anyone who might have learned it is locked out along with your memory of it.
The plainest way to picture it: the master key is the key to your house. You put a copy in two lockboxes, one opening to your passphrase and one to the recovery words. Changing a lockbox combination does not change the house key, and losing one combination does not matter as long as you know the other. Lose both and the house stays shut — there is no locksmith, because there is no one holding a third copy.
Drawn here from the same 17,576-word list the Words generator uses: 14.1 bits a word, 225 bits in total. There is deliberately no way to supply your own phrase, and that restraint is the whole security argument rather than fussiness.
A vault with two keys is only as strong as the weaker one, because an attacker takes whichever is cheaper to break. A memorable recovery phrase would therefore become the real strength of your vault, silently, no matter how good the passphrase was. At 225 bits of generated randomness the recovery path is not attackable at all, so the passphrase stays the binding constraint and this costs nothing. Between those two positions is a trap: anything memorable is anything guessable.
Words rather than a string of base32, because this gets written on paper and typed back by hand, months later, by someone already having a bad day. Duplicates are allowed — each draw is independent, which is exactly what makes the bit count true; removing them would look tidier and quietly lower it.
By default the vault lives in this browser's storage and nowhere else. It can instead live as a single encrypted file in a folder you choose — and if that folder is one your computer already syncs, such as Dropbox, OneDrive or iCloud Drive, then your vault is carried between machines and backed up by software you already trust, with no account here and nothing of ours in the middle.
What the file contains is the same sealed ciphertext the browser was holding. Your cloud provider learns that a file changed and roughly how big it is. It never learns what is in it, because it cannot.
Saving reads what is in the folder first and merges rather than overwriting. Entries added on either machine survive, and a deletion made on one is not resurrected by the other still holding the entry. The same applies to two tabs of one browser, which is the same problem over a shorter distance.
If you both changed the same entry, it stops and asks. You get the fields that differ side by side and three choices: keep yours, keep theirs, or keep both as two entries. Nothing is written until you pick. It asks because it cannot know — the timestamps only say which was saved last, not which knew more, and an edit box open in one browser holds a copy from before the other browser saved.
What this does not cover. Two machines saving while the folder itself is out of sync — offline, or inside the seconds your cloud client takes to catch up — is resolved by Dropbox or OneDrive, not by us: they write a second conflicted copy file, which this page cannot see. Import that file and its entries merge back in.
Chromium on the desktop — Chrome, Edge, Brave, Opera. This needs the File System Access API to write back to a file you chose, and Firefox and Safari have not implemented it. That is unlikely to change: Mozilla's published position on the folder and file pickers is harmful. Reading a file you pick works everywhere, which is why Import works in every browser; it is writing back that has no equivalent. Elsewhere the vault stays in the browser and an encrypted backup is how it moves.
Not on phones, deliberately, even where the browser allows it. Chrome and Edge on Android both have the API, so this used to be offered there — and then asked to reconnect on every refresh — not each time you open the app, each time the page loads — and twice over, once in the page and once from Android itself. Its picker also lists local storage rather than the cloud locations your provider’s app shows, so there was nothing to sync with even after all that. A button that relocates the only copy of your vault should not lead somewhere that awkward, so it is not shown. Keep the vault in the browser on a phone, and move it with an encrypted backup.
navigator.storage.persist(), and shows you the answer. It asks again on
every visit, because the answer can change. If the browser has not promised, the vault page
says so rather than letting you find out by losing it.
Install the app. That is the reliable fix. Chrome and Edge never ask you about this — they decide, granting it once a site is installed, bookmarked, or used often enough, and reporting it as refused until then. So "denied" usually means "not yet", not "no". Use Install or Add to Home Screen in the browser menu and it is generally granted the next time you open the vault. Firefox does ask, and there the Ask again button is the request.
It is also worth knowing how unlikely eviction is. Browsers evict under genuine storage pressure, least-recently-used first, and a vault is tiny — the page shows you its size against the space available, which is typically a few dozen kilobytes against several gigabytes.
None of it replaces a backup. A promise not to evict is not a promise against a lost laptop, a reinstalled browser, or your own hand on Clear browsing data — which ignores the promise entirely. Keep an encrypted backup.
Appearance & Settings
The gear in the floating bottom bar, available on every page.
System follows your operating system's appearance setting and switches live if you change it while the page is open. Choosing Light or Dark overrides that and stays fixed.
Your choice is saved in localStorage and applies across every page. It is
applied before the page draws, so switching to dark never flashes the light theme first.
Ten accent themes, independent of Light and Dark — every one works in both. A theme changes the accent color, the focus ring and the page background. In dark mode it also tints the cards, so a violet theme is violet throughout rather than the same gray card with a violet button.
Mono is a true grayscale theme. It is the only one that also drops color from the status messages and the changelog categories, which then tell themselves apart by lightness instead of hue.
Saved in localStorage and applied before the page draws, like the others.
Marks themes whose accent stays clearly distinct from the success, warning and error colors for every kind of color blindness — protanopia, deuteranopia and tritanopia. Blue, Indigo, Violet, Slate and Mono qualify.
An unmarked theme is not a problem to use. Nothing here depends on color alone: every status message and every changelog category is labelled in words. The marker only says the color is doing useful work on top of the words.
It is measured rather than asserted. Each theme is simulated under all three deficiencies and the color distances recomputed on every build, so the marker cannot quietly go out of date. Two candidate themes were rejected by that process: an amber accent that was identical to the warning color, and an earlier, brighter red that made error messages stop standing out.
What the marker deliberately does not cover: the accent's distance from the changelog category colors and the docs badge colors. Those were measured too, and several accents sit close to — or exactly on — a family color, by design: the Sky accent is the sky badge color. That is reuse, not confusion. Status colors are signals that must be tellable apart from an interactive accent; categories and badges always carry their names in text, so hue never bears their meaning alone. A floor that failed a palette for matching its own badge would be a badly chosen floor.
r into a custom separator or prefix never fires it.
| Key | Action |
|---|---|
| Enter / Space | Open or close the panel |
| Arrow keys | Move between the options in a group |
| Home / End | Jump to the first or last option |
| Escape | Close the panel and return focus to the gear |
| Tab | Leave the panel, which closes it |
Built With
The tools and technologies used to create this app.
There is no bundler and no deploy pipeline. The browser loads src/main.js as a native ES module and the site is served
as plain static files, so what runs in your browser is exactly what is in the repository — readable, and checkable.
One thing is generated rather than hand-written. Component templates are compiled to render functions ahead of time, so the browser
never compiles them itself — which is what lets the security policy refuse unsafe-eval and the page ship a Vue build
with no compiler in it at all. Both halves are committed: the templates you would actually read, in src/templates/, and
the generated code that runs. A test recompiles the first and fails if it no longer produces the second, so the two cannot drift.
render.yaml configuration file.