Skip to main content
Accès anticipé— LegendsTracker est en développement actif.

Technical documentation

Our methodology

At LegendsTracker, every number has a formula. This page documents all of our algorithms, our data sources, our limitations and our statistical choices. Full transparency.

Our data

All of our data comes from the official Riot Games API. We use three main endpoints:

  • match-v5: full data for every game (timeline, gold, damage, objectives, events)
  • spectator-v5: real-time data for live games
  • league-v4: ranked ladders, LP, ranks

10

Riot servers

173

analyzed (173 curated)

Every

patch

100%

Riot API

Honesty about our sample: LegendsTracker is an independent project. Our database holds roughly 800 to 1,200 games per champion per patch, where sites like LoLalytics or U.GG aggregate 500,000+. We compensate with statistical methods suited to that scale (Bayesian smoothing), but our winrates are naturally more volatile. We would rather be transparent than claim a precision we don't have.

Player DNA: 35 features × 13 archetypes

The Player DNA is our unified profiling system. It extracts 35 behavioral features from your last 30 ranked games (grouped into 6 chromosomes: Combat, Macro, Mechanics, Mental, Team, Tempo) and classifies you into one of 13 archetypes, all hand-validated (Sniper, Berserker, Architect, Ghost, Showman, Guardian, Coinflip, Tyrant, Rat, Predator, Phoenix, Wall, Patient).

Each feature is z-scored (deviation from the global mean in σ), then we identify your 3 signatures (recurring strengths, top-3 positive σ) and your block (recurring weakness, most negative σ). The archetype is assigned by rule-based scoring: score = mean(z[positive keys]) − mean(z[negative keys]) (a mean, not a sum — so archetypes with 3 or 5 keys stay comparable). Plan A (HDBSCAN centroids on 100k EUW vectors) sits in the Sprint 4 backlog.

Transparency — current limits of the Player DNA

