A cryptographically secure passphrase generator built with the EFF Dice-Roll Method. Generate memorable, high-entropy passphrases entirely in your browser. No data ever leaves your device.
- Multi-language wordlists : English (3 EFF lists) and Brazilian Portuguese (2 lists), with a language selector to switch between them
- Cryptographic randomness : Uses
crypto.getRandomValues()with rejection sampling for unbiased dice rolls - Configurable : Adjust word count (3-10), separators, capitalization, numbers, symbols, and emojis
- Entropy statistics : Real-time entropy bits, keyspace, crack time, and strength classification
- Dark mode : System-aware theme with manual light/dark/system toggle
- Copy to clipboard : One-click copy with toast notification feedback
- Responsive : Mobile-first layout with passphrase output prioritized on small screens
- Zero server dependency : All generation happens client-side; no API calls, no telemetry
- Educational content : Entropy explanations, best practices, and EFF methodology overview
- Extensible : Community contributions of new language wordlists are welcome
The Electronic Frontier Foundation (EFF) developed the dice-roll passphrase method as a transparent, verifiable way to generate strong passwords without trusting opaque software.
How it works:
- A curated wordlist maps every possible dice combination to a unique word
- Roll physical dice (or use a cryptographic random number generator) to select words
- Each word from the 7,776-word list adds ~12.9 bits of entropy
- Six random words produce ~77.5 bits of entropy, resistant to brute-force attacks for decades
This generator digitally reproduces the physical dice-roll process. Instead of rolling real dice, it uses the browser's crypto.getRandomValues() API with rejection sampling to ensure each die face (1-6) has exactly equal probability, the digital equivalent of perfectly fair dice.
Wordlist options:
| Language | List | Words | Dice | Use case |
|---|---|---|---|---|
| English | Long List | 7,776 | 5 per word | Maximum entropy (~12.9 bits/word) |
| English | Short #1 | 1,296 | 4 per word | Shorter words, easier to type |
| English | Short #2 | 1,296 | 4 per word | Longer memorable words, easier to remember |
| Português (BR) | Lista Longa | 7,776 | 5 per word | Maximum entropy (~12.9 bits/word) |
| Português (BR) | Lista Curta | 1,296 | 4 per word | Memorable Portuguese words |
- Next.js 16 : App Router, React Server Components
- Tailwind CSS 4 : CSS-first configuration via
@theme - Framer Motion : Animations and transitions
- Lucide React : Icon library
- next-themes : Dark mode with SSR support
- Google Fonts : Space Grotesk (display), DM Sans (body), JetBrains Mono (passphrase output)
- Node.js 18.17 or later
- npm (included with Node.js)
# Clone the repository
git clone https://github.com/lauroguedes/passphrase-gen.git
cd passphrase-gen
# Install dependencies
npm installnpm run devOpen http://localhost:3000 in your browser.
npm run build
npm startnpm run lintsrc/
├── app/
│ ├── globals.css # Tailwind v4 @theme config, dark mode, component classes
│ ├── layout.tsx # Root layout, fonts, ThemeProvider
│ └── page.tsx # Page assembly: hero, generator, educational sections
├── components/
│ ├── HeroSection.tsx # Animated hero with title, badge, dice icon, GitHub + theme toggle
│ ├── EntropyStats.tsx # Real-time entropy statistics display
│ ├── Providers.tsx # Client-side ThemeProvider wrapper
│ ├── generator/
│ │ ├── PassphraseSection.tsx # State wrapper (usePassphrase + EntropyStats)
│ │ ├── PassphraseGenerator.tsx # Main orchestrator (two-panel layout)
│ │ ├── PassphraseDisplay.tsx # Animated word-by-word passphrase display
│ │ ├── LanguageSelector.tsx # Language dropdown (flag + name)
│ │ ├── WordlistSelector.tsx # Wordlist picker (filtered by language)
│ │ ├── WordCountStepper.tsx # Word count -/+ stepper (3-10)
│ │ ├── SeparatorSelector.tsx # Separator character grid
│ │ ├── OptionSwitches.tsx # Capitalize, Numbers, Symbols, Emojis toggles
│ │ ├── GenerateButton.tsx # Roll Passphrase button with dice animation
│ │ └── CopyButton.tsx # Copy to clipboard with feedback
│ ├── educational/
│ │ ├── WhyRandomPassphrases.tsx # Entropy explanation + table
│ │ ├── BestPractices.tsx # Security tips grid
│ │ └── EffResearchSummary.tsx # EFF methodology overview
│ ├── illustrations/
│ │ ├── ShieldIcon.tsx # Custom SVG shield illustration
│ │ ├── DiceCluster.tsx # Custom SVG dice cluster
│ │ └── EntropyWave.tsx # Custom SVG wave pattern
│ └── ui/
│ ├── GlassCard.tsx # Glassmorphic card wrapper
│ ├── ThemeToggle.tsx # Light/dark/system theme switcher
│ ├── Tooltip.tsx # Hover tooltip
│ ├── Toast.tsx # Copy confirmation toast
│ └── SectionHeading.tsx # Section heading with gradient accent
├── hooks/
│ ├── usePassphrase.ts # Central state: config, result, generate action
│ └── useClipboard.ts # Clipboard API wrapper with auto-reset
├── lib/
│ ├── constants.ts # Wordlists, separators, symbols, emojis, defaults
│ ├── entropy.ts # Entropy computation with option bonuses
│ ├── generate.ts # Dice rolling with rejection sampling
│ └── wordlist.ts # Fetch, parse, and cache EFF wordlists
├── types/
│ └── index.ts # TypeScript type definitions
public/
└── wordlists/
├── en/ # English EFF wordlists
│ ├── eff_large_wordlist.txt
│ ├── eff_short_wordlist_1.txt
│ └── eff_short_wordlist_2.txt
└── pt-br/ # Brazilian Portuguese wordlists
├── diceware_pt_br_large.txt
└── diceware_pt_br_short.txt
Contributions are welcome! Here's how to get started:
- Fork the repository
- Create a branch for your feature or fix:
git checkout -b feature/your-feature-name
- Make your changes and ensure the build passes:
npm run build npm run lint
- Commit with a clear, descriptive message
- Push to your fork and open a Pull Request
- Follow the existing code style and patterns
- Use Tailwind CSS utility classes (with
dark:variants for new UI elements) - Keep components small and focused
- All randomness must use
crypto.getRandomValues(), neverMath.random() - Test both light and dark mode for any UI changes
- Ensure mobile responsiveness
Community contributions of wordlists in other languages are welcome! Follow these steps:
Create a TSV (tab-separated values) file where each line follows this format:
<dice-index>\t<word>
- For a 5-dice wordlist: indices run from
11111to66666(7,776 words) - For a 4-dice wordlist: indices run from
1111to6666(1,296 words) - Words should be common, easy to spell, and distinct from each other
- File encoding must be UTF-8
Add your wordlist to public/wordlists/<language-code>/:
public/wordlists/pt/diceware_pt_large.txt
Use ISO 639-1 two-letter language codes (e.g., pt for Portuguese, es for Spanish, de for German).
In src/lib/constants.ts:
- Add your language to the
LANGUAGESarray:
{ value: "es", label: "Español", flag: "🇪🇸" }- Add your wordlist(s) to the
WORDLISTSarray:
{
value: "es-large",
label: "Lista Larga",
icon: "L",
tooltip: "7,776 Spanish words, 5 dice per word.",
url: "/wordlists/es/diceware_es_large.txt",
diceCount: 5,
language: "es",
wordCount: 7776,
}- Add a default mapping in
getDefaultWordlistForLanguage():
"es": "es-large",- Add the language code to the
Languagetype insrc/types/index.ts.
- Ensure
npm run buildandnpm run lintpass - Test that the new wordlist loads correctly and generates passphrases
- Include the source or attribution for the wordlist in your PR description
This project is licensed under the MIT License.
Created by Lauro Guedes.
Inspired by Glenn Rempe's Diceware and Arnold G. Reinhold's Diceware.
If you find this project useful, please consider giving it a ⭐.