Live Match Scoreboard — Free Sports Scoreboard HTML CSS JS

Live Match Scoreboard · Dashboards · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Restart-safe flash animation
A forced reflow ensures the highlight restarts even on rapid, back-to-back score changes.
Tabular-number stability
Scores and the clock never jitter the layout as digit widths change.
Independent clock and scoring timers
Two separate intervals let match time and scoring cadence be tuned independently.
Live status indicator
A pulsing dot and "LIVE" label signal the match is in progress.
Set history pills
A simple derived row shows which team won each completed set.
Shared scoring logic
Manual buttons and auto-play both call the same addPoint() function.
Clean start/stop toggle
Auto-play cleanly clears both intervals with no leaked timers.
Sport-agnostic structure
Swap sets for periods, innings, or quarters with minimal changes.

About this UI Snippet

Live Match Scoreboard — Two Teams, a Clock, and a Flash on Every Score Change

Screenshot of the Live Match Scoreboard snippet rendered live

A good live scoreboard needs to answer three questions at a glance — who's ahead, how far into the match are we, and did something just happen — without the viewer having to stare and compare numbers. This snippet builds that in plain HTML, CSS, and vanilla JavaScript: two team scores, a running match clock, a row of completed-set history pills, and a genuine flash animation the instant either score changes.

A flash that restarts even on rapid updates

The signature detail is flash(): it removes the .lms-flash class, forces a synchronous reflow with void el.offsetWidth, and *then* re-adds the class. That reflow-forcing line matters — without it, if a score updates again while the previous flash animation is still finishing, simply re-adding a class that's already present does nothing, because the browser has no new state change to animate from. Forcing a reflow in between guarantees the flash restarts cleanly every single time, even during a burst of rapid scoring.

Tabular numbers keep the layout stable

Both the score and the clock use font-variant-numeric: tabular-nums, so digits don't shift the surrounding layout as they change width (a "1" next to an "11" would otherwise nudge things around) — small, but it's the difference between a scoreboard that feels solid and one that visibly jitters on every update.

Two independent timers, cleanly started and stopped

Auto-play runs two separate setInterval timers — one incrementing a random team's score every 1.8s, one advancing the match clock every second — started together and both cleared together by stopAuto(). Keeping them as two separate intervals (rather than one interval doing both jobs) means the scoring cadence and the clock cadence can be tuned independently, which matches how a real match actually behaves: the clock runs continuously while scoring is sporadic.

Set history as a simple derived row

setsWon is just an ordered array of 'A'/'B' characters, and renderSets() maps it straight to a row of colored pills — a lightweight way to show match history (who won set 1, set 2, and so on) without a heavier stats table.

Manual and automatic controls coexist

The "+1 Hawks" / "+1 Otters" buttons and the auto-play toggle both call the same addPoint() function, so manual scoring during a live event and a demo/simulated auto-play mode share identical logic — there's no separate code path to keep in sync.

Customizing it

Replace the auto-play random scoring with a real WebSocket or polling feed calling addPoint() on genuine score events, add a period/quarter indicator instead of sets for other sports, or extend setsWon with actual set scores. Pair it with a tournament match bracket for the full event, or a live vote bar race for a different kind of live-updating comparison.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Rather than debugging a flash animation that silently fails to restart, paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why forcing a reflow with void el.offsetWidth between removing and re-adding the flash class is necessary for the highlight to restart on rapid, consecutive score changes. The same assistant can help optimize it — for example asking whether the clock and scoring intervals should be replaced with a single requestAnimationFrame-based loop for tighter timing accuracy, or how to handle the auto-play timers correctly if the browser tab is backgrounded and setInterval gets throttled. It's also useful for extending the scoreboard: ask it to add a possession/serve indicator, a period or quarter structure instead of sets, or a subtle sound effect on score changes. Treat the code less like a finished artifact and more like a starting point for a conversation.

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 "live match scoreboard" for a two-team sports match in plain HTML, CSS, and JavaScript with no library.

