The math behind the NBA playoff odds, and exactly what is — and is not — modelled.
Every update plays the rest of the regular season 100,000 times. Each unplayed game is decided by a win probability from the two teams' Elo ratings; games that have already been played are fixed at their real result. At the end of each simulated season the 15 teams in each conference are ordered with the official tiebreakers, the Play-In Tournament is played for ranks 7-10, and the fixed best-of-seven bracket is played through the NBA Finals. Every number on the site — playoff, top-6, play-in, division, #1-seed and title odds, finish distributions, what-if tables, game scenarios — is a count over that same pool.
Nothing is smoothed or carried over between updates. Each run is an independent 100,000-season draw from the current standings, the remaining schedule and today's ratings.
Until the first game is final the site is in preseason mode: every percentage is a projection from last season's ratings regressed toward the mean, the page says so at the top, and there are no standings, seeds, games behind, clinch or elimination flags — because none exist yet. Two of each team's 82 games depend on the NBA Cup and are published by the league only in December; they are excluded (not invented) until then, and projected records say how many games they cover.
Each team has one rating. The season opens at a prior — last season's final Elo regressed halfway toward the league average of 1500 (the chain runs from 2015-16) — then moves after every game:
P(home wins) = 1 / (1 + 10^(−(elo_home + 40 − elo_away) / 400))
new_elo = elo + K × (result − P), with result = 1 / 0 for win / loss, K = 24, and a +40 Elo home-court bonus (zero at neutral sites such as the NBA Cup semifinals in Las Vegas). The published rating can also blend the prior in explicitly with a weight that decays with games played, as BaseChaser does with its WAR prior; on NBA data that weight measured best at zero, so the prior's influence decays only through K.
Strength is uncertain, and the simulation says so. A rating is an estimate with a standard error, and that error is the same in every one of the team's remaining games — it does not average out over a season the way per-game luck does. So once per simulated season every team's true strength is drawn from a normal distribution around its rating (standard deviation 80 Elo, mean-preserving so each single game keeps its calibrated probability), and the whole remaining schedule is played with that draw.
None of these numbers were chosen by hand. K, home court, the regression fraction and the prior weight were tuned jointly — all 2,048 combinations — on every regular-season game from 2017-18 to 2025-26 (10,614 games) by log-loss, with the choice validated out of sample (fit on 2017-18 to 2021-22, scored on 2022-23 to 2025-26, and the reverse: each fold's optimum scores within 0.0003 of the other fold's own optimum). The strength uncertainty was chosen by replaying the published playoff odds at 14-day checkpoints through the five play-in-era seasons 2021-22 to 2025-26 (1,950 team-checkpoints): with no uncertainty, teams shown at 10-20% in the first four weeks made the playoffs 46% of the time and teams at 95-100% only 85%; at 80 Elo those buckets come in at 17% and 100%, and the log-loss of the published odds falls from 0.383 to 0.343. The grids are reproducible with backend/tune_elo.py and backend/backtest_odds.py.
No margin-of-victory multiplier, no rest/back-to-back adjustment and no injury or roster adjustment. A team that loses its star is not re-rated until its results say so.
Every simulated season carries a full head-to-head result matrix, so ranking does not just compare win totals — it runs the league's published procedure. What is implemented, in order:
| Situation | Steps applied |
|---|---|
| Two teams | 1 head-to-head → 2 division winner (regardless of division) → 3 division record (same division only) → 4 conference record → 5 record vs. playoff-eligible teams in own conference → 6 record vs. playoff-eligible teams in the other conference → 7 point differential (real standings only) |
| Three or more | 1 division winner → 2 head-to-head among the tied teams → 3 division record (only if all share a division) → 4 conference record → 5 and 6 as above → 7 point differential, with the restart rule: whenever a step separates a team but leaves two or more tied, the survivors restart at step 1 (two-team format when two remain). |
| Division winners | Decided first, per division, with the same cascade minus the division-winner step. |
| Playoff-eligible teams | The top ten of each conference. Deciding who is tenth can itself need the tiebreaker, so the engine uses every team whose winning percentage is at least the tenth-best in its conference (ties at the tenth spot are all included). |
Not implemented: the coin toss. A simulated season carries no scores, so simulated ties that survive step 6 (about 0.06% of seasons) are settled with a per-season random priority, and today's standings do the same below point differential. Nothing beyond these steps is claimed.
Validation. Fed the complete 2025-26 regular season — all 1,230 real results from ESPN — the standings and tiebreaker code produced the published order of all 15 teams in both conferences (East: DET, BOS, NY, CLE, TOR, ATL, PHI, ORL, CHA, MIA, MIL, CHI, BKN, IND, WSH; West: OKC, SA, DEN, LAL, HOU, MIN, PHX, POR, LAC, GS, NO, DAL, MEM, SAC, UTAH), with no coin toss needed, and every team's overall, division, conference, home and road record matched ESPN's. That season included real ties for 5th/6th in the East (TOR/ATL, 46-36), 7th/8th in the East (PHI/ORL, 45-37), 8th/9th in the West (POR/LAC, 42-40), 11th/12th in the West (NO/DAL) and 14th/15th in the West (SAC/UTAH). Ranks 7-10 were verified against who hosted each play-in game, since the league's published seed for those ranks is the post-play-in one.
A team is marked clinched (top six: x; at least the play-in: pi) or eliminated (e) only when the standings make it mathematically certain — a sound, tiebreaker-independent test on win totals in which every rival that could still catch the team is assumed to win the tiebreaker — or when the league's own standings feed flags it. A 99.9% simulation result is never shown as a clinch. Magic numbers are not shown.
| Data | Source |
|---|---|
| Schedule, results, scores | ESPN's public scoreboard API, one call per calendar month; every one of the 1,230 games comes from it. The NBA Cup championship game (which does not count in the standings) and All-Star exhibitions are excluded; postponed games are taken from their make-up date. Nothing is generated. |
| Clinch flags, records | ESPN standings feed (cross-checked against the records computed from results). |
| Elo prior and backtests | ESPN results for the 2015-16 to 2025-26 seasons. |
| Team logos | ESPN CDN. |
| Tiebreaking procedure | The NBA's published tiebreaker basis (the standings note), cross-checked against the summary on Wikipedia. |
ESPN's API is unofficial and undocumented; if it changes, updates pause until the ingest is repaired.