Paper
Methodology

Expected goals (xG)

Expected goals (xG) puts a number on every shot: how often a shot like that goes in, from where and how it was taken. It's a standard model, built from published methods and fit on the NHL's own data.

On a season it never saw, it sorted dangerous shots from harmless ones a little less sharply than the best public models, and it expected about 5% too many goals. It describes the shot, not the shooter or the goalie.

How it is built

I fit it on about 750,000 shots since 2019-20. Blocked shots don't get an xG, because the NHL records where the shot was blocked, not where it was taken from. A player's xGF and xGA add up the xG of every shot for and against while he's on the ice.

What it looks at:

  • where the shot came from: distance and angle, and whether it was from behind the goal line;
  • the shot type: wrist, slap, tip-in and so on;
  • a rebound (his team shot in the 3 seconds before), and how far the goalie had to move across for it;
  • a rush (the play before was outside the attacking zone, within 4 seconds);
  • off-wing shots from the faceoff circles, the closest the play-by-play gets to a one-timer, since it doesn't record passes;
  • the score, home or away, and overtime.

There's one model each for even strength, the power play, short-handed and shots into an empty net. What is borrowed, and what is new says whose idea each part is.

Season adjustments. Shots logged inside 10 feet went from 8% of all shots in 2019-20 to about 14% in 2025-26, but those shots went in less often (18% down to 13%). My best guess is that's mostly a change in how scorers log where shots come from, not in how teams play, though the game may have changed some too. So each season gets its own small adjustment. Without it, the model expects about 7% too many goals in recent seasons.

The nitty-gritty

Each of the four models is a logistic regression, fit by Newton's method with a ridge penalty (λ = 1) on every term but the baseline. Distance and angle are bent lines (they can change slope at set distances and angles), plus their product. A rebound is any attempt by the same team in the 3 seconds before, a blocked one included. The rush is the play before in the neutral zone or the shooter's own, within 4 seconds. The shooter's hand comes from his NHL.com player page. A season's adjustment is a level plus two terms, for shots inside 10 ft and at 10–20 ft.

SeasonShots inside 10 ftTheir goal rate
2019-208.2%18.1%
2020-218.6%18.2%
2021-229.5%16.8%
2022-2311.8%15.2%
2023-2412.5%14.5%
2024-2512.3%14.1%
2025-2614.5%13.3%
2026-2714.5%13.5%

How well it works

To test it, I fit the model on every season through 2024-25 and had it predict 2025-26 cold. It's good at telling dangerous shots from harmless ones: given a goal and a shot that missed or was saved, it rates the goal higher 76% of the time at even strength (a coin flip would get 50%). That's a little behind the best public models, which know more about the play before each shot.

It ran hot, though: it expected about 8,500 goals and there were 8,086, about 5% too many. The miss was bigger on the power play (about 10% too many) and short-handed (about 14% too many). Part of it is a new season borrowing last season's adjustments before it has games enough for its own. Getting a new season right early is the main thing I'm still working on.

Every day, a check compares real goals with xG and writes a warning to a log I read when they drift apart. I plan to refit after each regular season ends, starting in April 2027, and I won't ship a refit that does worse on a season it hasn't seen.

The nitty-gritty

After the test, the model on the site was refit on every season, the test season included, so each season has its own adjustments. The daily check compares goals with xG by strength and distance.

The test season was scored with 2024-25's adjustments, since a season can't have its own until it's been played. AUC is the chance a goal was rated above a non-goal (0.5 is a coin flip). Log loss scores the probabilities themselves (lower is better), set beside a model that gives every shot the average rate.

ShotsCountGoalsxG
Even strength92,4365,8155,995
Power play16,1731,5871,741
Short-handed2,490176201
Into an empty net992508559
Every shot112,0918,0868,496
ShotsAUCLog lossAverage-rate log loss
Even strength0.7560.20980.2349
Power play0.6490.30900.3209
Short-handed0.8090.20990.2554
Into an empty net0.7520.58350.6929
Every shot0.7570.22740.2591

Goals against xG on the test season, by distance (1.00 is spot on; under 1 means it expected too many):

DistanceShotsGoals ÷ xG
0-10 ft16,2410.93
10-20 ft18,6570.93
20-30 ft17,0730.98
30-45 ft28,1971.00
45+ ft31,9230.92

What each part is worth

I test each new idea the same way: add it on its own and see whether it predicts 2025-26 better. Two made the cut: a rebound's change of angle and off-wing shots from the faceoff circles. The shot before, and four ways of handling a pulled goalie, didn't help. One that did, updating a new season's adjustments every week, is waiting on work I haven't done yet.

The nitty-gritty

Each idea is added to the model as it stood before (version 1), fit through 2024-25. I keep an idea only if it clearly helped (its whole 95% range below zero) and isn't just a weaker version of something else. The faceoff circles on their own helped too, but they're part of the off-wing idea, so they're here only as a check on it. The weekly season adjustments helped and aren't decided yet.

