webdevpuneet.com

Quick Math Arithmetic Game — Free HTML CSS JS Snippet

Quick Math Arithmetic Game · Games · Plain HTML, CSS & JS · Live preview

CategoryGames

For supporters

Source Code

Want the source code?

Play the game for free. Become a WebDevPuneet supporter to unlock the complete HTML, CSS and JavaScript source code, the exports (HTML, React, Vue, Angular, Tailwind) and Fork.

Play freeSource code: Patreon

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Three operators (+, −, ×) with difficulty-appropriate ranges: 1-50 for addition/subtraction, 1-12 times tables for multiplication
Non-negative subtraction guaranteed by generating the subtrahend as randInt(1, a), avoiding negative-number input parsing
Live countdown timer: setInterval-driven progress bar and numeric label, switching to a red .low gradient in the final 10 seconds
Zero-delay feedback loop: handleSubmit() checks the answer and calls generateProblem() immediately regardless of correctness
Re-triggerable CSS feedback: flash-correct/flash-wrong classes reset via forced reflow (void offsetWidth) for consecutive animations
Enter-key and button submission both handled by a single form submit listener for fast, keyboard-only play
Persisted best score via localStorage with try/catch guards for storage-restricted environments
Results summary computing live accuracy percentage: Math.round((correct / answered) * 100)

About this UI Snippet

Quick Math Arithmetic Game — Timed Arithmetic Quiz with Live Feedback and localStorage Best Score

Screenshot of the Quick Math Arithmetic Game snippet rendered live

Speed-arithmetic games like this one are a staple of mental maths practice apps because they combine two things that make casual games sticky: a hard time constraint and instant right/wrong feedback. This snippet builds a complete 60-second rapid-fire arithmetic quiz in vanilla JavaScript — random addition, subtraction, and multiplication problems, a live countdown, immediate visual feedback on every submission, and a persisted best score using localStorage so returning players have something to beat.

Problem generation and difficulty-appropriate ranges

generateProblem() randomly selects one of three operators — addition, subtraction, or multiplication — and generates operands within ranges chosen specifically to keep each problem type roughly equally difficult under time pressure. Addition and subtraction use operands from 1 to 50, which keeps sums in a comfortably mental-maths range without ever needing carrying-heavy three-digit arithmetic. Subtraction specifically generates b as randInt(1, a), which guarantees a - b is always non-negative — the game deliberately avoids negative-number answers to keep the input format simple (a plain numeric field, no minus-sign parsing ambiguity). Multiplication uses the classic 1-to-12 times-table range, matching how most people actually memorise multiplication facts, which keeps multiplication problems fast to solve rather than requiring long multiplication.

The countdown timer and its visual language

A setInterval running once per second decrements timeLeft and updates both a numeric 60s-style label and a horizontal progress bar whose width is set to (timeLeft / GAME_SECONDS) * 100 percent. In the final ten seconds, a .low class swaps the bar's gradient from the indigo accent to a red gradient, giving players a clear, low-cost visual cue that time is running out without needing to read the numeric label. When timeLeft reaches zero, endGame() fires immediately, stops the interval, and swaps the play area for a results summary — there is no grace period, matching how real speed-round games behave.

Instant feedback without breaking flow

Every submission — whether via the Submit button or the Enter key on the numeric input — is handled by a single handleSubmit() function that parses the input, compares it to currentAnswer, and immediately calls generateProblem() regardless of whether the answer was right or wrong, so play never pauses to wait for acknowledgement. Correct answers trigger a green colour flash on the problem text via a .flash-correct class; incorrect answers trigger a red CSS shake keyframe animation via .flash-wrong. Both classes are removed and a layout reflow is forced with void playArea.offsetWidth before re-adding the class, which is the standard trick for restarting a CSS animation that was already applied on the previous problem — without it, two wrong answers in a row would only animate once.

Scoring, accuracy, and the results screen

The game tracks three separate counters: answered (total problems submitted), correct (correct answers), and a derived score that in this implementation equals correct — each correct answer is worth one point, keeping the scoring model transparent and easy to read at a glance during play. When the timer expires, the results screen computes accuracy as Math.round((correct / answered) * 100) and displays answered count, correct count, and accuracy percentage side by side.

Persisting a best score with localStorage

getBest() and setBest() wrap localStorage.getItem/setItem in try/catch blocks (some browser contexts, like sandboxed iframes with storage disabled, throw on access) around a single numeric key, quick-math-best-score. At the end of each round, if the current correct count exceeds the stored best, the game updates localStorage, refreshes the header badge, and shows a "New best score!" message — the score persists across page reloads and browser sessions since localStorage survives until explicitly cleared, unlike sessionStorage or in-memory state.

Step by step

