WordWiz
A SvelteKit word finder and unscrambler: type a bag of letters and it returns every word that can be made from them, in English or German, filtered to a chosen length range. Backed by 450k+ and 670k+ word lists indexed for fast frequency-map lookups.



WordWiz is a letter-bag word finder and unscrambler, not a Wordle-style position solver: you type in a set of letters and it finds every dictionary word that can be built from them — useful for Scrabble-style games, anagram puzzles, or just figuring out what a jumble of tiles spells. The word data is two flat wordlists — English (455,528 words) and German (672,043 words) — loaded server-side at startup into a LetterMap index with three lookup structures: a wordMap (each word to its own letter-frequency map), a lengthMap (words bucketed by length), and a letterMap. There are also trimmed `_short` variants of each list for faster responses when exhaustive coverage isn't needed. Two SvelteKit API routes expose this index. `/api/findwords/[lang]/[letters]` does a "contains at least these letters" search: it builds a frequency map of the input letters and returns every word whose own letter-frequency map is satisfied by it (so extra unused letters in the bag are fine — this is the "what words can I make" mode). `/api/findwords/[lang]/[letters]/unscramble` instead does strict anagram matching — `exact: true` with `minLength`/`maxLength` both pinned to the input length — so only words using every single letter, no more and no fewer, come back. On the frontend, an EN/DE toggle switches which wordlist is queried, a comma-separated letter input feeds both endpoints, and a word-length range slider filters the contains-mode results client-side after they come back, so you can narrow a big letter bag down to, say, 5-7 letter words without re-querying the server.
Architecture
graph TD UI["SvelteKit page
letter input"] -->|fetch /api/findwords/lang/letters| API["+server.js route"] API --> Trie["LetterMap
(wordMap, lengthMap, letterMap)"] Trie -->|frequency-map comparison| Filter["findWordsWithLetters()"] Filter --> Candidates["candidate word list"] Candidates --> UI
Under the hood
Word lookup runs server-side against an in-memory index built at startup: words are bucketed by length and by which letters they contain, then candidates are filtered by comparing letter-frequency maps against the input.
findWordsWithLetters(letters, { exact = false, minLength = 0, maxLength = Infinity } = {}) {
const letterFreq = this.createFrequencyMap(letters);
const candidateWords = new Set();
const wordLengths = exact ?
[letters.length] :
Array.from(this.lengthMap.keys()).filter(len =>
len >= Math.max(minLength, letters.length) &&
len <= maxLength
);
for (const length of wordLengths) {
const wordsOfLength = this.lengthMap.get(length) || new Set();
for (const word of wordsOfLength) {
const wordFreq = this.wordMap.get(word);
// ... compare wordFreq against letterFreq ...
}
}
}