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.
| Season | Shots inside 10 ft | Their goal rate |
|---|---|---|
| 2019-20 | 8.2% | 18.1% |
| 2020-21 | 8.6% | 18.2% |
| 2021-22 | 9.5% | 16.8% |
| 2022-23 | 11.8% | 15.2% |
| 2023-24 | 12.5% | 14.5% |
| 2024-25 | 12.3% | 14.1% |
| 2025-26 | 14.5% | 13.3% |
| 2026-27 | 14.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.
| Shots | Count | Goals | xG |
|---|---|---|---|
| Even strength | 92,436 | 5,815 | 5,995 |
| Power play | 16,173 | 1,587 | 1,741 |
| Short-handed | 2,490 | 176 | 201 |
| Into an empty net | 992 | 508 | 559 |
| Every shot | 112,091 | 8,086 | 8,496 |
| Shots | AUC | Log loss | Average-rate log loss |
|---|---|---|---|
| Even strength | 0.756 | 0.2098 | 0.2349 |
| Power play | 0.649 | 0.3090 | 0.3209 |
| Short-handed | 0.809 | 0.2099 | 0.2554 |
| Into an empty net | 0.752 | 0.5835 | 0.6929 |
| Every shot | 0.757 | 0.2274 | 0.2591 |
Goals against xG on the test season, by distance (1.00 is spot on; under 1 means it expected too many):
| Distance | Shots | Goals ÷ xG |
|---|---|---|
| 0-10 ft | 16,241 | 0.93 |
| 10-20 ft | 18,657 | 0.93 |
| 20-30 ft | 17,073 | 0.98 |
| 30-45 ft | 28,197 | 1.00 |
| 45+ ft | 31,923 | 0.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.
| Idea | Verdict | Log loss change | AUC |
|---|---|---|---|
| The model before (version 1) | Baseline | 0 | 0.755 |
| A rebound's change of angle from the shot before | Kept | −4.4−5.6 to −3.2 | 0.757 |
| Off-wing shots from the faceoff circles (the shooter's hand) | Kept | −1.6−2.9 to −0.9 | 0.755 |
| The faceoff circles alone, no hand (a check on the row above) | Left out | −0.6−1.3 to −0.4 | 0.755 |
| The shot before: seconds since, which team, how far away | Left out | +0.5−0.1 to +0.8 | 0.755 |
| A pulled goalie's extra attacker | Left out | −0.2−0.5 to +0.2 | 0.755 |
| A late pull apart from other pulls (mostly delayed penalties) | Left out | −0.2−0.4 to +0.3 | 0.755 |
| That, plus strength inside the empty-net model | Left out | −0.1−0.3 to +0.3 | 0.755 |
| A separate model for shots with the shooter's goalie pulled | Left out | −0.3−0.7 to +0.2 | 0.755 |
| A new season's terms re-fitted weekly from its own shots | Not decided | −0.8−1.3 to −0.5 | 0.755 |
| Both kept ideas together: the model on this site | Kept | −6.0−7.7 to −5.2 | 0.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.
The same change in log loss by group. The power-play, short-handed and empty-net groups are small, so their ranges are wide.
| Idea | EVEven strength | PPPower play | SHShort-handed | ENInto an empty net |
|---|---|---|---|---|
| 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.1 | 0.0 |
| A late pull apart from other pulls (mostly delayed penalties) | −0.3 | +0.1 | +1.5 | 0.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.9 | 0.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:
| Model | Goals | xG | Goals ÷ xG |
|---|---|---|---|
| The model before | 217 | 204 | 1.06 |
| The model on this site | 217 | 206 | 1.05 |
| With an extra-attacker term | 217 | 237 | 0.92 |
| With late and other pulls apart | 217 | 236 | 0.92 |
| A separate model for them | 217 | 242 | 0.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.
| Term | EVEven strength | PPPower play | SHShort-handed | ENInto an empty net |
|---|---|---|---|---|
| Baseline | 0.580 | -0.285 | -0.675 | 1.546 |
| Distance to the net (per 30 ft) | -8.417 | -4.651 | -1.179 | -1.104 |
| … beyond 5 ft | 4.273 | 0.818 | -0.990 | -1.002 |
| … beyond 10 ft | 2.850 | 3.143 | 0.962 | -0.286 |
| … beyond 15 ft | 0.737 | 0.315 | 0.421 | 0.228 |
| … beyond 20 ft | -0.538 | -0.556 | -0.129 | 0.466 |
| … beyond 30 ft | -1.541 | -0.650 | -1.216 | 0.728 |
| … beyond 40 ft | 1.193 | 0.259 | 0.554 | -0.496 |
| … beyond 60 ft | 0.623 | 1.662 | -0.447 | 0.705 |
| Angle off the centre line (per 45°) | 0.104 | 0.309 | 0.387 | -0.828 |
| … beyond 20° | -0.172 | -0.115 | 0.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.259 | 0.396 | 0.074 |
| Distance × angle | -0.567 | -0.531 | -1.222 | 0.382 |
| From behind the goal line | 0.261 | 0.186 | 0.957 | 0.171 |
| Snap shot (against a wrist shot) | 0.283 | 0.384 | 0.401 | -0.108 |
| Slap shot | 0.462 | 0.381 | 0.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.582 | 0.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 × angle | 0.001 | -0.164 | -0.006 | 0.035 |
| Rush: the event before was outside the attacking zone, within 4 s | 0.089 | 0.083 | 0.286 | -0.240 |
| His team leading | 0.073 | 0.058 | 0.054 | 2.370 |
| His team trailing | 0.000 | 0.003 | -0.059 | -0.829 |
| Home team shooting | -0.012 | 0.018 | -0.090 | -0.023 |
| Overtime | 0.415 | 0.262 | -0.244 | 0.333 |
| Rebound: change of angle from his team's shot before (per 45°) | 0.453 | 0.557 | 0.515 | -0.251 |
| Rebound after a blocked (or unlocated) attempt | 0.193 | 0.443 | -0.015 | -0.338 |
| From a faceoff circle | -0.106 | -0.135 | 0.002 | -0.013 |
| Faceoff circle × slap or snap shot | 0.045 | -0.050 | -0.157 | 0.663 |
| From his off wing (a left shot on the right side, or the reverse) | -0.051 | -0.089 | -0.295 | 0.026 |
| Off wing × faceoff circle | 0.109 | 0.258 | 0.309 | -0.106 |
| Off wing × faceoff circle × slap or snap (a one-timer, as near as the feed can tell) | 0.369 | 0.166 | 0.716 | 0.623 |
Even-strength season adjustments (added to the above for a shot in that season):
| Season | Level | Inside 10 ft | 10–20 ft |
|---|---|---|---|
| 2019-20 | -0.078 | 0.225 | 0.116 |
| 2020-21 | -0.061 | 0.182 | 0.061 |
| 2021-22 | -0.036 | 0.045 | 0.005 |
| 2022-23 | -0.035 | -0.053 | -0.041 |
| 2023-24 | 0.033 | -0.197 | 0.009 |
| 2024-25 | 0.038 | -0.283 | -0.011 |
| 2025-26 | 0.051 | -0.362 | -0.103 |
| 2026-27 | 0.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.
- A logistic regression on how and where the shot was taken Standard practiceFrom Ryder 2004, Krzywicki 2005, Macdonald et al. 2012
- Distance and angle, as bent lines, with their product Standard practiceFrom Krzywicki 2010, Macdonald et al. 2012
- Rebound: the same team's attempt in the 3 s before BorrowedFrom Macdonald et al. 2012, McCurdy, Magnus 6 2022
- A rebound's change of angle (the goalie's "push") BorrowedFrom Krzywicki 2010
- Off-wing shots from the faceoff circles AdaptedFrom Tanner, MoneyPuck
- Rush: the event before outside the attacking zone, within 4 s BorrowedFrom McCurdy, Magnus 6 2022
- One model each for even strength, power play, short-handed and empty net BorrowedFrom Younggren & Younggren 2018
- Unblocked shots only Standard practiceFrom Younggren & Younggren 2018
- A ridge penalty on the fit BorrowedFrom McCurdy, Magnus 6 2022
- Score, home and overtime terms Standard practiceFrom Macdonald et al. 2012, Younggren & Younggren 2018
- Tested on a season it never saw Standard practiceFrom Krzywicki 2005, Younggren & Younggren 2018
- Per-season terms for drift in recorded shot locations AdaptedFrom Ryder 2007, Krzywicki 2009, Schuckers & Macdonald 2014, McCurdy 2020, McCurdy, Magnus 6 2022, Tanner, MoneyPuck
- The fit, the counting and the publishing Paper Puck's own
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