webdevpuneet.com

Live Match Scoreboard — Free Sports Scoreboard HTML CSS JS

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

CategoryDashboards

Copy & paste

Source Code

<div class="lms-card">
  <div class="lms-status">
    <span class="lms-live-dot"></span>
    <span>LIVE — 2nd Set</span>
  </div>

  <div class="lms-board">
    <div class="lms-team">
      <span class="lms-team-name">Ridgeview Hawks</span>
      <span class="lms-score" id="lmsScoreA">14</span>
    </div>
    <div class="lms-mid">
      <span class="lms-clock" id="lmsClock">18:42</span>
      <span class="lms-sep">SET 2</span>
    </div>
    <div class="lms-team lms-team-b">
      <span class="lms-score" id="lmsScoreB">11</span>
      <span class="lms-team-name">Northfield Otters</span>
    </div>
  </div>

  <div class="lms-sets" id="lmsSets"></div>

  <div class="lms-controls">
    <button type="button" class="lms-btn" id="lmsAddA">+1 Hawks</button>
    <button type="button" class="lms-btn lms-btn-b" id="lmsAddB">+1 Otters</button>
    <button type="button" class="lms-btn lms-btn-ghost" id="lmsAuto">Auto-play</button>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), 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

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.

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 websites
Embed a live score widget with two teams, a pulsing LIVE label and set history pills, with tabular numbers so the layout never jitters.
Esports match overlays
Adapt the layout for round-based competitions, where score changes flash and the match clock runs on its own interval independent of scoring.
School and club scoring
Run simple manual entry for local fixtures, using buttons to change the score and a confetti button for a winning moment.
Tournament pages
Pair with a match bracket tree so a score on one screen maps to progression on the next.
Restart-safe animation reference
Learn the forced-reflow trick that makes the highlight flash restart even when two scores change in quick succession.
Related: Page Visibility API Indicator
See the Page Visibility API Indicator for a related dashboards pattern worth pairing with this one.

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.