- JavaScript 95.3%
- CSS 4.2%
- HTML 0.5%
| .opencode | ||
| data | ||
| openspec | ||
| src | ||
| test | ||
| vendor | ||
| .gitignore | ||
| index.html | ||
| package.json | ||
| README.md | ||
| style.css | ||
Bad Wordle
A single-player Wordle-loop browser game with an offensive vocabulary. No server, no accounts, no build step — plain HTML + vendored Phaser 4.
Content warning: the answer words are raunchy. The game shows a content warning gate on first visit (remembered in
localStorage).
Look & feel
The game is styled "Neon Glass Noir": a near-black indigo base with glass-panel tiles and keys, neon magenta/violet/teal accents, drifting ambient glow blobs, and a slow color-cycling title. All SFX are synthesized at runtime with the Web Audio API (no audio files).
- Fonts: two OFL faces are vendored in
vendor/fonts/— Unbounded (display) and Space Grotesk (text) — so the look works fully offline. - Mute: a speaker button in the top-right toggles sound, persisted in
localStorage(bad-wordle:muted); the game starts muted if that flag is set. - Crisp on HiDPI: the canvas backs at up to 2× device pixel ratio; text and panels are baked at that scale.
- Canvas fallback: append
?canvasto force the Canvas renderer (used automatically when WebGL is unavailable); WebGL-only ambient garnish is skipped.
Run it
Any static file server works. From the repo root:
python3 -m http.server 8888
# then open http://127.0.0.1:8888/
The app must be served over HTTP (not file://) because it loads ES modules.
How to play
- Guess the 5-letter answer in 6 tries.
- Letters are validated against the standard American-English 5-letter dictionary or the curated Urban Dictionary 5-letter word list — a guess that isn't in either list is rejected.
- Green = right letter, right place. Yellow = right letter, wrong place. Gray = not in the word.
- On-screen keyboard or physical keyboard both work.
- After a win or loss, an overlay shows the result. If the word has a bundled Urban Dictionary definition (every word from the bundled urban list) it also shows that definition (with credit). Words without a bundled definition show no definition line.
- Click NEW ROUND for a fresh answer (never the same word twice in a row).
Word lists
Three bundled lists in data/:
dictionary-words.json— plain JSON array of ~16k standard American-English 5-letter words. The first "valid word" source (see below).urban-words.json—{ notes, words, definitions }object: a curated 5-letter Urban Dictionary word list (hilarious, raunchy, or offensive in character). The second "valid word" source;definitionsmaps each word to its bundled Urban Dictionary definition. It is a superset of the answer pool — every word inanswer-words.jsonappears here with the same definition (the test suite asserts this).answer-words.json—{ notes, words, definitions }object with the curated raunchy 5-letter answer pool.definitionsmaps each answer word to a bundled Urban Dictionary definition shown on the win/lose overlay. Thenotesfield documents curation rules;wordlists.jsaccepts this shape, the urban shape, or a plain array.
Valid word rule
A word (for in-round guesses) is valid if and only if it is exactly 5 letters and present in either the standard American-English dictionary (dictionary-words.json) or the curated Urban Dictionary list (urban-words.json). The union is computed once at load (loadWordData → validWords).
Curation rules (answer pool and urban list)
- Exactly 5 lowercase ASCII letters, no duplicates.
- Hilarious, raunchy, or offensive in character — no slurs or hate speech, no proper nouns or personal names.
- Every word needs a short plain-language definition in the
definitionsmap (Urban Dictionary style). - The list must stay a superset of
answer-words.jsonwith matching definitions. - To add words, append to the
wordsarray and a matchingdefinitionsentry, then runnpm test— the test suite asserts the invariants (5 letters, lowercase, unique, defined, superset) for the word lists.
Curation rules (dictionary)
- Exactly 5 lowercase ASCII letters, no duplicates.
- Extend by appending to the array;
npm testvalidates.
Tests
npm test
Runs the node-based unit tests (no framework): rules engine (round state machine, feedback with duplicate-letter handling, guess validation), answer selection, and word-list invariants.
Project layout
index.html entry page
style.css page styling
src/app.js Phaser game bootstrap (HiDPI config, mute, exposes window.__game)
src/scene.js WarningScene + GameScene (board, keyboard, overlays, control bar)
src/theme.js palette, fonts, easing/duration constants (single source of truth for the look)
src/textures.js boot-time texture bakes (glass, glow, confetti shard, vignette) + HiDPI scale
src/audio.js Web Audio SFX synthesis + mute state (no audio files)
src/rules.js round state machine, feedback, validation (pure, no DOM)
src/wordlists.js loads word data (answer pool, urban + dictionary lists, bundled definitions)
data/ word lists
vendor/ vendored phaser.min.js (Phaser 4.2.1, MIT) + fonts/ (OFL WOFF2) + LICENSE-NOTES
test/ node --test unit tests
Debug hooks
window.__game— the Phaser game instance (scenes, input).window.__badWordle— round state, current guess, feedback,setAnswer(word), pool size. Used for deterministic E2E assertions.