Word Guess Game (Wordle-Style) — Free JS Snippet

Word Guess Game (Wordle-Style) · Forms · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Two-pass scoreGuess() algorithm with a shared consumed array for correct duplicate-letter handling
Rank-based updateKeyStatus() ensures a key never downgrades from green to yellow/gray across guesses
6x5 grid built dynamically by buildGrid(), with per-row and per-cell IDs for direct DOM targeting
Staggered 120ms-per-tile flip reveal via setTimeout for a left-to-right cascading animation
Dual input paths (physical keydown listener + on-screen QWERTY keyboard) both routed through one handleKey() function
Small hardcoded 20-word list (WORD_LIST) with random selection via Math.floor(Math.random() * list.length)
Win detection on exact match; loss reveal of the target word after 6 failed rows via gameOver flag
Hidden focused input keeps mobile virtual keyboards available while the visible grid handles rendering

About this UI Snippet

Word Guess Game (Wordle-Style) — Duplicate-Letter Scoring, Two-Pass Algorithm & Tile Flip Animation

Screenshot of the Word Guess Game (Wordle-Style) snippet rendered live

Building a Wordle clone is a popular exercise, but most naive implementations get one specific detail wrong: scoring a guess that contains a repeated letter. This snippet implements the real Wordle algorithm correctly — a two-pass, position-then-frequency scoring routine — alongside a 6x5 guess grid with flip-reveal animations, dual real-keyboard and on-screen keyboard input, and accurate color feedback that reflects actual letter availability rather than naive character presence checks.

The classic duplicate-letter bug and how this snippet avoids it

A naive scoring function checks, for each letter in the guess, whether that letter exists anywhere in the target word, and colors it yellow if so. This breaks the moment a letter is repeated: if the target word is CRANE and the guess is ERASE, a naive check would mark both E's in ERASE as present because "E" does exist in CRANE — but CRANE only has one E, so only one of the guessed E's should be colored, and the other should be gray. This snippet's scoreGuess() function solves this with two distinct passes and a shared consumed tracking array. Pass one walks the guess left to right and marks every position where the guessed letter exactly matches the target letter at that same index as 'correct' (green), immediately marking that target letter's index as consumed so it cannot be claimed again. Pass two then walks the guess again, and for every letter not already marked correct, searches the target word for an *unconsumed* occurrence of that letter using targetLetters.findIndex((t, idx) => t === letter && !consumed[idx]). If one is found, that position is marked 'present' (yellow) and the matched target index is consumed; if none is found, the letter remains 'absent' (gray) by the array's default fill. Because consumption is shared across both passes and tracked per target-letter-index rather than per unique-letter, a target with exactly one E will correctly color at most one guessed E as green or yellow combined — matching real Wordle behavior exactly.

Grid, flip animation, and staggered reveal

The board is a 6-row by 5-column grid of .cell divs built dynamically by buildGrid(). As the player types, updateCurrentRow() fills the active row's cells with letters and toggles a .filled class for a subtle border highlight before submission. On submit, applyResult() staggers each cell's reveal using setTimeout(..., c * 120), so the five tiles flip left to right in sequence rather than all at once — a small but important detail that gives each guess a satisfying cascading reveal instead of an abrupt color change. The flip itself is a CSS rotateX keyframe animation that rotates the tile through 90 degrees (momentarily edge-on) and back to 0, with the color class applied at the animation's midpoint conceptually — in practice both the .flip and status classes are added together so the color appears as the tile rotates back into view.

Dual input: real keyboard and on-screen keyboard

Two input paths funnel into the same handleKey() function. A document-level keydown listener maps physical key presses (e.key) to letters, Enter, and Backspace, calling handleKey() directly — this is how most players will interact with the game. Separately, buildKeyboard() renders a full on-screen QWERTY layout from the KEY_ROWS array, with each rendered <button> also wired to call handleKey() with its own key value on click, supporting touch devices with no physical keyboard. Both paths converge on identical logic, so there is only one code path to validate for guess length, letter matching, and submission — no duplicated state machine.

