NHL Excite‑o‑Meter

Methodology

How Excitement is Calculated

Base event weights

The core weights in exciteo/config.py apply before any context multipliers or decay:

  • Goals: 1.08 points each (before bonuses).
  • High Danger Chances (HDC): 0.42 points.
  • Medium Danger Chances (MDC): 0.28 points.
  • Hits: 0.10 points with an anti-spam guard—roughly eight hits per simulated minute get full value, then contributions drop to 25% via HITS_OVERSAT_SCALE.
  • Expected Goals (xG): 0.28 × the xG proxy derived from shot distance and type.

Shot danger buckets follow the backend geometry helper: ≤20 ft is high danger, 21–35 ft is medium danger, anything deeper is low danger.

Signature moments & bonuses

The scorer rewards key swings on top of the base weights:

  • Short-handed goals: +0.18.
  • Tie/lead change goals: +0.12 when the play pulls even or grabs the lead.
  • Comeback goals: +0.30 for trimming a multi-goal deficit to within striking distance.
  • Lead swing goals: +0.24 once a game has seen at least two lead changes.
  • Fights: 1.20 base + 0.20 per major, +0.20 more for line brawls, with a 50% repeat cut if another fight erupts inside three minutes. A Gaussian “afterglow” can add up to 0.30 in the immediate aftermath.

Context multipliers

Every event is scaled by how tight and dramatic the game state is:

  • Score differential: tied games earn a 1.33× boost, one-goal games 1.22×, two-goal games 1.00×, and anything 3+ goals drops to 0.88×.
  • Late-game tension: inside the last five minutes of the third period, one-goal games stack an additional 1.12× “OT potential” multiplier.
  • Overtime: any OT event is locked to a 1.50× multiplier; shootouts are flagged but capped by the same event limit below.

These multipliers are recorded in the API’s context.multiplierReasons so the UI can explain why a game is surging.

Game-level modifiers

After all events are summed, the backend applies team-level adjustments:

  • Blowout penalty: no reduction through a two-goal gap, 0.66× at a three-goal margin, and 0.35× at four or more.
  • High-scoring boost: combined goal totals unlock multipliers of 1.35 (≥5.5), 1.45 (≥6.0), 1.55 (≥6.5), 1.64 (≥7.0), and 1.72 (≥7.5) so barn burners pop.
  • Ice tilt penalty: when one team controls more than 65% of high/medium danger looks, multipliers step down (0.90 at 65%, 0.75 at 70%, 0.60 at 85%).
  • Low-action penalty: if goals + danger chances + hits stay below 15 events after the first intermission, a 0.90× dampener reflects a sleepy game.

Recency decay & event cap

Events are spaced at roughly six seconds per play. The scorer uses a five-minute half-life (300 seconds) exponential decay so recent chaos matters most, and every event is capped at 4.0 points after context but before decay to prevent outliers from blowing up the total.

Categories

Raw excitement scores map to the same tiers the API returns:

  • Meh 0 – 5.0
  • Heating Up 5.0 – 10.5
  • Sizzling 10.5 – 17.5
  • Barn Burner 17.5+

Preview model

The PreviewModel in the backend re-scores up to the last 15 completed regular-season/playoff games for each team (1 ÷ game-order weighting so fresh games count most). That weighted excitement total is compared to a 9.0 baseline, clipped between 0.90× and 1.10×, averaged across both clubs, and scaled by 0.45 to fit the live score range.

Recent form metrics (goals, HDC, MDC, hits, xG) reuse the same weights to build a projected excitement contribution, plus storyline hooks:

  • High scoring: combined recent goals ≥5.7.
  • Chance barrage: combined high-danger chances ≥40.
  • Hits frenzy: combined hits ≥50.
  • Low-event pace: combined goals ≤2.5.

The preview output also inherits the blowout guard: if recent goal averages imply a lopsided matchup, the expected score gets down-weighted by the same penalty curve used for live games.

Live & final storytelling

Responses from /excitement/<gamePk> carry more than a single number. The backend packages highlight and detractor badges (short-handed tallies, comeback surges, lead swings, fights, high scoring, ice tilt, low-action stretches), the running totals for each team, and the exact multipliers that shaped the raw score so the frontend can surface the same narrative as the API.