You Might Also Like
Regex Match Game — Free HTML CSS JS Snippet
Regex Match Game · Games · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Regex Match Game — Live RegExp.test Grading, Positive and Negative String Sets & Too-Loose Diagnosis

The hard part of regular expressions is never writing something that matches — it is writing something that matches the right things and nothing else. A pattern that passes your three examples and quietly also matches half your production data is the classic regex bug, and it is invisible if you only ever test the positive cases. This snippet is a playable trainer built around that exact tension: every level gives a list of strings the pattern must match and a second list it must reject, and a level is only cleared when both lists are fully satisfied.
Grading against string sets, not a stored answer
There is no reference pattern anywhere in the level data. Each level is defined purely by its yes and no arrays, and grading runs the player's compiled RegExp against every string in both lists with re.test(s), requiring true for every positive and false for every negative. That means any pattern satisfying the specification passes — on the file-extension level, \.(png|jpg)$, \.(jpe?g|png)$ and other equivalent formulations are all accepted. Because the specification *is* the test set, the game teaches the habit of thinking about a regex in terms of what it accepts and rejects rather than in terms of a memorised incantation.
Compiling user input without letting it break the page
compile() wraps new RegExp(pattern) in try/catch and returns null on failure, because a half-typed pattern like [a- or (png| throws a SyntaxError on nearly every keystroke while the player is composing. Returning null lets callers distinguish three states cleanly — empty input, invalid syntax, and a usable expression — so the input can show a red border mid-typing without clearing the result marks or spilling errors into the console. The input is also length-capped in the markup, which keeps a pathological pattern from being pasted in wholesale.
Live per-string verdicts on both columns
paint() walks a list, tests each string, and applies a pass or fail class based on whether the actual result equals the *desired* result for that column — so in the left column a match is a pass, and in the right column a match is a failure. Each row also prints the raw verdict ("match" or "no") next to the string, which separates the two things a learner needs to see at once: what the regex did, and whether that was what the level wanted. Because both columns are repainted on every input event, a player watches strings flip between columns' pass states character by character as the pattern is built.
Failure messages that name the direction of the error
When a submission is not perfect, the status line compares the two column scores and reports the specific failure mode: "too strict" when every negative is correctly rejected but some positives are still unmatched, "too loose" when every positive matches but some negatives matched too, and a plain score when both columns have problems. Too-loose is the regex bug that matters most in real code, and naming it explicitly — rather than saying "wrong" — is what turns a wrong answer into a lesson about over-broad patterns.
Levels as a progression of concepts
The seven levels move through literal substrings, character classes, the + quantifier, the ^ start anchor, the \d digit shorthand, alternation with an escaped literal dot and the $ end anchor, and finally a both-ends-anchored email-shaped pattern. Each carries a one-line hint explaining the technique rather than giving the answer, and the negative lists are chosen adversarially — level four rejects "blog", "catalog" and "analogue" specifically so an unanchored log fails and the player has to discover ^.
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 flags row (g, i, m) with levels that only solve when a specific flag is set, which teaches the part of regex most tutorials skip. Other strong extensions: add a capture-group mode where the level specifies expected captured values rather than just match/no-match, add a "shortest pattern wins" scoring rule to discourage brute-force alternation lists, show a live visualisation of which characters in each string the regex consumed using match indices, or add a catastrophic-backtracking guard that times a test run and warns when a pattern is pathologically slow on the sample strings.
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 playable regular-expression game in plain HTML, CSS, and JavaScript — no frameworks or libraries.
Requirements:
- A levels array where each level is specified ONLY by a plain-English goal, an array of strings the pattern must match, an array of strings it must not match, and a one-line hint. Do not store a reference answer — the string sets are the specification.
- An input styled between two literal slashes, where the typed pattern is compiled with new RegExp() inside a try/catch. Return a three-state result: empty, invalid syntax, or usable, so a half-typed pattern shows an invalid-input state rather than throwing.
- Re-test every string in both lists on every keystroke with RegExp.test(), and show two things per row: the raw verdict (matched or not) and whether that verdict was correct for its column — a match in the must-not-match column is a failure.
- Clear a level only when every string in both columns is correct, accepting any pattern that satisfies the specification.
- On a failed submission, diagnose the direction of the error: "too strict" when negatives are all correctly rejected but positives remain unmatched, "too loose" when all positives match but some negatives matched too, with counts.
- Make the must-not-match lists adversarial near-misses so anchors, escaping and precise quantifiers have to be discovered — for example a level whose positives all start with "log" and whose negatives include "blog" and "catalog".
- Include at least seven levels progressing through literal substrings, character classes, the + quantifier, the ^ anchor, \d, alternation with an escaped literal dot and the $ anchor, plus hint and skip buttons and a cleared-levels counter.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
- 1Read both columns before typingThe green column lists strings your pattern must match; the red column lists strings it must not. The negative list is chosen adversarially — on the "log" level it contains blog, catalog and analogue, so an unanchored pattern will visibly fail.
- 2Type a pattern between the slashesThe input sits between two literal slashes to mirror regex literal syntax. Every keystroke recompiles the pattern and retests all six strings, so you see the effect of adding a single character immediately.
- 3Watch the per-string verdictsEach row prints whether the regex matched it ("match" or "no") and turns green or red depending on whether that was the desired outcome for its column — a match in the right-hand column is a failure, not a success.
- 4Press Enter or click Test to submitA submission is only accepted when every string in both columns is correct. There is no reference answer to guess: any pattern that satisfies the specification clears the level.
- 5Read the direction of your missA failed submission tells you whether you were too strict (positives still unmatched) or too loose (negatives matched as well) with counts, which is the distinction that matters most when debugging a real regex.
- 6Use hints and work through all seven levelsShow hint explains the technique the level is about — character classes, the + quantifier, anchors, \d, alternation, escaping a literal dot — without giving the pattern away. Levels ramp from a literal substring to an anchored email-shaped expression.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
No. Each level is defined only by its list of strings that must match and strings that must not; grading runs your compiled pattern against all of them with RegExp.test() and clears the level when every verdict is correct. On the image-extension level, \.(png|jpg)$ and \.(jpe?g|png)$ both pass because both satisfy the specification.
Incomplete patterns like [a-z or (png| are invalid regex and make new RegExp() throw a SyntaxError. compile() catches that and returns null, which the UI treats as "not valid yet" — the input border turns red, the string verdicts reset to a neutral dot, and nothing is logged as an error.
It means every string in the must-match column matched, but at least one string in the must-not-match column matched as well — your pattern is over-broad. That is the most common and most dangerous regex bug in real code, which is why the game names it specifically instead of just saying the answer was wrong.
Push an object onto LEVELS with four keys: goal (the instruction), yes (an array of strings the pattern must match), no (an array it must reject), and hint (a one-line explanation of the technique). No answer key is needed. Make the no array adversarial — near-misses of the yes strings are what force precise patterns.
Yes. Keep LEVELS and the compile() helper in a plain module, hold the pattern string and level index in component state, and derive the per-string verdicts during render instead of mutating classes — the whole grading step is a pure function of (pattern, level), which maps neatly onto useMemo in React, a computed property in Vue, or a computed signal in Angular. The only imperative bits are focusing the input and the advance timeout, which belong in useEffect/onMounted/ngAfterViewInit with clearTimeout on cleanup.