Bug Hunt Game — Free HTML CSS JS Snippet

Bug Hunt Game · Games · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Seven real defect classes: bad accumulator seed, off-by-one loop bound, dot notation on a dynamic key, return inside forEach, missing clearTimeout in a debounce, reference copy mistaken for a clone, and a stray semicolon after if
A stated intent per level, without which several of the defects would be unfalsifiable
Corrected line shown as copy-ready code alongside a written explanation of the failure mechanism
Line numbers rendered with a CSS counter in a: :before pseudo-element, so copied code stays clean
Single-guess rounds with a locked flag, highlighting the correct line whether or not it was found
Round-over state expressed as a class on the code container rather than by unbinding event listeners
Event-delegated clicks resolving the line index from a data attribute
Fisher-Yates shuffle over an index array so replays reorder without mutating the source data

About this UI Snippet

Bug Hunt Game — Clickable Code Lines, Real Failure Modes & A Fix Shown After Every Guess

Screenshot of the Bug Hunt Game snippet rendered live

Reading code for defects is a skill nobody is explicitly taught — it is absorbed slowly through code review, usually after shipping the same bug once. This snippet compresses that loop into something playable: a short function with a stated purpose, a file-window UI with numbered lines, and one click to say where the defect is. Every round ends by showing the corrected line and explaining exactly how the original fails, so a wrong guess costs nothing but still teaches the pattern.

Seven defects that are real, not typos

The bugs are chosen because each represents a class of failure rather than a one-off slip: seeding a maximum at 0 so all-negative input returns a value that was never in the array; the <= off-by-one that reads one past the end and throws on property access; dot notation used for a dynamic property so state.key looks up a literal "key"; return inside a forEach callback, which exits the callback and is silently discarded, making the function always return false; a debounce that never calls clearTimeout, so every event still fires — just late; assigning an object and mutating the "copy", which writes through to the caller's original; and a stray semicolon after if (...) that empties the conditional's body so the discount applies to every order. Each why string names the mechanism, and each fix shows the corrected line ready to read.

Clickable lines built from data, numbered by CSS

Each bug is a data object holding the file name, a brief describing intended behaviour, an array of source lines, the index of the defective line, the corrected line, and the explanation. Lines render as <li> elements carrying a data-index, and line numbers come from a CSS counter-reset/counter-increment pair with the value drawn in a ::before pseudo-element — so the numbers are presentational only. That matters more than it sounds: a player who selects and copies the code gets the code, not a column of numbers glued to it, which is exactly how a real editor behaves.

A brief that makes the bug findable

Every level states what the function is *supposed* to do before showing the code, because a defect is only a defect relative to an intention. Without the brief, the max = 0 seed and the forEach return are unfalsifiable — the code is internally consistent and syntactically fine. Giving the intent first is also the honest model of real review: you cannot review code whose purpose you do not know.

One guess, then the answer

A locked flag makes each round single-shot: the first click resolves it. The correct line is always highlighted green whether or not the player found it, and a wrong pick is marked red simultaneously, so both pieces of information are on screen at once. The container also gets a locked class that removes the hover affordance and the pointer cursor, which is the visual signal that the round is over — state expressed through a class on the parent rather than by unbinding listeners, so nothing has to be re-bound on the next render.

Shuffled rounds over an index array

start() shuffles an array of indices with Fisher-Yates and walks that, leaving BUGS itself untouched, so a replay presents the same seven defects in a new order. Finishing the last bug reports the round score and restarts automatically after a short pause.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Paste this snippet into an AI assistant like Claude and ask it to add a second phase per round where, after finding the buggy line, the player has to pick the correct fix from three plausible candidates — spotting a defect and knowing how to repair it are different skills, and the wrong-but-tempting fixes are where the real teaching happens. Other extensions worth requesting: a timer with a par time per bug, a difficulty ramp that shows longer functions with more distractor lines, a category tag per bug (async, mutation, off-by-one) with per-category accuracy tracked in localStorage so weak areas can be replayed, or a mode that shows a failing test output instead of a written brief so players debug from a symptom rather than a description.

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 playable "spot the bug" code review game in plain HTML, CSS, and JavaScript — no frameworks or libraries.