How to Use

  1. 1
    Answer problems as fast as you canA random addition, subtraction, or multiplication problem appears in generateProblem(). Type your answer into the numeric input and press Enter or click Submit — handleSubmit() checks it immediately and generates the next problem with zero delay, so keep typing without pausing.
  2. 2
    Watch the live countdown barThe horizontal timer-fill bar and the numeric label both count down from 60 seconds via a setInterval tick() call. In the final 10 seconds the bar switches to a red gradient using the .low class as an urgency cue.
  3. 3
    Read the instant right/wrong feedbackA correct answer flashes the problem text green via the flash-correct class; an incorrect answer triggers a red shake animation via flash-wrong. Both are re-triggered on every submission using a forced reflow (void playArea.offsetWidth) so consecutive same-outcome answers still animate.
  4. 4
    Review your results when time runs outWhen timeLeft hits zero, endGame() stops the timer and shows problems answered, correct count, and accuracy percentage (Math.round((correct / answered) * 100)). Click "Play again" to call startGame() and reset the timer, score, and problem generator.
  5. 5
    Beat your persisted best scoreThe Best badge in the header reads from localStorage via getBest(). If your correct count this round beats the stored best, setBest() updates localStorage and a "New best score!" message appears on the results screen — this persists across page reloads.
  6. 6
    Adjust difficulty and durationChange GAME_SECONDS at the top of the JS panel to make rounds longer or shorter. Adjust the randInt() ranges inside generateProblem() — e.g. raise addition/subtraction to randInt(1, 100) or the multiplication range to randInt(1, 20) — to tune difficulty for different age groups or skill levels.

Real-world uses

Common Use Cases

Mental maths practice tool for students and classrooms
Timed arithmetic drills are a well-established method for building automaticity in basic maths facts. Embed this in an education site or classroom activity page as a warm-up exercise — the 60-second format is short enough to run at the start of a lesson, and the persisted best score gives students a personal target to beat across sessions without needing a login system.
Waiting-room or loading-screen engagement game
Drop this into an app splash screen, queue page, or "your download is preparing" state to give users something genuinely fun to do while they wait, rather than a static spinner. Because it is fully self-contained with no network calls, it works even if the underlying page is still loading other resources.
Brain-training or cognitive-speed daily challenge feature
Brain-training apps commonly include a reaction-speed or processing-speed mini-game alongside memory and logic puzzles. This snippet's combination of a hard time limit, instant feedback, and a persisted personal best fits that pattern directly, and can sit alongside puzzle-style content like the Word Unscramble Puzzle Game in a mini-games hub.
Teaching real-time countdown UI and animated feedback states
The timer bar's percentage-driven width and colour-swap-at-threshold pattern is directly reusable for any countdown UI — session expiry warnings, form auto-save timers, or checkout time limits. Swap the accent colour #6366f1 for your brand colour and the urgency-red #ef4444 stays as a universally understood warning colour.
Learn setInterval timer management and localStorage persistence patterns
This snippet demonstrates correctly starting and clearing a setInterval (clearInterval is called both when a new game starts and when the timer expires, preventing duplicate intervals from stacking up on repeated Play Again clicks) plus a defensive localStorage read/write pattern wrapped in try/catch — both are common sources of subtle bugs in real applications.
A/B testing different difficulty curves for gamified onboarding
Product teams building gamified onboarding flows can use a snippet like this as a base to test how different problem-difficulty ranges and round lengths affect completion rate and perceived difficulty. Because the number ranges and GAME_SECONDS constant are isolated at the top of the JS, they are trivial to parameterise for an experiment.
Related: Sky Hopper Game
See the Sky Hopper Game for a related games pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

The subtrahend b is generated as randInt(1, a) — a random integer between 1 and the already-chosen minuend a — which guarantees a - b is always zero or positive. This is a deliberate design choice to keep the answer input a plain non-negative numeric field; supporting negative answers would require either a signed-number keypad affordance or extra input parsing for a leading minus sign.

Add '÷' to the ops array, and inside the operator branch generate the answer first and derive the operands from it to guarantee a whole-number result: const answer = randInt(1, 12); const b = randInt(1, 12); const a = answer * b; currentAnswer = answer; then display a + ' ÷ ' + b + ' = ?'. Generating from the answer outward avoids fractional results, which the numeric-only input cannot represent.

Yes. The best score is stored under the localStorage key quick-math-best-score, which is scoped to the browser and origin — clearing site data, using a different browser, or playing in a private/incognito window all start best-score tracking fresh. To sync a best score across devices you would need to send it to a backend and associate it with a user account.

Yes — change the GAME_SECONDS constant at the top of the JS panel to any value in seconds, for example 30 for a faster round or 120 for an extended session. The timer bar, numeric label, and low-time red-gradient threshold (currently the final 10 seconds) all derive from GAME_SECONDS automatically, so no other code needs to change.

In this implementation score and correct always move together — every correct answer adds exactly one to both. They are tracked as separate variables so you can extend the scoring model independently later, for example weighting multiplication problems higher than addition, or adding a speed bonus for very fast consecutive correct answers, without having to change how the correct-answer accuracy statistic is calculated.