Keyboard color feedback with rank-based upgrades

As guesses are scored, updateKeyStatus() colors each on-screen key to reflect the best status ever observed for that letter, using a numeric rank map (absent: 0, present: 1, correct: 2) to ensure a key already shown green from an earlier guess is never downgraded to yellow or gray by a later guess that scores that same letter differently in a different position. This mirrors the letter-priority display convention used in the original Wordle and gives players a persistent, at-a-glance summary of every letter's status across all their guesses so far — similar in spirit to how a Tic-Tac-Toe win-line highlight persists to summarize the final board state.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Paste this snippet into an AI coding assistant like Claude and ask it to trace scoreGuess() through a concrete duplicate-letter example — for instance, target CRANE against guess ERASE — cell by cell, to confirm exactly which letters end up green, yellow, and gray and why the consumed array is what prevents the classic double-credit bug. It's also worth asking the assistant to explain why the two input paths (physical keydown and on-screen keyboard clicks) are both routed through the same handleKey() function rather than duplicated. Beyond understanding, use the assistant to extend the game: ask for a deterministic word-of-the-day mode seeded from the date instead of Math.random(), a persisted win/loss streak and guess-distribution stats using localStorage, a shareable emoji-grid result summary (green/yellow/gray squares) copyable to the clipboard, or a larger validated word/guess dictionary. Treat the current file as a correct, well-tested scoring core to build daily-puzzle features on top of.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a Wordle-style 5-letter word guessing game in plain HTML, CSS, and JavaScript with a 6x5 guess grid and an on-screen keyboard.

Requirements:
- A hidden target word chosen randomly from a hardcoded list of 15-20 common 5-letter words at the start of each game.
- A 6-row by 5-column grid where the player types letters into the current row (via physical keyboard input and/or an on-screen keyboard) and submits the guess with Enter; reject submission if fewer than 5 letters have been entered.
- On submission, score each of the 5 letters as correct (right letter, right position), present (right letter, wrong position), or absent (not in the word, or already fully accounted for), with CORRECT duplicate-letter handling: if a letter appears once in the target but twice in the guess, only one instance of that letter in the guess should be colored correct/present — the other must be colored absent. Do not use a naive "does this letter exist anywhere in the target" check, since it fails on repeated letters.
- Reveal each submitted row's colors with a visual tile-flip animation, ideally staggered slightly across the 5 tiles rather than all appearing simultaneously.
- Reflect accumulated letter knowledge on an on-screen keyboard, coloring each key by the best status observed for that letter across all guesses so far, without ever downgrading a key that was already confirmed correct.
- Detect a win when the guess exactly matches the target word and display a clear win message; after 6 failed guesses, end the game, reveal the target word, and block further input.
- Provide a "New Word" control that picks a new random target and fully resets the grid, keyboard colors, and guess state.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Type a 5-letter guessUse your physical keyboard (letters, Backspace, Enter) or click the on-screen keyboard — both call the same handleKey() function. Letters fill the current row left to right up to 5 characters via currentGuess.
  2. 2
    Submit and watch the flip revealPress Enter or click the ENTER key to call submitGuess(). Each of the 5 tiles flips in sequence (120ms stagger) and reveals its color: green for correct position, yellow for present-but-misplaced, gray for absent.
  3. 3
    Understand duplicate-letter scoringIf your guess repeats a letter that appears only once in the target, scoreGuess()'s two-pass algorithm ensures only one instance is colored green or yellow — the extra repeated letter is correctly marked gray, matching real Wordle rules.
  4. 4
    Track letter status on the keyboardThe on-screen keyboard keys recolor as you play, always showing the best (highest-rank) status ever seen for that letter — a key already green stays green even if a later guess scores that letter differently elsewhere.
  5. 5
    Win or exhaust your attemptsGuessing the exact word shows a win message immediately. After 6 failed rows, gameOver locks further input and reveals the target word in the message area, and the "New Word" button appears.
  6. 6
    Export and customize the word listClick HTML or JSX to export. Edit the WORD_LIST array in the JS panel to add your own themed word set (e.g. brand terms, product names) — any 5-letter uppercase words work without touching the scoring logic.