The Player DNA z-score normalization is currently computed against a global distribution, not yet per (rank × role) bucket: the current corpus (storage capacity and the dev's own skill level) is too thin for reliable buckets. Rank × role calibration is in progress, alongside the HDBSCAN training on 100k EUW vectors (Plan A, sprint 4) that will replace the current rule-based scoring.

How a metric becomes a score

Every raw metric (CS/min, vision, damage…) is converted into a 0-100 score through linear interpolation against a rank-calibrated benchmark, then adjusted by a role multiplier. This is the machinery that powers the detailed match grade (next section) and the Player DNA z-scoring.

score = ((value - lower_bound) / (upper_bound - lower_bound)) × 100

The score is then clamped between 0 and 100.

Benchmarks by rank

The bounds are calibrated on real data extracted on March 23, 2026 from our database (830k+ ranked games, EUW1). Manual refresh on major patches — a weekly scheduler is on the roadmap:

RankCS/minKDAKP%DPMGold/minDeaths/game
Iron4.92.437%5443507.5
Bronze5.83.445%6984016.5
Silver6.63.546%8094346.4
Gold6.83.546%7394286.4
Platinum7.13.546%7634386.4
Emerald7.63.647%7744546.2
Diamond7.63.648%7604526.0
Master7.73.648%7314485.8
Grandmaster7.93.949%7624575.5
Challenger8.14.250%7984665.3

Role multipliers

Benchmarks are adjusted per role to compare a support and an ADC fairly:

RoleCSVisionDPMObjectivesKP
Support×0.25×2.0×0.55×0.4×1.30
Jungle×0.75×1.3×0.70×1.5×1.15
Top×1.0×0.8×0.90×0.8×0.90
Mid×1.0×0.9×1.0×0.7×1.05
Bot (ADC)×1.1×0.8×1.1×0.9×1.10

Grading system

Every match gets an overall grade (S, A, B, C, D) based on 6 weighted categories. The grade is computed relative to your rank, not in absolute terms: a B in Diamond is far better than an A in Bronze.

The 6 categories

CategoryWeightMain metrics
Teamfight25%DPM, kill participation, damage share*
Laning20%CS@15, gold diff@15, XP diff
Survival18%Deaths (inverted), KDA, low-death bonus
Income13%Gold/min, CS/min
Vision12%Vision score, wards destroyed
Objectives12%Objective damage/min, tower kills

* The damage share is role-adjusted: the reference 8-35% range is multiplied by the role played (UTILITY × 0.45 → 3.6-15.75% range, BOTTOM × 1.15 → 9.2-40.25%, etc.). Before this fix, a tank support at 10% damage share was capped around 20/100 on this component (25% of Teamfight) no matter what — now a support at 12% scores the same as an ADC at 28%.

⚠️ These ranges are reachable targets per role, not an additive breakdown: the per-role medians don't sum to 100%. A team's damage share sums to 100% by construction (5 players split 100% of the damage), but each role has a different envelope for what it's expected to weigh individually.

Category weights per role

The 6 weights above are the laner weights (Top / Jungle / Mid / ADC). A support structurally has neither income (gold/CS) nor objective last-hits: judging them on that tanks their grade no matter what. The UTILITY role therefore redistributes those weights toward what a support actually controls.

CategoryLanersSupport
Teamfight25%28%
Laning20%15%
Survival18%20%
Income13%5%
Vision12%30%
Objectives12%2%

Objectives & towers — community feedback (2026-05-30): in the Objectives category, personal tower takedowns only weigh 0.05 for a support (vs 0.35 for a laner). Leaving plate gold to your ADC is the right play — it's no longer penalized. Your objective damage (auto-attacking drake/baron) already captures your real contribution, and the whole category only weighs 2% of your support grade.

How to read a grade (post-match)

Since 2026-05-20, every sub-grade (Laning / Combat / Survival / Objectives / Vision / Farm) in the match summary is clickable. Opening the detail shows you:

  • Every sub-metric with your value vs the benchmark for your rank + role and a ▲ / ─ / ▼ status.
  • The sub-metric's normalized 0-100 score (colored mini-bar) and its weight % in the category grade.
  • A marker on the benchmarks adjusted to your role (see the multiplier table above), with a “Calibrated for <role>” banner on top.
  • An “X weighs the most in this grade” bubble pointing at the sub-metric responsible when the grade drags down — handy to know what to work on first.

The general rule: a score under 50 / 100 on a heavily weighted sub-metric almost always explains a C or D grade. Conversely, an S / A on the category is never “luck” — there are at least two sub-metrics above 70.

Grade thresholds

S = 85+
A = 68-84
B = 48-67
C = 28-47
D = < 28

Percentile by rank (interpolated)

The raw 0-100 score is converted into a percentile against players of the same rank via stepwise linear interpolation. That avoids discrete jumps — a 79 score is no longer abruptly at 78% and an 80 at 90%: the function lerps between anchors for a continuous progression.

anchors: 0→5, 20→12, 30→22, 40→35, 50→50, 60→65, 70→78, 80→90, 90→97, 100→99

Impact map (impact within the game)

The Impact map (impactScore, 0-100) measures your real weight in that specific game, not against your rank. It is role-aware:

impactScore = KP_eff × 0.55 + objective_participation × 0.45

For UTILITY, objective participation (a share of team damage — a support does 5-15% of it no matter what) is replaced by vision and presence, which actually measure their presence on the map:

impactScore (UTILITY) = KP_eff × 0.40 + vision_share × 0.30 + assist_density × 0.20 + objective_participation × 0.10

Effective KP — weighted by kill volume (Discord feedback 2026-05-30). Raw KP is a % of the team's total kills: 67% on 3 kills (= 2 takedowns) weighed as much as 67% on 26. For scoring only, we apply a volume factor KP_eff = KP × clamp(team_kills / 10; 0.3; 1). Concretely 67%×0.3 ≈ 20 finally lands below 31%×1 = 31: in a 3-26 stomp, the enemy support at 31% / 26 kills comes out on top. Raw KP is still displayed as-is (67%) — only its internal weight changes.

MVP / LVP

The MVP badge goes to the player with the best globalScore (0-100) among the 10 players. The formula is role-aware:

globalScore = impactScore×0.38 + min(KDA, 8)×7.5 + DPM/maxDPM×18 + Vision/maxVision×12 + CS/maxCS×10 + Gold/maxGold×10 - Deaths×1.5 + 4

For UTILITY, the formula drops CS/Gold (irrelevant for the role) and redistributes toward vision and impact:

globalScore (UTILITY) = impactScore×0.45 + min(KDA, 8)×7.5 + Vision/maxVision×18 + DPM/maxDPM×6 - Deaths×1.5 + 4

The KDA contribution is capped at 8: without that cap, a Perfect KDA (e.g. 6/0/28 → KDA 34) saturated the score at 100 and killed MVP discrimination between Perfect-KDA players. impactScore itself is role-aware (see Impact map above, volume-weighted KP included).

Carry — every role in its own way

  • Damage Carry — laners who carry through kills: kill share ≥ 50% of the team, ≥ 5 kills, in a win.
  • Engage Carry — supports who carry through engage/peel/vision: UTILITY + win + KP ≥ 65% + ≤ 3 deaths + ≥ 1.5 vision/min. Before 2026-05-09, the generic "Carry" badge structurally excluded supports.

“God of X” game badges

Every game also hands out “best of the game” badges on a given metric. Since 2026-05-30, the “God of X” badges require absolute floors on top of being the best — so a 1/3/1 in a stomp doesn't grab “Support God” just for being the only support on their team:

  • Support God — best supportScore of the game, and ≥ 10 assists and ≥ 25 vision.
  • Vision God — best vision score, and ≥ 25 total and ≥ 1.4/min (a long game with mediocre vision no longer cuts it).
  • CS God — best CS/min among laners, ≥ 7 (supports excluded).

Deliberate choice: we keep recognition in defeat. A huge support game, or genuinely elite vision (44 vision in 18 min = 2.4/min), deserves its badge even on the losing team — the wards you placed exist whether you win or not. The supportScore that ranks the “Support God” combines KP, vision, assists minus a per-death penalty: a 0.6 KDA no longer edges out a clean support.

Role Signature: every role has its own KPIs

Overall grades give an average, but a splitpushing Top and a carry ADC don't share the same responsibilities. The Role Signature is a composite score specific to each role, built on 5 KPIs with different weights per role.

Formula: score = Σ(kpi_grade × kpi_weight) with S=95, A=82, B=68, C=52, D=30. The result is a /100 score reflecting what your role is supposed to do.

The 5 signatures

TOP

Side pressure + TF impact

TF impact (KP + dmg share)30%
CS diff vs opponent25%
Splitpush pressure (tower dmg)20%
Tanking (dmg taken % team)15%
CS / min10%
JUNGLE

Tempo · Ganks · Objectives

Kill participation30%
Early objectives (< 15 min)25%
Successful gank rate20%
Objective damage (% team)15%
Camps farmed / min10%
MIDDLE

Damage · Priority · Roams

Share of team damage30%
Kill participation25%
Roams detected20%
CS diff vs opponent15%
CS / min10%
BOTTOM (ADC)

Damage · Farming · Survival

Share of team damage35%
CS / min25%
Deaths (inverted — survival)20%
CS diff vs opponent10%
Damage / gold spent10%
UTILITY (Support)

Vision · Peel · Presence

Vision score / min30%
Kill participation25%
Control wards / min20%
Roam efficiency15%
Tanking (engage/peel)10%

Support — sub-archetype weighting: the 5 UTILITY weights shown above are the default set. Since May 12, the UTILITY role applies 5 distinct weight sets based on the champion's sub-archetype (enchanter, tank, catcher, mage, or default): a catcher Leona is judged mostly on her KP and roams, an enchanter Soraka mostly on her vision and peel.

Concrete example: an ADC at 30% dmg share gets an A (28%+ threshold), and since that weighs 35% of their score, it directly impacts the overall grade. A support with the same stat also gets an A but it weighs 0% in their role (not a KPI) — their vision/min counts 30% instead. Every role is judged on what truly belongs to it.

Death analysis: contextual and role-aware

Every death is tagged and scored from Riot timeline events (positions, nearby allies/enemies, wards, objectives, HP before death, gold diff). The system outputs a 0-100 score and a category (good / acceptable / bad / terrible).

The 13 detected tags

teamfight

Death in a 3v3+, allies nearby

good_trade

Positive trade (enemy kills > ally kills ±10s)

objective_trade

Death for a secured objective

ganked

Killer/assist from another lane (jungler, roam, TP)

solo_killed

Killed in a 1v1 (0 assists, 1 nearby enemy)

caught_out

Alone against 2+ enemies, outside a teamfight

overextend

Enemy territory + 0 nearby allies

no_vision

0 allied wards placed in the 90s window

facecheck

Bush/river without a ward, enemies nearby

pre_objective

Alone, < 2 min before a major spawn

dive_gone_wrong

Near a tower + enemy territory

repeated_death

Same killer 2+ times

low_resource_death

HP < 30% 30s earlier — should have backed

Quality score

Base at 50. Each tag applies a bonus/penalty:

score = 50 + bonuses(tags) - penalties(tags) + phase_modifier(role, phase) + role_bonus
TagPointsExtra condition
objective_trade+30
good_trade+25
teamfight+15
objectiveSecuredNearby+20context bonus
nearbyAllies ≥ 3+10context bonus
caught_out-25
pre_objective-25
overextend-20
facecheck-20
dive_gone_wrong-20
no_vision-15
repeated_death-15
low_resource_death-15
good (≥ 70)
acceptable (45-69)
bad (25-44)
terrible (< 25)

Contextual modifiers

Game phase

  • Early (< 14 min): caught_out -5, solo_killed -3, objective_trade +5 — lane mistakes impact the whole game
  • Mid (14-24 min): neutral
  • Late (≥ 25 min): caught_out -8, teamfight +8, objective_trade +8 — one solo mistake can lose the game

Role bonuses

  • Support: if a low-HP ally is nearby, +10 (peel attempt recognized) — no caught_out / solo_killed tag
  • Jungle: no overextend in late game (invading = the job), early caught_out +3
  • ADC: no overextend in late game side lanes (splitpushing = the job), late +3

Ward detection fix (April 2026): Riot API WARD_PLACED events don't always include the position. We now distinguish confirmed nearby wards (known position + within the 3000 radius) from vision effort (total allied wards placed in the 90s window, position or not). The no_vision tag only fires if zero wards were placed — no more false positives unfairly costing -15 pts.

Lifetime-aware pre-objective vision fix (May 2026): previously, the Wards before objectives metric only counted wards placed within the [drake−2min; drake] window. A Control Ward placed 5 minutes before the drake (typical jungler anticipation) was never counted → the coaching AI wrongly blamed the player for “not warding the drake”. Now, every ward gets a computed lifetime window: Control Ward = ∞ (until killed/replaced), Yellow Trinket = 120s, Sight Ward = 150s, Blue Trinket = 60s. WARD_KILL events are matched by (wardType, position ±150 units) to close the window earlier if the ward was destroyed. The signal sent to the AI Coach goes from a raw counter to qualitative coverage: “X/Y objectives with ≥1 active ward in river/enemy territory 60s before the spawn”.

Progression plan: 100% deterministic, aligned with your scoring

The 14-day progression plan is generated directly from your scoring diagnosis, with no AI call. Before (until April 12, 2026), a Claude call invented the objectives and thresholds — expensive, slow (~15s), and sometimes inconsistent. Now it's generated server-side in pure TypeScript: instant, free, and deterministic.

How it's generated

1. The server fetches your last 30 ranked games from cached_matches

2. The Player DNA computes your scores and identifies your 3 weakest axes (the same building blocks the badge system uses)

3. For each weak axis, we pick 1-2 objective templates from a catalog of 12 curated objectives (cs_per_min, vision_per_min, deaths_per_game, kda, kill_participation, control_wards, etc.)

4. Each objective gets a target aligned with the next rank's benchmark (10-15% above your current level — realistic over 2 weeks)

5. The number of objectives and required games adapts to your coach tier (Iron-Silver: 3 obj × 3 games | Gold-Plat: 4 × 5 | Emerald-Diamond: 4-5 × 5-6 | Master+: 5 × 6)

6. The 14-day daily focus rotates themes across your 3 weak axes (4 concrete tasks per axis, alternating by day)

DNA convergence — eventually (Sprint 6.5), the plan will directly use the DNA's block and prototype gaps to target archetype-aware levers (e.g. “you're a Berserker, your plan prioritizes tilt_resistance”). In the meantime the Player DNA provides the same information in a form compatible with the objective template catalog.