Requirements:
- A BUGS array where each entry has a file name, a brief describing what the code is SUPPOSED to do, an array of source lines, the zero-based index of the defective line, the corrected line, and an explanation of how the original fails.
- Choose defects that represent real failure classes rather than typos: an accumulator seeded at 0 that breaks on all-negative input, a <= loop bound that reads past the end, dot notation used for a dynamic property key, return inside a forEach callback being discarded, a debounce missing clearTimeout, an object assignment mistaken for a copy, and a stray semicolon after an if condition.
- Render the code in a file-window UI as a list of clickable lines, with line numbers produced by CSS counters in a ::before pseudo-element so copied code stays clean.
- Always show the brief above the code — a defect is only judgeable against stated intent.
- Allow one guess per round using a locked flag. On the guess, highlight the correct line green whether or not the player found it, mark a wrong pick red at the same time, and reveal both the corrected line as copy-ready code and the written explanation.
- Express the round-over state as a class on the code container that removes hover affordances, rather than unbinding event listeners.
- Use event delegation with a data-index attribute for line clicks, shuffle the round order with Fisher-Yates over an array of indices (never mutating the source data), and report the round score before reshuffling for another pass.

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
    Read the brief firstThe orange-bordered line states what the function is supposed to do. That intent is what makes the defect findable — several of these bugs are syntactically valid code that is simply wrong for the stated purpose.
  2. 2
    Scan the numbered linesThe code renders in a file-window with numbered lines, and hovering highlights the line under your cursor. Line numbers come from a CSS counter, so selecting and copying the code gives you clean source without the numbers.
  3. 3
    Click the line you think is wrongYou get one click per round. The correct line highlights green regardless of your answer, and a wrong pick is marked red at the same time so you can compare the two directly.
  4. 4
    Read the fix and the explanationEvery round shows the corrected line as copy-ready code plus a written explanation of the failure — why returning inside forEach is discarded, why <= reads past the end, why a stray semicolon empties an if body.
  5. 5
    Continue through all seven bugsThe found counter tracks how many you spotted on the first try. After the last bug the round score is announced and the game reshuffles automatically for another pass.
  6. 6
    Replay for a different orderEach round shuffles an array of indices with a Fisher-Yates pass rather than mutating the source data, so the same seven defects arrive in a new sequence every time.

Real-world uses

Common Use Cases

Code review training for junior developers
Reading for defects is learned by repetition against real failure classes, not by reading a style guide. A short bug hunt makes an effective warm-up before a review session, especially paired with a predict the output quiz covering the language quirks that cause the same bugs.
Internal onboarding for a codebase's common mistakes
Swap the BUGS array for defects that actually recur in your repository — a forgotten cleanup, a misused helper, an unsafe cast — and the game becomes a targeted onboarding module that teaches your team's specific footguns rather than generic ones.
Developer marketing, careers pages, and conference booths
A two-minute bug hunt is instantly legible to a technical audience and demonstrates competence better than a feature list. It sits well beside other learn-by-playing snippets such as the CSS Selector Challenge Game.
File-window UI pattern for code presentation
The window chrome, monospaced line list, CSS-counter line numbers, hover affordance and inset highlight bar together make a clean, reusable pattern for any interface that displays code — diff viewers, error reports, and documentation callouts.
Interactive changelog or postmortem storytelling
Engineering blog posts about an incident can present the offending code and let readers try to spot the defect before revealing it, which is far more memorable than a screenshot with an arrow drawn on it.
Assessment component for a hiring or training platform
Because each round is one data object and one click, the format is easy to score and instrument — recording which defect classes a candidate misses is more informative than a single pass/fail on a whole exercise.

Got questions?

Frequently Asked Questions

Because a bug only exists relative to intended behaviour. Several of these samples — seeding max at 0, returning inside forEach — are perfectly valid JavaScript that simply does not do what was wanted. Without the stated intent there is nothing to judge the code against, which is also true of real code review.

With CSS counters: counter-reset on the list, counter-increment on each item, and the value drawn in a ::before pseudo-element. They are presentational only, so a player who selects and copies the code gets the source without a column of numbers merged into it — the same behaviour a real editor gives you.

No — a locked flag makes each round single-shot, which keeps the game honest about whether you actually spotted the defect. The correct line highlights green either way and a wrong pick is marked red at the same time, so a miss still shows you both what you chose and what was right.

Push an object onto the BUGS array with six keys: file (the filename shown in the window chrome), brief (what the code should do), lines (an array of source lines, one string per line), bug (the zero-based index of the defective line), fix (the corrected line), and why (the explanation). Keep samples short — seven lines or fewer reads well without scrolling.

Yes. Keep BUGS in a module, hold the shuffled order, position, score and locked flag in component state, and render the lines from the data array with the highlight classes derived from state rather than added imperatively. A single click handler on the list with the index from the rendered item replaces the delegated listener, and the auto-restart timeout should be cleared on unmount in useEffect / onUnmounted / ngOnDestroy.