Requirements:
- Two team score displays and a match clock, all using tabular number formatting so digit width changes never shift the surrounding layout, plus a row of pills showing which team won each previously completed set/period.
- A live status indicator (for example a pulsing animated dot plus a "LIVE" label) always visible while the match is in progress.
- A single function that increments a given team's score, updates its displayed number, and triggers a brief flash-highlight animation on that score element — the flash must be implemented so that it reliably restarts even if the same team scores again while the previous flash animation is still playing, which requires explicitly forcing a synchronous browser reflow between removing and re-adding the animation class rather than simply toggling the class.
- Manual "+1" buttons for each team that call this shared scoring function, plus a separate auto-play toggle that starts two independent timers when activated — one that randomly increments either team's score every couple of seconds, and one that advances the match clock display once per second — both of which must be fully and cleanly stopped (no leaked timers) when auto-play is toggled off.
- The match clock must be formatted as minutes:seconds and increment correctly, including rolling over seconds into minutes.
- The set-history pills must be generated from a simple ordered array of which team won each set, rendered as colored badges distinguishing the two teams.

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
    Paste HTML, CSS, and JSA scoreboard renders mid-match with two team scores, a clock, and set history pills.
  2. 2
    Click "+1 Hawks" or "+1 Otters"That team's score increments with a brief green flash-highlight.
  3. 3
    Click "Auto-play"Scores update randomly every 1.8s and the clock advances every second.
  4. 4
    Click "Stop auto-play"Both timers clear immediately.
  5. 5
    Score rapidlyNotice the flash animation restarts cleanly even on back-to-back updates.
  6. 6
    Connect a real feedCall addPoint('A') or addPoint('B') from your live-scoring WebSocket or API poll.

Real-world uses

Common Use Cases

Sports league and event sites
A live scoreboard widget for volleyball, tennis, or similar set-based sports.
Esports match overlays
Adapt for round-based competitive games with a match clock.
School and community league scoring
A simple manual-entry scoreboard for local games.
Sports betting and stats dashboards
Pair with a tournament match bracket for full-event context.
Live event broadcast graphics
A lower-third-style scoreboard driven by a real scoring feed.
Learning restart-safe CSS animations
A reference for the forced-reflow flash technique — compare with confetti button for other feedback animations.

Got questions?

Frequently Asked Questions

If a score updates again while the previous flash animation is still playing, simply removing and immediately re-adding the same CSS class does nothing visually, because the browser batches the class changes and never sees the class actually leave and return. Reading el.offsetWidth between the remove and the re-add forces the browser to synchronously recalculate layout at that point, which flushes the class removal before the class is re-added — guaranteeing the animation restarts cleanly every time, even during a fast scoring streak.

A real match's clock runs continuously and predictably (once per second) while scoring happens sporadically and at a different rhythm, so using two independent setInterval calls lets each cadence be tuned without affecting the other. It also means the clock keeps ticking accurately even while, in a real integration, score updates might arrive irregularly from a WebSocket rather than on a fixed timer.

Replace the auto-play interval\'s random selection with your WebSocket message handler or polling logic, calling addPoint(\'A\') or addPoint(\'B\') whenever a real point is scored for that team — addPoint() already handles incrementing the score, updating the DOM, and triggering the flash animation, so no other changes are needed. Drive clockSeconds from your feed\'s authoritative match time in the same way, calling renderClock() after updating it.

Most fonts render digits at different widths (a "1" is narrower than a "4"), so without tabular-nums a score changing from 9 to 10, or a clock ticking from 19:59 to 20:00, would cause the surrounding layout to visibly shift as the digit count or digit widths change. Tabular-nums forces every digit to the same fixed width, keeping the scoreboard\'s layout perfectly stable through every update.

Hold scoreA, scoreB, clockSeconds, and setsWon in component state, and trigger the flash animation with a short-lived state flag toggled on and off (or by keying the score element with a changing key prop in React to force a remount) rather than manually manipulating classList — frameworks generally handle "restart an animation on repeated state changes" via key-based remounting instead of the manual reflow trick this vanilla version uses.