Catalog of 12 objective templates

Each template defines an extractable metric from the match data, a target, and 3 hand-written actionable tips:

Improve your CS/min

cs_per_min
farm

Increase vision score / min

vision_per_min
vision

Reduce your deaths / game

deaths_per_game
survival

Improve your KDA

kda
teamfight

Show up to team events

kill_participation
roaming

Presence at objective fights

kp_objectives
objectives

Increase your DPM

damage_per_min
teamfight

Place more wards

wards_per_game
vision

Buy more control wards

control_wards
vision

Increase your damage share

damage_share
teamfight

Contribute more to neutral objectives

objective_damage
objectives

Optimize your gold/min

gold_per_min
goldEfficiency

Objective auto-validation

Every time you visit your Progression page, the system re-fetches your latest matches and automatically checks each objective via extractMetric():

for (match of last20RankedMatches) { if (extractMetric(match, obj.target.metric) >= obj.target.value) { obj.qualifying_matches.push(match.id) } } if (obj.qualifying_matches.length >= obj.target.games_required) { obj.completed = true }

No need to click to validate — as soon as you play a game that meets the condition (e.g. 8 CS/min), it's automatically counted. You watch your progress update in real time.

Why deterministic vs AI? Before, a Claude call generated the plan in ~15s, cost tokens, and sometimes invented inconsistent thresholds. Now: generation in < 200ms, zero cost, and the thresholds are 100% aligned with the benchmarks the Player DNA already uses. Consistency with the diagnosis is guaranteed.

