About this Wordle solver

A small, dependency-free filtering tool for five-letter word-guessing games. It doesn’t know any game’s daily answer — it just applies the green/yellow/grey rules correctly, including the part most home-made solvers get wrong: duplicate letters.

Why it exists

The duplicate-letter case is the single most common bug in quick word-solver scripts. A naive implementation treats grey as “this letter never appears,” full stop — which is correct until a guess repeats a letter. Guess a word with two of the same letter, get one green or yellow and one grey back, and a naive filter throws away every remaining word containing that letter at all, including the correct answer. This tool tracks a count per letter instead of a plain presence flag, so a grey tile only caps how many of that letter can appear, rather than banning it outright.

How the constraint logic is implemented

Every guess row is read as a set of five { letter, state } tiles. For each row, the letters marked green or yellow are counted per letter — that count becomes a known minimum for how many of that letter the answer contains. If any tile for that same letter in the same row came back grey, that row's non-grey count becomes a known maximum too — the answer has exactly that many, no more. Minimums and maximums are combined across every row you've entered, and green tiles additionally fix the exact letter at that position.

for each row: nonGrey[letter] += 1 for every green or yellow tile of that letter if any tile of that letter in this row is grey: cap[letter] = min(cap[letter], nonGrey[letter] in this row) minimum[letter] = max(minimum[letter], nonGrey[letter] in this row) a candidate word survives if, for every position and every letter: green positions match exactly yellow letters are present, but not at their yellow position each letter's count in the word is >= its minimum and <= its cap (if any)

A word only makes it into the results if it satisfies every row's constraints simultaneously — adding a row can only shrink the list, never grow it.

How it was tested

The filtering function has no dependency on the page at all — it's a plain function that takes a list of guess rows and a word list and returns the matches, so it runs the same way under Node.js as it does in the browser. Before shipping, it was checked against small hand-built word lists for the cases that are easy to get wrong:

  • A green letter at a given position keeps only words with that exact letter there.
  • A yellow letter at a given position keeps only words that contain the letter somewhere else, never at the yellow position itself.
  • A plain grey letter (no duplicate in the guess) removes every word containing it.
  • The duplicate-letter case: a green copy of a letter plus a grey copy of the same letter, from the same guess, keeps words with exactly one of that letter and correctly drops words with two.
  • Two green copies of the same letter (no grey) requires at least two, without capping higher counts.
  • A duplicate-letter cap established in one row survives being combined with an unrelated later row, rather than being reset.

The same function was then run against the full ~8,500-word list with a multi-clue example and checked that every surviving word actually satisfied each individual constraint by direct inspection, not just by trusting the filter's own output.

The suggested next guess

The suggestion is a frequency heuristic, and it's worth being precise about what that means. Among the words still matching your clues, it counts how often each letter shows up at each position, and how often each letter shows up anywhere, then scores every remaining candidate by summing those frequencies for its own letters (counting each distinct letter once for the “anywhere” score, so a word with a repeated letter doesn't get double credit for it). The highest-scoring candidate is suggested.

That is a fast, reasonable proxy for “a guess built from common, well-distributed letters,” recomputed instantly on every keystroke over thousands of candidates. It is not a full information-theoretic solver that simulates every possible colour pattern for every possible guess and picks the one with the highest expected information gain — that kind of search is the gold standard for optimal play, but it's too slow to redo on every tile click in a browser without a server. The heuristic here is a deliberate, disclosed trade of a small amount of optimality for something that stays instant.

How the site is built

  • Static HTML, one stylesheet, one script. No framework, no build step, no bundler.
  • The word list ships with the site. It's derived locally from a public-domain English word list, filtered to five-letter lowercase words, with no proper nouns and no network fetch at runtime.
  • Nothing you type is transmitted. The filtering runs entirely in your browser. There's no account and no save feature — refreshing the page clears your guesses.
  • Keyboard operable. Every tile can be filled and cycled through its colours without a mouse.
  • Dark mode follows your system setting via prefers-color-scheme.

Scope and honesty about limits

This tool has no connection to any specific word game and no knowledge of any specific day's answer — it is a general constraint filter over a general dictionary. Two consequences follow directly: it can list a valid English word as a candidate that a given game's own curated answer list would never actually use, and it cannot tell you which candidate, if any, is the real answer today. What it verifies is narrower and more honest: which words are logically consistent with the clues you typed in, given the standard rules for how green, yellow and grey are assigned.

The suggested next guess is a heuristic, not a proof of optimality, as described above. And like any tool that trusts its input, a mis-clicked tile colour produces a wrong-but-consistent result — the logic has no way to know a clue was entered incorrectly.