The number is the change in log loss per shot, in ten-thousandths (below zero is better). Log loss scores the probabilities themselves: lower means the chances it gave were closer to what happened. The range is a 95% interval from redrawing whole games at random. For scale, the whole model sits 311 ten-thousandths below one that gives every shot the average rate.

IdeaVerdictLog loss changeAUC
The model before (version 1)Baseline00.755
A rebound's change of angle from the shot beforeKept−4.4−5.6 to −3.20.757
Off-wing shots from the faceoff circles (the shooter's hand)Kept−1.6−2.9 to −0.90.755
The faceoff circles alone, no hand (a check on the row above)Left out−0.6−1.3 to −0.40.755
The shot before: seconds since, which team, how far awayLeft out+0.5−0.1 to +0.80.755
A pulled goalie's extra attackerLeft out−0.2−0.5 to +0.20.755
A late pull apart from other pulls (mostly delayed penalties)Left out−0.2−0.4 to +0.30.755
That, plus strength inside the empty-net modelLeft out−0.1−0.3 to +0.30.755
A separate model for shots with the shooter's goalie pulledLeft out−0.3−0.7 to +0.20.755
A new season's terms re-fitted weekly from its own shotsNot decided−0.8−1.3 to −0.50.755
Both kept ideas together: the model on this siteKept−6.0−7.7 to −5.20.757

By group: EV even strength, PP power play, SH short-handed, EN into an empty net. The power play and short-handed groups are small, so their ranges are wide.

IdeaEVPPSHEN
A rebound's change of angle from the shot before−3.9−8.3−1.5+5.5
Off-wing shots from the faceoff circles (the shooter's hand)−1.8+0.7−3.1−18.5
The faceoff circles alone, no hand (a check on the row above)−0.6−0.2−0.2−15.8
The shot before: seconds since, which team, how far away+0.6+1.0+0.5−12.1
A pulled goalie's extra attacker−0.3+0.1+1.10.0
A late pull apart from other pulls (mostly delayed penalties)−0.3+0.1+1.50.0
That, plus strength inside the empty-net model−0.3+0.1+1.5+4.2
A separate model for shots with the shooter's goalie pulled−0.3+0.2−1.90.0
A new season's terms re-fitted weekly from its own shots−0.3−3.0−0.9−9.9
Both kept ideas together: the model on this site−5.7−7.7−4.3−13.2

Pulled goalies. When a team pulls its own goalie for an extra attacker (late in a game, or during a delayed penalty), its shots go through the even-strength, power-play and short-handed models with nothing to mark them. Every fix I tried overshot on the test season. Goals against xG for those shots, all 2,719 of them:

ModelGoalsxGGoals ÷ xG
The model before2172041.06
The model on this site2172061.05
With an extra-attacker term2172370.92
With late and other pulls apart2172360.92
A separate model for them2172420.90

The fitted model

Every coefficient, for anyone who wants to check the math or rebuild the model.

The coefficients

Model version 2, fit on 2026-11-09. Coefficients are on the log-odds scale (positive raises the chance of a goal). A shot's xG is 1 ÷ (1 + e−z), where z is the baseline plus each coefficient times its term, plus its season's adjustment.

One column per model: EV even strength, PP power play, SH short-handed, EN into an empty net.

TermEVPPSHEN
Baseline0.580-0.285-0.6751.546
Distance to the net (per 30 ft)-8.417-4.651-1.179-1.104
… beyond 5 ft4.2730.818-0.990-1.002
… beyond 10 ft2.8503.1430.962-0.286
… beyond 15 ft0.7370.3150.4210.228
… beyond 20 ft-0.538-0.556-0.1290.466
… beyond 30 ft-1.541-0.650-1.2160.728
… beyond 40 ft1.1930.2590.554-0.496
… beyond 60 ft0.6231.662-0.4470.705
Angle off the centre line (per 45°)0.1040.3090.387-0.828
… beyond 20°-0.172-0.1150.031-1.238
… beyond 35°-0.537-0.036-1.071-0.023
… beyond 50°-0.991-0.621-0.728-0.737
… beyond 65°0.930-0.2590.3960.074
Distance × angle-0.567-0.531-1.2220.382
From behind the goal line0.2610.1860.9570.171
Snap shot (against a wrist shot)0.2830.3840.401-0.108
Slap shot0.4620.3810.626-0.222
Backhand-0.433-0.331-0.125-0.687
Tip-in-0.706-0.206-0.726-1.352
Deflection-0.559-0.187-0.765-0.454
Wrap-around-1.160-0.809-0.5820.335
Other types (poke, bat, between the legs, cradle)-0.436-0.306-0.747-0.362
Rebound: his team's attempt in the 3 s before-0.246-0.088-0.214-0.340
Rebound × angle0.001-0.164-0.0060.035
Rush: the event before was outside the attacking zone, within 4 s0.0890.0830.286-0.240
His team leading0.0730.0580.0542.370
His team trailing0.0000.003-0.059-0.829
Home team shooting-0.0120.018-0.090-0.023
Overtime0.4150.262-0.2440.333
Rebound: change of angle from his team's shot before (per 45°)0.4530.5570.515-0.251
Rebound after a blocked (or unlocated) attempt0.1930.443-0.015-0.338
From a faceoff circle-0.106-0.1350.002-0.013
Faceoff circle × slap or snap shot0.045-0.050-0.1570.663
From his off wing (a left shot on the right side, or the reverse)-0.051-0.089-0.2950.026
Off wing × faceoff circle0.1090.2580.309-0.106
Off wing × faceoff circle × slap or snap (a one-timer, as near as the feed can tell)0.3690.1660.7160.623

Even-strength season adjustments (added to the above for a shot in that season):

SeasonLevelInside 10 ft10–20 ft
2019-20-0.0780.2250.116
2020-21-0.0610.1820.061
2021-22-0.0360.0450.005
2022-23-0.035-0.053-0.041
2023-240.033-0.1970.009
2024-250.038-0.283-0.011
2025-260.051-0.362-0.103
2026-270.087-0.406-0.123

What is borrowed, and what is new

Expected goals is twenty years of other people's work, and mine is built from it. Here's whose idea each part is, and where I did something different. I took ideas only: no coefficient, number or line of code comes from any of these works.

It also leaves out things other models use: who took the shot, who was in net, and the play just before the shot in detail. That's probably most of why it's a little less sharp than the best of them.

Notes on each part, and what I left out
  • A logistic regression on how and where the shot was takenThe approach expected goals began with. Paper Puck's inputs are close to the 2012 list.
  • Distance and angle, as bent lines, with their productThe inputs are standard. The bends (at set distances and angles) and the product term are Paper Puck's choice of form.
  • Rebound: the same team's attempt in the 3 s beforeMagnus 6's definition; the shot before may be any attempt, a blocked one included. The rebound × angle term follows the 2012 angle-on-rebound interaction.
  • A rebound's change of angle (the goalie's "push")Krzywicki measured the push in 2010 and left it out of his model as too close to the rebound flag. Here it is the angle from the same team's shot before, when that came within the rebound's 3 s, and a separate term for a rebound off a blocked attempt, which has no shot location. On a season the model never saw it was the clearest gain of everything tried.
  • Off-wing shots from the faceoff circlesMoneyPuck compares the shooter's hand with the side he shoots from. Paper Puck narrows it to the faceoff circles, and most of all to slap and snap shots there, as a stand-in for the one-timer the play-by-play cannot see (it records no passes); an off-wing term everywhere was worth little. The hand comes from the shooter's NHL.com player page.
  • Rush: the event before outside the attacking zone, within 4 sMagnus 6's definition (the event before in another zone, within 4 s): here, the neutral or the shooter's defensive zone.
  • One model each for even strength, power play, short-handed and empty netEvolving-Hockey's split. One difference: an extra attacker for a pulled goalie counts as even strength here, where theirs counts 6v5 and 6v4 as a power play.
  • Unblocked shots onlyA blocked shot's recorded location is where it was blocked, not where it was taken.
  • A ridge penalty on the fitMagnus fits a generalised ridge logistic regression with shooter, goalie and location-grid terms. Paper Puck's is a plain ridge (λ = 1) on 37 terms.
  • Score, home and overtime termsScore and home terms are in the 2012 model and Evolving-Hockey's. Overtime as its own term is Paper Puck's choice; Evolving-Hockey reads the period.
  • Tested on a season it never sawFitted through one season, scored on the next, as the 2005 model and Evolving-Hockey's were.
  • Per-season terms for drift in recorded shot locationsCorrecting for how scorers record shots is old; it has been done by rink, by rink and season, and by refitting each season. Paper Puck's form, one level and two close-range terms per season inside a single fit over every season, for drift across the whole league, is the one part we have not found published. Not found is not the same as first.
  • The fit, the counting and the publishingThe solver is written here, every shot is scored as the on-ice counter counts it, and every coefficient and the held-out test are published on this page. Only the NHL's own data goes in. Engineering, not a new idea.

What other models use that mine leaves out:

  • The event before each shot in detail (its type, place and time)Reduced to the rebound and rush flags and a rebound's change of angle. The shot before (how long ago, which team, how far away) was tested on a season the model never saw and added nothing; the event's kind (a faceoff, a hit, a giveaway) is not tested yet.From Younggren & Younggren 2018, Tanner, MoneyPuck
  • Who shot and who was in netPaper Puck's xG describes the shot, not the shooter.From McCurdy, Magnus 6 2022
  • A term for a pulled goalie's extra attackerTested four ways (one term; late pulls apart from the rest; strength in the empty-net model; a separate model). None beat the model on a season it never saw, and each overshot those shots. Evolving-Hockey's split counts 6v5 and 6v4 as a power play instead.From Younggren & Younggren 2018
  • Rink-by-rink scorer adjustmentNot done. The season terms correct drift across the league, not differences between rinks.From Krzywicki 2009, McCurdy 2020
  • Discounting later shots in a flurryNot tried yet.From Tanner, MoneyPuck