Skill tree

The Progression tab also shows a micro-skill tree (recall timing, survival, objective focus, wave management, vision…). Since June 2026, each grade is computed server-side from your real timelines: your ~15 latest ranked games run through the same analyzers as the rest of the site (death analysis, wave management, macro, pings) and the aggregate grades each skill on what actually happened in game.

Transparency rule: if a skill is not measurable on your recent games (missing timeline, missing data), it's shown as “unavailable” — never padded with a neutral grade that would fake it. The tree's overall grade only averages the skills that were actually evaluated.

Tier List: Bayesian + Wilson score

With a sample of ~1,000 games per champion, raw winrate is too volatile. A champion at 60% winrate over 80 games isn't necessarily better than one at 52% over 3,000 games.

The small-sample problem

With little data, rarely played champions show extreme winrates (40% or 60%) through pure statistical variance. We combine two complementary estimators: Bayesian smoothing to estimate the true WR, and the Wilson score interval (95% lower bound) to penalize small-sample uncertainty.

1. Bayesian winrate formula

bayesianWR = (games × winrate + C × prior) / (games + C)

C = 150: smoothing constant (the bigger C, the harder we pull toward the prior)

prior = 50%: prior winrate (expected average)

games: number of games on the champion

winrate: observed raw winrate

Concrete example: A champion at 60% WR over 50 games: bayesianWR = (50 × 60 + 150 × 50) / (50 + 150) = 52.5%. Over 500 games: (500 × 60 + 150 × 50) / 650 = 57.7%. The bigger the sample, the less the smoothing matters.

2. Wilson score interval (95% lower bound)

The Bayesian estimates a central point but says nothing about uncertainty. Two champions can share the same bayesianWR with radically different confidence intervals. The Wilson score gives the lower bound of the 95% CI — a champion with 50 games (σ ≈ 7%) mechanically ends up below a champion with 1000 games (σ ≈ 1.6%).

wilsonLower = (p̂ + z²/2n − z·√(p̂(1−p̂)/n + z²/4n²)) / (1 + z²/n)

with z = 1.96 (95% CI), = observed winrate as a fraction, n = games.

3. Final score

Mix of 70% Bayesian (global stability) + 30% Wilson (penalizes uncertainty) + popularity bonus:

score = (bayesianWR × 0.7 + wilsonLower × 0.3) + max(log₂(games / avgGames) × 3, −3)

The bonus rewards popular champions (meta-validated by volume) with no positive cap. The penalty has been floored at −3 points since 2026-05-20: the Bayesian regression already handles small-sample reliability — beyond that, we would double-punish viable pocket picks (a champion at 200 games in a pool averaging 800 took −6, about a full tier on top of the regression).

The Bayesian/Wilson mix stabilizes tiers from patch to patch: “lucky outlier” champions climb less.

Tier assignment

Champions are sorted by descending score, then split by percentile:

S+ : Top 5%
S : Top 15%
A : Top 35%
B : Top 65%
C : Top 85%
D : Rest

Small pools. “Top 5% / 15%” means nothing on a tiny pool: a pool of 5 ranked champions or fewer (typically 24-48h after a patch) gets neither S+ nor S — the best caps at A until the pool reaches 6 champions. We don't manufacture an elite tier out of merely being 1st among a handful of champions.

The minimum threshold is adaptive: between 10 and 50 games to be included, based on the pool's average volume. On a freshly released patch (little data) the threshold drops toward 10 so the list doesn't empty out, then climbs back toward 50 as collection catches up — Bayesian/Wilson pulls small samples toward the 50% prior anyway.

Theoretical rank estimation

The theoretical rank estimates the rank you should be at by crossing your performance with that of other players at your current rank. Since 2026-05-12, it is powered by the Player DNA: we no longer aggregate per-rank thresholds over your averages, we directly read the z-scores the ML service computes across 35 features (see /api/ml/dna).

Algorithm

1.

The DNA service returns, for each feature, a z-score normalized against the pool's global distribution (per-rank × role normalization is being calibrated — see the transparency note above). z=+1 = 1σ above the mean; features that don't apply to the role (roam for jungle/support, wave management for jungle/support) return z=0.

