How the site works — architecture, the 13 supported UI languages, the 15 word lists, settings, achievements, and the developer API.
How It Works
The site is a pure client-side web app — no server, no data sent anywhere. Everything runs in the browser.
Key files
File
Purpose
index.html
Single-page UI
js/main.min.js
Core logic — CorrectHorseBatteryStaple class, exposed as window.CHBS
js/achievements.js
Achievement system — CHBSAchievements. Also exposes badge metadata (CHBSAchievements.badges) in all 13 languages, which this wiki renders live
js/lang-detect.js
Runs before anything else; picks which starter-data script to preload (only Dutch, English, French and German have one) based on the browser's language
js/starter.XX.js
Inline starter word data for zero-latency first render — only exists for nl, en, fr and de; the other 11 word lists load via AJAX instead
js/i18n.XX.js
UI translation strings — one file per supported language, loaded on demand
data/*.txt
Full word lists (15 files), loaded on demand via AJAX
css/styles.min.css
Main styles
css/achievements.css
Achievement toast & panel styles
Load sequence
Page loads; an inline script resolves the UI language (URL lang param → saved CHBS_lang → browser language, defaulting to English), preloads its js/i18n.<lang>.min.js, and hides the page until that language is confirmed applied — avoiding a flash of the raw Dutch fallback text baked into the HTML. js/lang-detect.js separately picks a starter-data script (Dutch, English, French or German only) and inserts it before main.min.js runs.
main.min.js initialises window.CHBS, which knows about all 13 UI languages via CHBS_SUPPORTED_LANGS.
Saved options are read from localStorage (or URL params).
The active UI language is resolved again and applied via applyLanguage(); only once its callback confirms the UI text is translated does init() correct the wordlist checkboxes and reveal the page — this ordering prevents a flash of the wrong language or wordlist before things settle.
If the active word list is Dutch, English, French or German, its starter data lets a password appear instantly; the other 11 word lists wait for their first AJAX fetch before the first password appears.
The full word list for the active dictionary loads in the background and silently replaces the starter data.
achievements.js wraps CHBS.trackGenerate, trackCopy, and trackShare to intercept events and check badge conditions.
Randomness: all random selection uses window.crypto.getRandomValues with a Uint32Array and rejection sampling to avoid modulo bias. Math.random() is never used.
Dictionaries & Word Lists
There are 15 word lists in total: 13 language lists, plus the Heroes and HoHoHo novelty lists. Exactly one is active at a time (or none, in Random mode).
Key
File
Words
Description
primary
dutch.txt
62,123
Dutch words — active by default
english
english.txt
20,000
English words
french
french.txt
20,384
French words
german
german.txt
20,000
German words
spanish
spanish.txt
20,000
Spanish words
italian
italian.txt
20,000
Italian words
portuguese
portuguese.txt
20,000
Portuguese words
finnish
finnish.txt
20,000
Finnish words
turkish
turkish.txt
20,000
Turkish words
greek
greek.txt
20,000
Greek words
russian
russian.txt
20,000
Russian words
esperanto
esperanto.txt
20,000
Esperanto words
japanese
japanese.txt
13,479
Japanese words
heroes
heroes.txt
219
Superhero / character names (secret — see Easter Eggs)
hohoho
hohoho.txt
368
Christmas-themed words (secret — see Easter Eggs)
random
—
—
Fully random character mode — no word list used
Word counts: these reflect the current data/*.txt files (each a single comma-separated line) and will drift slightly whenever a list is edited.
Selection rules (passphrase mode)
English active → all words come exclusively from the English list.
Heroes active → at least 2 words are forced from the Heroes list; each further word then has a 60% chance of coming from Heroes and a 40% chance of coming from English. The small 219-word Heroes list is mixed with English so passwords aren't trivially guessable.
HoHoHo active → at least 2 words are forced from the HoHoHo list; each further word then has a 60% chance of coming from HoHoHo and a 40% chance of coming from Dutch (primary), for the same reason.
Any other single list (French, German, Spanish, Italian, Portuguese, Finnish, Turkish, Greek, Russian, Esperanto, Japanese) → all words come exclusively from that list.
Dutch (primary) → the default when no other list is selected.
Only one list can be active at a time (radio behaviour). Switching deactivates all others.
Password Generation
Passphrase mode
Random words are drawn from the active dictionary (or mixed per the Heroes/HoHoHo rules above).
Words are capitalised if the "Uppercase" option is on.
Words are joined with the chosen separator character(s). When multiple separators are provided, one is chosen at random per gap.
If "Append number" is on, a random digit (0–9) is added after the last separator.
If the result is shorter than "Minimum length", one extra word is added and the process repeats until the target is reached.
Random mode
Generates a string of the configured length from a combined charset of uppercase, lowercase, digits, and the configured special characters. Duplicate characters are removed from the charset before sampling.
Strength scoring
Mode
How score is calculated
Random
Primarily by length (1–5). Loses 1 point if fewer than 3 character types are present and score > 2.
Passphrase
By word count (1–5). Gains +1 bonus point if total length ≥ 40 characters.
Settings & Storage
Settings are saved under the localStorage key CHBSOptions as JSON, and can also be shared via URL query params.
Option key
Type
Default
Description
minWords
number
6
Minimum number of words
minLength
number
10
Minimum total character length
separator
string
-
Separator character(s) between words
firstUpper
boolean
true
Capitalise the first letter of each word
appendNumbers
boolean
true
Append a random digit at the end
randomLength
number
36
Length in random mode
randomChars
string
!@#$%^&*()_+-=[]{}|;:,.?
Special characters for random mode
dict-<name>
boolean
false
Marks a word list as active (see Dictionaries above) — e.g. dict-french, dict-heroes, dict-random. At most one is ever true; none being true means Dutch/primary is active.
saveOptions
boolean
false
Whether to persist options to localStorage
Internationalisation
13 UI languages are supported. Only one language's translation file is kept in memory at a time — switching languages loads the new one and unloads the previous one.
Code
Language
nl
Nederlands (Dutch)
en
English
fr
Français (French)
de
Deutsch (German)
es
Español (Spanish)
it
Italiano (Italian)
pt
Português (Portuguese)
fi
Suomi (Finnish)
tr
Türkçe (Turkish)
el
Ελληνικά (Greek)
ru
Русский (Russian)
eo
Esperanto
ja
日本語 (Japanese)
Language resolution order
lang URL query parameter.
CHBS_lang in localStorage.
Browser's navigator.languages list — first match wins.
Falls back to Dutch.
Auto dictionary selection
On first visit (no saved options, no URL dict param), the detected browser language pre-selects the matching word list:
Browser language
Auto-selected word list
en
english
fr
french
de
german
es
spanish
it
italian
pt
portuguese
fi
finnish
tr
turkish
el
greek
ru
russian
eo
esperanto
ja
japanese
nl / other
primary(Dutch, the default)
Stats & Sessions
Stats are persisted under the localStorage key CHBS_stats. A new session is counted once per tab (tracked via sessionStorage). Achievement badges are stored separately under CHBS_badges.
Passwords generated per language — one counter per word list, tracked for all 13 languages
heroesGenerated
Passwords generated with the Heroes list
hohohoGenerated
Passwords generated with the HoHoHo list
randomGenerated
Passwords generated in random mode
longGenerated
Passwords with 20+ characters
veryStrongGenerated
Passwords with strength score 5
veryWeakGenerated
Passwords with strength score 1
settingsChanged
Settings changed between generates
statsOpened
Times the stats panel was opened
quickdrawCount
Times a password was copied within 2 s of generating
longWordGenerated
Times a password with 8+ words was generated
Easter Eggs
There are two hidden word lists, each unlocked by clicking a language label a specific number of times in quick succession (about 600ms between clicks).
🎅 HoHoHo (Christmas)
Triple-click the Dutch (NL) label (not the checkbox itself) while Dutch is the active word list. On the third click, the label changes to "🎅 HoHoHo" and the hohoho dictionary is activated. The Santa's Dirty Secret achievement unlocks at this point.
Click the label again while the Easter egg is active to reset it back to the Dutch list.
🦸 Secret Lair (Heroes)
Click the English (EN) label five times while English is the active word list. On the fifth click, the label changes to "🦸 Heroes" and the heroes dictionary is activated. The Secret Lair achievement unlocks at this point.
Click the label again while the Easter egg is active to reset it back to the English list.
Achievements
Achievements unlock automatically and appear as a toast notification. All unlocked badges are visible in the Stats panel. A handful — the three flagged as hidden (Hacker Man, The Matrix, Root Access) plus the two Easter-egg badges above — don't reveal themselves until earned.
Badge names and conditions below are pulled live from CHBSAchievements.badges and shown in the language you're currently reading this page in.
Generation milestones
Badge
Condition
ID
Copy milestones
Badge
Condition
ID
Sharing
Badge
Condition
ID
Languages & word lists
Badge
Condition
ID
Security & quality
Badge
Condition
ID
Speed
Badge
Condition
ID
Behaviour
Badge
Condition
ID
Streaks & longevity
Badge
Condition
ID
Time of day
Badge
Condition
ID
Calendar dates
Badge
Condition
ID
Password length novelties
Badge
Condition
ID
Secret badges
These are hidden until earned and do not appear in the stats panel count beforehand.
Badge
Condition
ID
Console API (Dev / Cheat)
Open the browser console and type CHBSAchievements.help() to see the full cheat sheet.
Command
Description
CHBSAchievements.help()
Show the cheat sheet in the console
CHBSAchievements.list()
List all badge ids — appends ✓ next to already-unlocked ones
CHBSAchievements.missing()
List only the badges you haven't unlocked yet, with their IDs
CHBSAchievements.unlock('id')
Unlock a specific badge by ID (IDs listed in the achievement tables above)
CHBSAchievements.unlockAll()
Unlock every badge
CHBSAchievements.revoke('id')
Remove a specific badge (revoking hacker-man grants The Matrix)
CHBSAchievements.maxStats()
Set all stats to their maximum badge-triggering values
CHBSAchievements.setStats({key: val})
Override specific stats, e.g. {totalGenerated: 1000}
CHBSAchievements.fakeTime(h, m)
Test time-of-day badges, e.g. fakeTime(13, 37)
CHBSAchievements.fakeDate(month0, day)
Test calendar badges, e.g. fakeDate(11, 25) for Christmas
CHBSAchievements.unlockHohoho()
Unlock Santa's Dirty Secret directly
CHBSAchievements.unlockHeroesSecret()
Unlock Secret Lair directly
CHBSAchievements.check()
Re-run all badge checks against current state
CHBSAchievements.badges
Read-only array of every badge's id/icon/name/desc/secret flag, in all 13 languages — this is what powers the tables above
Note:fakeDate uses 0-indexed months (January = 0, December = 11).
Any use of unlock, unlockAll, maxStats, setStats, fakeTime, or fakeDate automatically grants the Hacker Man secret badge.