Generate word search puzzles in pure PHP. Hides words in an N×N letter grid across all eight directions and returns the solution coordinates, so you can render the puzzle and the answer key.
- Zero dependencies — plain PHP 8.2+, no framework required
- Deterministic — pass a seed and get the same puzzle every time (great for shareable/permalink puzzles and tests)
- Hidden message mode — the leftover cells spell out a secret message in reading order
- Quality-aware search — backtracking placement with restarts guarantees a share of diagonal words instead of boring row-stacked grids
See it in action: the Emoji (in Dutch) word search maker at emojioneel.nl/woordzoeker.
composer require tinusg/word-searchuse TinusG\WordSearch\WordSearchGrid;
$puzzle = new WordSearchGrid(
size: 13,
words: ['elephant', 'giraffe', 'penguin', 'crocodile', 'butterfly', /* … */],
);
$puzzle->grid(); // 13×13 array of single uppercase letters
$puzzle->placedWords(); // [['word' => 'ELEPHANT', 'cells' => [['row' => 2, 'col' => 5], …]], …]
$puzzle->wordList(); // sorted list of the words that made it into the gridWords are normalized automatically: uppercased, deduplicated, and skipped when they contain non-A–Z characters, are shorter than 3 letters, or don't fit the grid. Supply more words than the target count and the generator picks a good mix.
$puzzle = new WordSearchGrid(13, $words, seed: 20260717);Same seed + same input = same puzzle. Handy for "puzzle of the day" pages and snapshot tests.
Hidden message
Fill the unused cells with a secret message instead of random letters. Solvers find it by crossing out all the listed words and reading the leftovers:
$puzzle = new WordSearchGrid(13, $words, seed: 7, message: 'Happy birthday!');
$puzzle->encodedMessage(); // 'HAPPYBIRTHDAY' — letters only, as embeddedThe generator runs an exact-cover style search so the placed words cover every cell except exactly the ones needed for the message. Throws InvalidArgumentException when the message is longer than the grid, and RuntimeException when no exact cover can be found for the given word pool — supply more (and more varied) words if that happens.
Any size works. Sizes 11, 13, and 15 have tuned target word counts (10, 13, and 16 words); other sizes default to one word per row:
WordSearchGrid::targetWordCount(13); // 13composer testMIT. See LICENSE.
Special thanks to Puzzelpedia.