2.

We aggregate a subset of 17 rank-correlated features (mechanics C3, macro C2, mental C4, team C5, and 2 outcomes C1). Playstyle features (early aggression, risk profile, tempo, …) are excluded so an atypical style isn't penalized.

3.

Aggregated Z = Σ(z × weight), with the weighting concentrated on cs_efficiency (15%), damage_per_gold, vision_per_min, objective_priority (10% each) — see the table below.

4.

Tier offset = round(aggregated z ÷ 0.55), clamped to ±3 tiers. 0.55σ ≈ one step (calibrated on the empirical gap between tier means on mechanical features).

5.

Theoretical rank = current rank + offset, clamped between Iron and Challenger.

Weighting (sum = 1.0)

cs_efficiencyMechanics15%
vision_per_minMacro10%
objective_priorityMacro10%
damage_per_goldMechanics10%
multi_kill_rateMechanics8%
tower_pressureMacro6%
anti_gank_awarenessMacro6%
kp_dmg_balanceTeam6%
snowball_conversionOutcomes5%
tilt_resistanceMental4%
post_death_recoveryMental4%
comeback_rateMental4%
hourly_consistencyMental3%
comeback_contributionOutcomes3%
role_expectation_matchTeam2%
death_evennessTeam2%
wave_managementMacro (laners)2%

Confidence score

confidence = min(100, 35 + sampleFactor × 50 + min(1, |aggregated z| ÷ 1.5) × 15)

Below 20 ranked games in the DNA window, no estimate is produced (per-bucket z-scores too noisy). sampleFactor (0 to 1) measures how solid the archetype assignment is — it's not a simple games ratio. Confidence is bounded between 35% (thin sample, soft signal) and 100% (full sample, clear signal > 1.5σ).

Tilt Detector: 7 signals

The Tilt Detector analyzes your recent session (consecutive games < 90 min apart) and detects 7 tilt signals.

Lose streak

Number of consecutive losses

Thresholds: 2 = low, 3 = medium, 4 = high, 5+ = critical

CS decline

CS/min drop between the 1st and 2nd half of the session

Thresholds: 10-15% = low, 15-20% = medium, 20-30% = high, 30%+ = critical

Deaths rising

Rising average deaths

Thresholds: 1.5-2 = low, 2-3 = medium, 3-4 = high, 4+ = critical

Champion switching

Unique champions / games played ratio

Thresholds: 80-90% = medium, 90%+ (5+ games) = high

Queue rage

Fast requeue after a loss (< 2 min)

Thresholds: 1 = medium, 2+ = high, 3+ = critical

Session winrate

Overall session winrate

Thresholds: 15-35% = medium, ≤15% = high, 0% = critical

Shorter games

Dropping game length (earlier FFs)

Thresholds: > 3 min drop = medium, > 6 min = high

Score calculation

tiltScore = Σ(severity_weight) + long_session_bonus

Weights: low = 8, medium = 15, high = 22, critical = 30

Mental fatigue bonus: linear scaling from 4 games — +min(20, round((games − 3) × 2.5)), capped at +20 from 12 games

Cool 😎 (0-19)
Warming 😐 (20-44)
Tilted 😤 (45-69)
Full Tilt 🔥 (70-100)

Badge system

31 badges across 9 categories, each with 4 progressive rarity levels. Thresholds are fixed and absolute (not rank-relative), but three badges are adjusted by main role so every role can unlock them at its natural level: damage_dealer and gold_income are normalized by role multiplier (a support at 330 DPM = an ADC at 660 DPM = Bronze), and cs_machine is locked for UTILITY mains (CS is irrelevant — they already have ward_master, control_ward, ward_killer).

Combat

KDA, KP, Damage, Penta, Quadra, Duelist

Farm

CS/min, Gold/min, Perfect CS, Early Farmer

Vision

Ward Master, Total Control, Ward Hunter

Survival

Survivor, Untouchable, Comeback, Early Survivor

Objectives

Towers, Obj Damage, FB, Dragon, Baron, Plates

Progression

Win Streak, Grinder, WR, One Trick, Phoenix

Learning

First Autopsy, Coached, Matchups, Analyst

Social

Ambassador, Storyteller, Connected, Quests

Special

Pioneer, Supporter, XP Machine

Each badge has 4 tiers: BronzeSilverGoldLegendary. Progress toward the next tier is shown as a percentage.

Team Luck analysis

The system automatically detects problem players (AFKs, inters, smurfs) on each team to gauge the luck factor of your games.

AFK detection

  • Hard AFK: time played < 60% of the game duration
  • Soft AFK: game > 15 min AND gold/min < 120 AND damage < 1,500
  • Semi-AFK: game > 15 min AND gold/min < 180 AND damage < 3,000 AND ≤ 2 deaths (support: tightened thresholds — gold/min < 120 AND damage < 1,800)

Int detection

  • Hard int: ≥ 10 deaths AND damage share < 8% AND KDA < 0.5
  • Soft int: ≥ 8 deaths AND deaths/min > 0.4 AND KDA < 0.8

Smurf detection

  • Suspected smurf: KDA > 2.5× the lobby average AND KDA > 6 AND DPM > 1.5× the average AND (kills ≥ 12 OR kills+assists ≥ 20) — all 4 conditions met
  • Extreme carry: KDA > 8 AND kills ≥ 15 AND DPM > 1.8× the lobby average

Individual perf vs team

How we separate your real contribution from the team's result. A win proves nothing on its own — a player can carry with a 20/0 in a loss, or get carried at 1/8/15 in a stomp. Here's how LegendsTracker attributes performance to the individual rather than the collective scoreboard.