Real-world uses

Common Use Cases

Branded daily word game for a product or marketing site
Replace WORD_LIST with product names, feature keywords, or brand terms to build a lightweight, on-brand daily word game for a marketing site or internal tool, similar to how companies run branded Wordle clones for engagement campaigns. No backend is required since the word list and scoring logic are entirely client-side.
Teaching example for the classic duplicate-letter scoring bug
This snippet is a strong teaching artifact for interview prep or coding tutorials specifically because the naive one-pass implementation is a well-known trap. Walking through why the two-pass consumed-array approach is necessary, using an example like target CRANE and guess ERASE, makes an abstract correctness issue concrete and testable.
Standalone puzzle-of-the-day widget
Seed targetWord deterministically from the current date (e.g. hashing today's date string to pick an index into WORD_LIST) instead of Math.random() to turn this into a genuine "one puzzle per day" widget that all visitors see the same word for, matching the original Wordle format rather than an infinite-replay game.
Accessible color-blind-friendly palette customization exercise
The default green/yellow/gray palette can be hard to distinguish for color-blind players. Because status classes (.correct, .present, .absent) are applied consistently to both grid cells and keyboard keys, swapping to a color-blind-safe palette or adding shape/icon indicators only requires editing the CSS, with no changes to the scoring logic.
Foundation for a larger word list or difficulty modes
WORD_LIST currently holds 20 common words; swapping in a larger curated dictionary and adding an input-validation step that rejects guesses not present in a separate "valid guesses" list (as real Wordle does) is a natural next step for a production version of this game.
Statistics and streak-tracking extension using localStorage
Layer a persisted win/loss streak and guess-distribution histogram on top of the existing gameOver and currentRow state, stored in localStorage the same way a Whack-a-Mole Game persists its best score, to give players a reason to return daily.

Got questions?

Frequently Asked Questions

scoreGuess() runs two passes over the guess. The first pass marks every exact-position match as correct (green) and immediately records that target letter index as consumed. The second pass then checks each remaining guessed letter against the target using findIndex to locate an unconsumed occurrence — if found, it is marked present (yellow) and that index is consumed too; if not found, it stays absent (gray). Because consumption is tracked per target letter index and shared across both passes, a letter that appears once in the target can only be credited once across the whole guess, exactly matching Wordle's real behavior.

Both instances can be colored, since scoreGuess() consumes one target index per matched guess letter. If the target has two E's and the guess also has two E's, and both guessed E's either match position exactly or find an unconsumed target E, both will be colored green/yellow — the consumed array only prevents over-crediting when the guess has MORE of a letter than the target does, not when both have the same count.

Both work simultaneously. A document-level keydown listener captures physical key presses (letters, Enter, Backspace) and routes them through the same handleKey() function that the on-screen keyboard buttons call on click. There is no separate logic path for either input method, so behavior is identical regardless of how a letter is entered.

updateKeyStatus() uses a rank map (absent: 0, present: 1, correct: 2) and only updates a key's displayed status if the new status outranks its current one. This is intentional and matches real Wordle behavior: if a letter was confirmed correct in one position from an earlier guess, a later guess placing that same letter in the wrong position (which would score as absent or present at that new position) should not erase the earlier, more informative green confirmation.

Edit the WORD_LIST array in the JS panel — any 5-letter uppercase words work with no other code changes. For a deterministic "word of the day" instead of a random word each game, replace the Math.floor(Math.random() * WORD_LIST.length) call in newGame() with an index derived from the current date, for example by hashing a YYYY-MM-DD string, so every player sees the same word on the same calendar day.