You Might Also Like
Hangman Word Guessing Game — Free HTML CSS JS Snippet
Hangman Word Guessing Game · Games · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Hangman Word Guessing Game — SVG Gallows Reveal, On-Screen Keyboard & Word State Machine

Hangman is one of the oldest word-guessing games, and its core mechanic translates cleanly into a small, self-contained state machine: a hidden word, a limited number of wrong guesses, and a visual penalty that escalates with every mistake. This snippet builds a fully playable version with a real SVG-drawn gallows figure that reveals one new body part per wrong guess, a clickable on-screen QWERTY keyboard, and full physical-keyboard support, all driven by a small set of JavaScript functions with no external word-guessing library.
The gallows as layered, pre-drawn SVG
Rather than drawing the hangman figure with JavaScript canvas calls or swapping image files, the entire gallows and figure are pre-drawn once as a single SVG containing eight separate shape elements: the gallows post and beam (always visible), then seven body-part elements — the rope, head, body, both arms, and both legs — each given the class hm-part and starting at opacity: 0. A PART_ORDER array in JavaScript lists these seven part class names in the exact sequence they should appear. Every time a wrong guess is made, revealBodyPart(wrongCount - 1) looks up the next class name in that array and adds a .show class, which flips opacity to 1 with a CSS transition. This means the entire drawing logic is just an array index lookup plus a class toggle — no path recalculation, no redraw, and the reveal animation is handled entirely by the CSS transition: opacity 0.25s ease rule.
Word state and letter-slot rendering
The secret word is chosen once per game with WORDS[Math.floor(Math.random() * WORDS.length)] from a small hardcoded word list, and correctly-guessed letters are tracked in a Set called guessedLetters — a Set is used specifically because it gives O(1) has() lookups and naturally prevents duplicate guesses from being counted twice. renderWord() rebuilds the row of letter slots on every guess: it iterates each character of the secret word and, for characters present in guessedLetters, renders the actual letter with a .revealed class that switches its underline to the accent colour; otherwise it renders an underscore placeholder. Because a Set lookup is used rather than tracking revealed positions directly, a single correct guess of a repeated letter (for example guessing "A" in "GALAXY") reveals every occurrence of that letter simultaneously, exactly as the physical game works.
Dual input: on-screen keyboard and real keyboard
The on-screen keyboard is generated programmatically by looping character codes 65 through 90 (A-Z) and creating one button per letter, each wired to guessLetter(letter) via addEventListener. A parallel keydown listener on document normalises e.key to uppercase and, if it is a single A-Z character, calls the exact same guessLetter() function — so physical typing and on-screen clicking share one code path with no duplicated logic. Once a letter has been guessed, its corresponding button gets disabled = true plus either a .correct (green) or .wrong (red) class, which both greys it out via reduced opacity and blocks further clicks or accidental re-guesses of the same letter from the keyboard listener, since guessedLetters.has(letter) short-circuits guessLetter() at the top.
Win and loss detection
After every guess, checkGameEnd() runs two checks: [...secretWord].every(ch => guessedLetters.has(ch)) tests whether every unique character in the word has been guessed (a win), and wrongCount >= MAX_WRONG (set to 7, matching the seven gallows parts revealed after the always-visible frame) tests for a loss. On a loss, revealWordFully() forcibly renders every letter of the secret word regardless of what was guessed, so the player sees the answer. Either outcome disables the entire keyboard and reveals the "Play Again" button, which calls newGame() to pick a fresh random word, reset guessedLetters, hide all gallows parts, and rebuild the keyboard from scratch.
Build with AI
Build, Understand, Optimize, and Extend It With AI
Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain how PART_ORDER and revealBodyPart() work together to turn a single wrongCount integer into the correct sequence of visible SVG body parts — that indirection is the cleverest part of the implementation and worth understanding fully before you extend it. It's also a strong candidate for AI-assisted improvements: ask the assistant to add word categories or difficulty levels by restructuring the WORDS array, to add a hint system that reveals one free letter after three wrong guesses, or to add a simple win/loss streak counter persisted in localStorage across rounds. You could also ask it to review the shared guessLetter() code path for the on-screen keyboard and the physical keydown listener and confirm there's no way for a disabled key's guess to be double-counted. Use it as a live collaborator to poke at the logic, not just a black box to copy.
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:
Build a classic Hangman word-guessing game in plain HTML, CSS, and JavaScript — no frameworks, no build tooling.
Requirements:
- A hidden word chosen randomly from a small hardcoded word list at the start of each game, displayed as a row of underscore placeholders that reveal the correct letter (in every matching position at once) as soon as it is guessed correctly.
- A visual penalty figure (built from layered SVG or stacked CSS shapes, for example a hangman gallows) that reveals exactly one additional piece for each wrong guess, up to a maximum of 7 wrong guesses before the game ends in a loss.
- Both a clickable on-screen QWERTY keyboard and support for real physical keyboard input, routed through the same underlying guess-handling function so behavior never diverges between the two input methods.
- Already-guessed letters must become visually disabled/greyed out on the on-screen keyboard immediately after being guessed, with a distinct visual treatment for correct versus incorrect guesses, and must not be guessable again from either input method.
- A win condition when every letter in the word has been guessed before running out of wrong guesses, and a loss condition when wrong guesses reach the maximum — on loss, reveal the full secret word even if it was not completed.
- A "Play again" control that starts a brand-new round with a new random word, resets the wrong-guess count, hides the penalty figure, and re-enables the full keyboard.
- Handle repeated letters correctly (for example guessing a letter that appears multiple times in the word reveals all of its occurrences in a single guess) and ignore invalid input (non-letter keys, already-guessed letters) without penalising the player.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
- 1Guess a letterClick any letter on the on-screen keyboard, or simply type on your physical keyboard — the document-level keydown listener normalises input and calls the same guessLetter() function either way.
- 2Watch correct guesses reveal instantlyA correct letter reveals every matching position in the word at once via the guessedLetters Set lookup in renderWord(), and the corresponding on-screen key turns green and becomes disabled.
- 3Track wrong guesses on the gallowsEach wrong guess increments wrongCount, updates the "X / 7 wrong guesses" counter, and calls revealBodyPart() to fade in the next SVG body part — rope, head, body, arm, arm, leg, leg — in that fixed order.
- 4Reach a win or loss stateGuessing every letter in the word before reaching 7 wrong guesses triggers a win message; reaching 7 wrong guesses first triggers a loss message and forcibly reveals the full word via revealWordFully().
- 5Start a new roundClick "Play Again" to call newGame(), which picks a new random word from the WORDS array, clears guessedLetters, resets wrongCount to zero, hides all gallows parts, and rebuilds a fully enabled keyboard.
- 6Expand the word listAdd or replace entries in the WORDS array at the top of the JS panel — keep them uppercase since guessLetter() compares against uppercase input, or add a themed category selector that swaps in a different array before calling newGame().
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
A PART_ORDER array lists the CSS class names of the seven hangman parts in the exact sequence they should appear (rope, head, body, both arms, both legs). Each wrong guess calls revealBodyPart(wrongCount - 1), which looks up PART_ORDER at that index and adds a .show class to the matching SVG element, flipping its opacity from 0 to 1 via a CSS transition — no coordinate math or redrawing is involved.
A Set gives constant-time has() lookups, which keeps duplicate-guess checking and word-completion checking fast regardless of word length, and it inherently rejects duplicate values so the same letter can never be counted twice even if guessLetter() were somehow called twice with the same letter.
Yes. A single keydown listener on document normalises e.key to uppercase and, if it is a letter A-Z, calls the exact same guessLetter() function that the on-screen keyboard buttons call. There is no separate code path for physical typing, so behaviour (including duplicate-guess prevention and disabling used keys visually) stays consistent between both input methods.
Seven, defined by the MAX_WRONG constant, which matches the seven SVG parts revealed after the always-visible gallows frame (rope, head, body, two arms, two legs). Change MAX_WRONG to make the game easier or harder — note you would also need to adjust PART_ORDER if you change the total number of visual stages.
Yes — replace the single flat WORDS array with an object of categories, for example { easy: [...], hard: [...] }, add a category selector to the HTML, and update newGame() to pick a random word from the currently selected category array instead of the single WORDS constant. The rest of the game logic (guessing, rendering, win/loss detection) needs no changes.