Why 500,000 damage means nothing

Raw numbers (total damage, total gold, total CS) are dependent on game duration and the team's tempo. An ADC at 500k damage over 40 min is worse than an ADC at 300k over 22 min. And a carry at 35% damage share in a loss often played better than a teammate at 40% in a 25-3 win.

Rule: a metric is only comparable if it's put in context. Either normalized per minute (DPM, GPM, CSPM), per team share (damage share, kill participation), or against an adjusted benchmark (role + rank).

Damage share > Damage total

The damage share is your slice of the team's damage. It removes the “longer game = more damage” effect and surfaces your relative contribution.

damage_share = your_damage / Σ(team_damage) × 100
PlayerRaw damageDurationDPMDamage shareVerdict
ADC A (loss)120,00025 min4,80038%Carry ✓
ADC B (win)180,00045 min4,00022%Mid

Player B has more raw damage but plays worse relative to time and team.

Role normalization

Every metric is scaled by the role's expectations. A support at 800 DPM is excellent; an ADC at 800 DPM is a red flag. We use benchmark multipliers to fairly compare roles that aren't playing the same game.

RoleExpected DPMTarget damage shareVision/minCS/min
TOP ~600 22-26% 1.2 7.5
JUNGLE ~550 20-25% 1.4 5.5
MID ~750 25-30% 1.2 7.8
ADC ~800 28-34% 1.1 8.5
SUPPORT~350 10-15% 3.5 0.8

Your adjusted score = raw_metric / role_benchmark × 100. A support at 3.8 vision/min scores 109% on that axis — even with 0 CS.

Rank normalization

Within a single role, expectations vary wildly between Bronze and Master. We apply per-rank benchmarks so an Iron player isn't compared to a Diamond. The same 7.5 CS/min is an A+ in Bronze and a C in Master.

adjusted_score = raw_metric × (Platinum_benchmark / your_rank_benchmark)

Platinum serves as the reference rank (middle of the distribution). Benchmarks are recalibrated on every major patch to reflect meta shifts.

Game context (stomp vs close)

A 10/2/5 KDA in a 30-5 stomp isn't as impressive as a 6/3/12 in a 40-35 close game. We compute a closeness factor to weight performances:

closeness = 1 - |blue_kills - red_kills| / max(blue_kills, red_kills)

Stomp 30-5

closeness ≈ 0.17

Perfs toned down (easy carry)

Close 42-38

closeness ≈ 0.90

Perfs valued up (clutch)

This factor is only applied to the final grade (S/A/B/C/D), not to the axis scoring itself — to avoid punishing someone who performs in a stomp.

Who carried? The attribution algorithm

The Carry badge and the individual MVP rank are computed from a composite score — not KDA alone:

carry_score = damage_share × 0.40 + KP × 0.25 + gold_share × 0.15 + obj_damage_share × 0.20
  • Damage share (40%): what you did to the enemies
  • Kill Participation (25%): your teamfight presence
  • Gold share (15%): you were the one snowballing economically
  • Objective damage share (20%): you brought down the turrets/drakes/baron

KDA doesn't enter carry_score directly. A carry at 15/0/8 with 20% damage share and 40% KP didn't carry — they just farmed without pressure. The real carry is the one who absorbs the resources AND converts them into objective pressure.

What we CANNOT fix

  • Macro decisions (rotations, calls, voice shot-calling): invisible in the Riot API
  • Teamfight positioning (front vs back): we don't have per-frame XY coordinates
  • Focus targets (who focused whom): aggregated only, not per target
  • The champion factor (a Yuumi will always have less damage share): compensated by Role Signature

For these blind spots, we lean on the AI coaching, which reads the full timeline and adds context — where deterministic formulas stop.

Theorycraft Engine

The Theorycraft is an interactive build simulator (not an optimizer) that computes, from a combo you define, a damage estimate honoring curated champion passives, AP/AD/HP scalings, and the main item effects (spellblade, on-hit, procs, burn). It doesn't reproduce a meta database like Mobalytics — it recomputes from official Riot formulas across the covered scope.

Important — it's a lab, not an absolute truth. The engine does not search for the optimal build: it prices out the sequence you feed it. Item recommendations are hand-curated presets plus a heuristic fallback scoring — useful to compare options, but not a global optimization. League is too complex (rune ↔ item interactions, unique passives, spike thresholds, matchups, power curves) for a single score to capture “the” ideal build.

Hand-curated data

  • 173 curated champions in champion-spells.ts — every spell (Q/W/E/R) + passive with its AD/AP/HP ratios, its triggers (on_hit, on_3_hit, after_ability), and its special notes
  • 84 items in item-effects.ts with their passive effects (Sheen spellblade, on-hit Nashor/Titanic/BotRK, %HP Kraken, Liandry burn, etc.)
  • 173 curated meta builds in champion-builds.ts — 173/173 of the roster (Yunara and Zaahen have their spells, curated build coming), hand-picked kit-aware with per-item reasons and a kit summary
  • 17 keystones with damage computation (Conqueror stacks, Electrocute, Dark Harvest, etc.) and 45 minor runes with inline FR descriptions

Build suggestions (4 priority levels)

These are starting-point suggestions, not proven optimization. The goal is to hand you a coherent build to load in one click, which you can then tweak by hand.

