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)) × 100The 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:
| Rank | CS/min | KDA | KP% | DPM | Gold/min | Deaths/game |
|---|---|---|---|---|---|---|
| Iron | 4.9 | 2.4 | 37% | 544 | 350 | 7.5 |
| Bronze | 5.8 | 3.4 | 45% | 698 | 401 | 6.5 |
| Silver | 6.6 | 3.5 | 46% | 809 | 434 | 6.4 |
| Gold | 6.8 | 3.5 | 46% | 739 | 428 | 6.4 |
| Platinum | 7.1 | 3.5 | 46% | 763 | 438 | 6.4 |
| Emerald | 7.6 | 3.6 | 47% | 774 | 454 | 6.2 |
| Diamond | 7.6 | 3.6 | 48% | 760 | 452 | 6.0 |
| Master | 7.7 | 3.6 | 48% | 731 | 448 | 5.8 |
| Grandmaster | 7.9 | 3.9 | 49% | 762 | 457 | 5.5 |
| Challenger | 8.1 | 4.2 | 50% | 798 | 466 | 5.3 |
Role multipliers
Benchmarks are adjusted per role to compare a support and an ADC fairly:
| Role | CS | Vision | DPM | Objectives | KP |
|---|---|---|---|---|---|
| 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
| Category | Weight | Main metrics |
|---|---|---|
| Teamfight | 25% | DPM, kill participation, damage share* |
| Laning | 20% | CS@15, gold diff@15, XP diff |
| Survival | 18% | Deaths (inverted), KDA, low-death bonus |
| Income | 13% | Gold/min, CS/min |
| Vision | 12% | Vision score, wards destroyed |
| Objectives | 12% | 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.
| Category | Laners | Support |
|---|---|---|
| Teamfight | 25% | 28% |
| Laning | 20% | 15% |
| Survival | 18% | 20% |
| Income | 13% | 5% |
| Vision | 12% | 30% |
| Objectives | 12% | 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
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→99Impact 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.45For 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.10Effective 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 + 4For 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 + 4The 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
supportScoreof 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
Side pressure + TF impact
Tempo · Ganks · Objectives
Damage · Priority · Roams
Damage · Farming · Survival
Vision · Peel · Presence
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
teamfightDeath in a 3v3+, allies nearby
good_tradePositive trade (enemy kills > ally kills ±10s)
objective_tradeDeath for a secured objective
gankedKiller/assist from another lane (jungler, roam, TP)
solo_killedKilled in a 1v1 (0 assists, 1 nearby enemy)
caught_outAlone against 2+ enemies, outside a teamfight
overextendEnemy territory + 0 nearby allies
no_vision0 allied wards placed in the 90s window
facecheckBush/river without a ward, enemies nearby
pre_objectiveAlone, < 2 min before a major spawn
dive_gone_wrongNear a tower + enemy territory
repeated_deathSame killer 2+ times
low_resource_deathHP < 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| Tag | Points | Extra condition |
|---|---|---|
| objective_trade | +30 | |
| good_trade | +25 | |
| teamfight | +15 | |
| objectiveSecuredNearby | +20 | context bonus |
| nearbyAllies ≥ 3 | +10 | context 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 |
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) — nocaught_out/solo_killedtag - 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_minIncrease vision score / min
vision_per_minReduce your deaths / game
deaths_per_gameImprove your KDA
kdaShow up to team events
kill_participationPresence at objective fights
kp_objectivesIncrease your DPM
damage_per_minPlace more wards
wards_per_gameBuy more control wards
control_wardsIncrease your damage share
damage_shareContribute more to neutral objectives
objective_damageOptimize your gold/min
gold_per_minObjective 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), p̂ = 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:
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
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.
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.
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.
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).
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_bonusWeights: 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
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: Bronze → Silver → Gold → Legendary. 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| Player | Raw damage | Duration | DPM | Damage share | Verdict |
|---|---|---|---|---|---|
| ADC A (loss) | 120,000 | 25 min | 4,800 | 38% | Carry ✓ |
| ADC B (win) | 180,000 | 45 min | 4,000 | 22% | 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.
| Role | Expected DPM | Target damage share | Vision/min | CS/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.tswith 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.
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_minand*_delta_5mfeatures 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_idto 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.