A Wordle solver needs to answer one question: given everything I know so far, which 5-letter words are still possible?
In a running game, you accumulate clues across multiple guesses:
- Green 🟩 — correct letter, correct position
- Yellow 🟨 — correct letter, wrong position
- Gray ⬜ — letter not in the word
After 2-3 guesses, you might have constraints like:
Position 1 = S (green), R is somewhere but not position 2, A and E are excluded entirely.
That's a lot of state to track. And it has to update instantly as you type.
The Approach
Dictionary Preprocessing
Everything starts with the dictionary. SOWPODS (Scrabble International word list) contains ~267K words. I filtered it down to 12,470 five-letter words — the Wordle candidate pool.
This was a one-time preprocessing step. The list lives in memory and gets filtered on every solver query.
The Filtering Engine
The core logic is a pipeline of constraint checks:
function solveWordle(rows, excludedLetters) {
return FIVE_LETTER_WORDS.filter(word => {
for (const row of rows) {
for (let pos = 0; pos < 5; pos++) {
const letter = row.letters[pos];
const color = row.colors[pos];
if (color === 'green' && word[pos] !== letter) return false;
if (color === 'yellow' && (
word[pos] === letter ||
!word.includes(letter)
)) return false;
if (color === 'gray' && word.includes(letter)) return false;
}
}
for (const ch of excludedLetters) {
if (word.includes(ch)) return false;
}
return true;
});
}
The simplicity is deliberate. Each constraint is an independent filter — short-circuit evaluation makes it fast. On average, this runs in under 2ms for the full 12K word list.
Multi-Row State Management
The tricky part is managing state across 5 rows:
const grid = Array(5).fill(null).map(() =>
Array(5).fill(null).map(() => ({
letter: '',
color: 'empty'
}))
);
const COLOR_CYCLE = ['empty', 'green', 'yellow', 'gray'];
function cycleColor(current) {
const idx = COLOR_CYCLE.indexOf(current);
return COLOR_CYCLE[(idx + 1) % COLOR_CYCLE.length];
}
Performance
Here's the performance profile for a typical 3-guess scenario:
- List initialization: ~0.3ms
- Per-constraint filter: ~0.5ms per row
- Total solve time: < 5ms
Building the UI
The UI went through three iterations:
v1 — Server-side: Every input triggered a server round-trip. Slow and wasteful.
v2 — React-style: Too much overhead for what's essentially a filtering tool.
v3 — Vanilla JS (final): Direct DOM manipulation. No framework, no dependencies, no bloat.
CSS handles the visual feedback:
.wordle-tile.green { background: #6aaa64; color: white; }
.wordle-tile.yellow { background: #c9b458; color: white; }
.wordle-tile.gray { background: #787c7e; color: white; }
What I Learned
Simplicity wins — The first version tried to be clever with bitmasks and precomputed indexes. The second version just iterates over 12K words. 10x simpler and just as fast.
Client-side > server-side — For a problem this small (12K items, simple string operations), there's zero reason to involve a server. It's faster, cheaper, and works offline.
CSS transitions > JavaScript animations — Tile color changes feel smooth because they're hardware-accelerated CSS transforms, not JS timer loops.
Try It
👉 (https://wordhelper.me/wordle-solver)
No signup, no ads, no tracking. Just type your clues and get answers.
This is part of a larger collection of word game tools I'm building at WordHelper.me — including a Scrabble word checker, anagram solver, crossword helper, and more. All tools are free, client-side, and built with vanilla JavaScript.
Top comments (0)