1.
User intent override
Click an AD / AP / Tank / Hybrid / Support pill → swaps the reco for a hardcoded preset. Useful when you want to force a non-standard build (e.g. “tank Darius”).
2.
Live meta build (Emerald+)
Since June 2026, the simulator shows the builds actually played on the champion, aggregated from our Emerald+ matches and sorted by Wilson lower bound (not raw winrate). A live build only becomes the default if the sample is solid — at least 200 games on the lane and 20 games on the build — otherwise it stays advisory and the curated preset keeps the lead.
3.
Champion's curated preset
Hand-picked meta build for the champion, honoring its passive and scalings. Examples: Veigar → Mejai's Soulstealer (stacks AP with the Phenomenal Evil passive), Nasus → Trinity Force (Sheen proc on Q Siphoning Strike), Shyvana → Liandry+Rylai+Riftmaker (AP-scaling E). It's the default when the live build doesn't meet its sample thresholds. These presets reflect one credible meta path — they don't claim to be “the” optimal build.
4.
Kit-based scoring (heuristic fallback)
If a champion isn't curated: the algorithm analyzes its kit (AD vs AP ratios, burst vs sustained, on-hit passives, multihit, execute) and scores each item through a heuristic damage estimate (approximate ratios, not a perfect simulation — many unique passives and rune/item interactions aren't modeled). Burst profiles (assassins) are scored with the correct armor amplification formula — lethality counts 1:1, no correction factor. Treat it as a starting point, not a definitive answer.

Step-by-step combo calculator

The combo engine calculateCombo (unified engine, shared between the page and the recommendations) simulates an action sequence (Q W E R A C — or a preset like Burst / All-in / Execute) and estimates step by step:

  • Enemy HP recomputed after each step (lets %HP items scale dynamically: BotRK 8% current HP, Liandry burn 2% max HP/s refreshed between spells — no stacking)
  • Mitigation through effective armor or magic resist (after flat + percent penetration)
  • On-hit procs tracked per auto: Nashor's, Titanic, BotRK, Wit's End, Terminus, Muramana
  • Sheen cooldown 1.5s — Trinity/Lich Bane/Iceborn don't proc twice in a row
  • Elder Dragon execute true damage if enemy HP < 20%
  • Specific empowers: Karma Mantra — the post-R Q (Soulflare) detonates a bonus scaled to R rank (W/E Mantra = utility, no damage)

Acknowledged limits: not every unique passive is modeled identically to the game (some conditional effects, heals, shields, CC, and rune ↔ item interactions are simplified or ignored). The engine gives a faithful order of magnitude to compare options, not a frame-perfect reproduction of the League client.

Official Riot quadratic formula

All per-level stats use the official Riot formula:

Stat(n) = Base + Growth × (n - 1) × (0.7025 + 0.0175 × (n - 1))

The quadratic coefficient is what gives a champion a non-linear growth curve between level 1 and 18. We use exactly this formula for HP/Mana/AD/Armor/MR, which guarantees the computed stats match the LoL client to the decimal.

Enemy target: champion or jungle monster

To test your build against a real target, you can pick:

  • 🎯Any champion — through the picker shared with the main pick, with configurable level and enemy items
  • 🐲12 hardcoded jungle monsters (Patch 16.x): Drake / Elder / Baron / Herald / Void Grub / Raptor / Krug / Wolves / Gromp / Blue / Red / Scuttle — HP/Armor/MR stats bypass the champion flow to simulate an objective dive combo
  • 🛡️4 class presets (Tank / Bruiser / Squishy / Dummy) for quick setup

The HP% slider simulates a low-HP enemy to test executes (e.g. Collector < 5%, Darius R < 20% + 5 bleed stacks, Garen R true damage on the marked villain).

Contextual buffs

  • 🐉6 dragon types (Infernal, Mountain, Ocean, Cloud, Chemtech, Hextech) with 0-4 stacks + SOUL at 4
  • 👑Baron Nashor: +AD, +AP, +empowered siege minions
  • 🐲Elder Dragon: execute burn true damage on enemies < 20% HP
  • Game time: enables time-based scalings (Gathering Storm, Overgrowth, Kindred marks, Smolder Q stacks)

Current accuracy — modeled vs approximated

We would rather be transparent than claim frame-perfect accuracy. Here is exactly what the engine computes, and what it approximates or ignores. Estimated accuracy vs the real game: ~88-92% on standard combos.

Modeled exactly

  • Aggregated stats (AD/AP/HP/mana/armor/MR/AS/crit/AH/lethality/MS) from items + runes + objective buffs + Riot quadratic level scaling
  • Official mitigation order: flat reduction → % reduction → bonus pen → total pen → lethality (applied 1:1, no per-level scaling)
  • Crit: base 200% crit damage, Infinity Edge +30% (flat). The Auto action applies the build's average crit (1 + crit% × bonus), the Crit action forces the critical
  • Sustain: lifesteal AA-only, omnivamp on all damage (ranged abilities ×0.33), Conqueror heal fully-stacked (melee 9% / ranged 8%)
  • Conqueror / Lethal Tempo stacks dynamic per step
  • Item synergies: Muramana (bonus mana → AD), Bloodmail (max HP → AD), Rabadon (×1.35 total AP), Seraph (mana → AP), Warmog (+10% bonus HP), IE crit, Sheen family (1.5s CD)
  • On-hit per auto: Nashor's, Titanic, BotRK, Wit's End, Terminus, Muramana, Kraken (3rd hit: 140→310 by level, up to +50% based on missing HP)
  • Dynamic Black Cleaver shred: stacks tracked per hit (phys auto/spell + phys procs), effective armor refreshed between actions. Cap 6 stacks (30% reduction) (round 2)
  • Grievous Wounds — 7 items detected: Mortal Reminder, Morellonomicon, Executioner's Calling, Bramble Vest, Chempunk Chainsword, Thornmail, Oblivion Orb. −40% sustain when the enemy builds one of them (round 2)
  • Magic damage amplification: Liandry's Torment (+6% max stacks) + Abyssal Mask (+12% aura) applied to post-mitigation magic damage, multiplicative across items (round 2)
  • Lifeline shields folded into EHP: Sterak's Gage (100 + 80% bonus AD, phys + magic), Maw of Malmortius (200 + 150% bonus AD, magic), Kaenic Rookern (18% max HP, magic). Assumed active (defensive scenario) (round 3)

Approximated (limited impact)

  • Liandry's amp: assumes max stacks reached (real ramp 6s, applicable to most combos)
  • Abyssal Mask: assumes the aura is active (enemy within <700u). No range check
  • Black Cleaver: stack added POST-hit, so the very first hit doesn't benefit from the shred. Slightly undervalued for very short combos (1-2 hits)
  • Grievous Wounds: assumes mutual contact (the enemy hits us at least once to apply the debuff). No temporal tracking
  • DoTs (Liandry burn 3s, Sunfire 1s, Blackfire stacking): total value computed but without a tick-by-tick timeline
  • Champion spells: 173/173 covered, last major sync 2026-04-15 (patch 16.9.1 partially applied). Yunara + Zaahen added on May 14; the full sync of the other champions touched by 16.9.1 happens champion by champion

Not modeled (coming)

  • Animation / cast time / windup: combos assumed instant cast. Overestimates sustained DPS for champions with long animations (Veigar W, Brand R, etc.)
  • Tenacity / slow resist recognized as buffs but not applied to CC duration or MS
  • Projectile travel time: ranged autos assumed instant
  • Combat-over-time simulation / 3s/6s/18s DPS timeline — instant computation only for now
  • Auto counter-build vs enemy archetype (full AP → suggests Maw automatically, etc.)

The theorycraft isn't a direct coaching tool — it's a laboratory. It gives you a realistic order of magnitude behind your build ideas (“what if I go full crit Yasuo?”, “how hard do I hit a Drake with this gear?”, “Mejai's Veigar at 25 stacks, what does that damage look like?”). It doesn't replace game experience: a build that “numbers well” against a dummy can be weak in game because of tempo, matchup or spikes. Combine it with the AI coaching and the Progression plan to close the full loop: data → theory → practice.

Win Probability

The win probability isn't a simple heuristic percentage. It relies on calibrated XGBoost models trained on snapshots of real games at different key minutes.

What the model looks at

  • Economy and state gaps: gold_diff, level_diff, kill_diff, tower_diff
  • Objective control: dragons, heralds, barons, elders, void grubs
  • Recent momentum: *_per_min and *_delta_5m features to tell whether the game is accelerating or flipping

Minutes served in the product

The pipeline supports minutes 5, 10, 15, 20 and 25. In the product, we currently serve the 10 / 15 / 20 / 25 snapshots when they exist in the match timeline.

10 min

product snapshot

15 min

product snapshot

20 min

product snapshot

25 min

product snapshot

Probabilistic quality

We separate the model's raw probability from the probability served to the player. The latter is calibrated through isotonic regression to better reflect observed reality.

served_proba = isotonic_calibrator(raw_xgboost_proba)

Concretely, it prevents an overconfident model from showing 95% where history says more like 82%. We favor a useful, honest probability over a spectacular score.

Explainability

To explain the score, we use the XGBoost contributions (pred_contribs=True):

  • Local top contributors: why this specific match tips toward blue or red
  • Global feature effects: which features weigh the most on average at a given minute
  • AI reading: an LLM layer then translates these signals into natural language without replacing the ML score

Pipeline robustness

  • Split by match_id to avoid data leakage between snapshots of the same game
  • Evaluation per patch and on a recent window to check the model holds up over time
  • Drift monitoring on feature and probability distributions
  • Bounded hyperparameter tuning to avoid opportunistic overfitting

Transparency and limits

What we do well

  • All formulas are publicly documented on this page
  • Bayesian smoothing compensates for our small sample in a mathematically proven way
  • Our benchmarks are calibrated on real data per rank and per role
  • The Player DNA (35 features × 13 archetypes) is unique on the market — no other tracker profiles this finely
  • Updated on every Riot patch

What we could improve

  • Our sample (~1,000 games/champion) is small, winrates for rarely played champions are less reliable
  • Per-rank benchmarks are currently static (manual updates), an automated system would improve accuracy
  • The Player DNA's per-rank/role benchmarks are calibrated on a limited sample — continuous refinement via the upcoming HDBSCAN training
  • The Theorycraft is a simulator, not an optimizer: it prices out the combos you feed it, but doesn't “find” the optimal build (too many factors to model in League). Some unique passives and rune/item interactions are simplified or missing
  • The Tilt Detector relies on heuristics (defined rules), not machine learning
  • Some Riot API metrics aren't available (precise XY position, clicks, camera)

Our philosophy

LegendsTracker would rather be honest and useful than claim artificial precision. Our algorithms are designed to deliver reliable trends and actionable advice, even with a modest sample. The AI coaching complements the raw data with context and nuance.

Last updated: June 3, 2026 · Real-time streaming match autopsy coaching (SSE) · Tier list: adaptive threshold + small-pool guardrail · Calibrated XGBoost Win Probability (10/15/20/25) + explainability · Theorycraft V2 (173 curated champions, combo calculator, intent presets, jungle monsters) · Deterministic progression plan · Riot Games API data

LegendsTracker isn't endorsed by Riot Games and doesn't reflect the views or opinions of Riot Games.