Three days on the basement floor in Chinatown. A ten-company field, 61 players, and 100 matches of Day of Defeat 1.3 — draft Friday, group play Saturday, playoffs and finals Sunday. Every match played on the KTP stack and recorded end to end.
Thirty-three matches a day, three days running. Friday was 16 draft matches, 15 scrims and 2 twelve-mans. Saturday was 31 tournament matches, a scrim and a twelve-man. Sunday was 27 tournament matches, 5 scrims and a twelve-man. Three completely different days, and every one of them landed on exactly 33 — with one twelve-man the Thursday night before to round the weekend to 100. Stats are cool, huh.
Matches ran on five LAN servers on the full KTP stack — the same engine, cvar enforcement and match handling as the online league, with the broadcast HUD capturing per-player telemetry the whole weekend. Comms on self-hosted TeamSpeak, POV demos recorded every half, HLTV on every match.
Questions about the event: the WSDoD Discord.
Rosters shown are as registered. Several squads reshuffled between registration and the weekend.
| № | Start | Map | Length | Halves | Demos |
|---|---|---|---|---|---|
| 1 | 13:19 | dod_armory_b6 | 45 min | 2 | 2 demos |
| 2 | 13:25 | dod_armory_b6 | 44 min | 2 | 2 demos |
| 3 | 13:26 | dod_armory_b6 | 44 min | 2 | 2 demos |
| 4 | 13:26 | dod_armory_b6 | 43 min | 2 | 2 demos |
| 5 | 13:27 | dod_armory_b6 | 46 min | 2 | 2 demos |
| 6 | 14:35 | dod_thunder2 | 42 min | 2 | 2 demos |
| 7 | 14:37 | dod_thunder2 | 43 min | 2 | 2 demos |
| 8 | 14:39 | dod_thunder2 | 43 min | 2 | 2 demos |
| 9 | 14:39 | dod_thunder2 | 45 min | 2 | 2 demos |
| 10 | 14:40 | dod_thunder2 | 44 min | 2 | 2 demos |
| 11 | 15:43 | dod_lennon5_b1 | 42 min | 2 | 2 demos |
| 12 | 15:45 | dod_lennon5_b1 | — | 1 | 3 demos |
| 13 | 15:47 | dod_lennon5_b1 | 44 min | 2 | 2 demos |
| 14 | 15:47 | dod_lennon5_b1 | 43 min | 2 | 2 demos |
| 15 | 15:50 | dod_lennon5_b1 | 42 min | 2 | 2 demos |
| 16 | 15:52 | dod_lennon5_b1 | 42 min | 2 | 2 demos |
| 17 | 18:10 | dod_saints2_b3e | 43 min | 2 | 2 demos |
| 18 | 18:16 | dod_saints2_b3e | 46 min | 2 | 2 demos |
| 19 | 18:16 | dod_saints2_b3e | 43 min | 2 | 2 demos |
| 20 | 18:23 | dod_saints2_b3e | 46 min | 2 | 2 demos |
| 21 | 18:23 | dod_saints2_b3e | 49 min | 2 | 2 demos |
| 22 | 19:27 | dod_harrington | 45 min | 2 | 2 demos |
| 23 | 19:28 | dod_harrington | 42 min | 2 | 2 demos |
| 24 | 19:29 | dod_harrington | 43 min | 2 | 2 demos |
| 25 | 19:32 | dod_harrington | 47 min | 2 | 2 demos |
| 26 | 19:35 | dod_harrington | 44 min | 2 | 2 demos |
| 27 | 20:45 | dod_anzio | 44 min | 2 | 2 demos |
| 28 | 20:46 | dod_anzio | 43 min | 2 | 2 demos |
| 29 | 20:47 | dod_anzio | 44 min | 2 | 2 demos |
| 30 | 20:51 | dod_anzio | 44 min | 2 | 2 demos |
| 31 | 21:08 | dod_anzio | 44 min | 2 | 2 demos |
| № | Start | Map | Length | Halves | Demos |
|---|---|---|---|---|---|
| 1 | 11:07 | dod_railroad2_s9a | 44 min | 2 | 2 demos |
| 2 | 11:33 | dod_armory_b6 | 42 min | 2 | 2 demos |
| 3 | 12:22 | dod_lennon5_b1 | 43 min | 2 | 2 demos |
| 4 | 12:29 | dod_armory_b6 | 44 min | 2 | 2 demos |
| 5 | 12:36 | dod_harrington | 43 min | 2 | 2 demos |
| 6 | 12:45 | dod_harrington | — | 1 | 2 demos |
| 7 | 12:45 | dod_saints2_b3e | 45 min | 2 | 2 demos |
| 8 | 12:47 | dod_harrington | 43 min | 2 | 2 demos |
| 9 | 13:30 | dod_saints2_b3e | 44 min | 2 | 2 demos |
| 10 | 13:39 | dod_railroad2_s9a | 43 min | 2 | 2 demos |
| 11 | 13:43 | dod_saints2_b3e | 45 min | 2 | 2 demos |
| 12 | 13:50 | dod_railroad2_s9a | 43 min | 2 | 2 demos |
| 13 | 15:12 | dod_thunder2 | 44 min | 2 | 2 demos |
| 14 | 15:34 | dod_railroad2_s9a | 43 min | 2 | 2 demos |
| 15 | 15:38 | dod_lennon5_b1 | 42 min | 2 | 2 demos |
| 16 | 15:56 | dod_lennon5_b1 | 45 min | 2 | 2 demos |
| 17 | 16:13 | dod_lennon5_b1 | 50 min | 2 | 2 demos |
| 18 | 16:39 | dod_saints2_b3e | 43 min | 2 | 2 demos |
| 19 | 16:47 | dod_armory_b6 | 44 min | 2 | 2 demos |
| 20 | 16:54 | dod_harrington | 51 min | 2 | 2 demos |
| 21 | 17:34 | dod_saints2_b3e | 43 min | 2 | 2 demos |
| 22 | 18:34 | dod_railroad2_s9a | 43 min | 2 | 2 demos |
| 23 | 18:39 | dod_armory_b6 | 42 min | 2 | 2 demos |
| 24 | 19:01 | dod_armory_b6 | 45 min | 2 | 2 demos |
| 25 | 19:59 | dod_railroad2_s9a | 46 min | 2 | 2 demos |
| 26 | 20:12 | dod_harrington | — | 1 | 4 demos |
| 27 | 21:39 | dod_anzio | 44 min | 2 | 2 demos |
† Two matches were aborted a couple of minutes in and replayed immediately: Best Buds v onLAN thunder on lennon5_b1 (restarted 15:47) and NoSoul v dicE on harrington (restarted 12:47). Their demos also predate the naming change that puts team names in the filename, so the matchup was recovered from HUD player data instead. The replays alongside them carry the real result.
Philly LAN 2026 is running an experimental KTPR. It leans on stats that only exist because the broadcast HUD was watching — assists, damage, capture breaks — and that is exactly why it is not the finished article for future seasons. Read it as a worked example of what KTP could rate players on, not a settled standard. It will keep being tweaked.
What it is trying to do. A scoreboard rewards whoever killed the most, and Day of Defeat is not that game. Someone breaks a capture and dies for it. Someone puts four hundred damage into a push a teammate finishes. Someone spends the half holding a flag nobody ends up contesting. Those players win matches and a kill count cannot see any of them. KTPR is an attempt to put a number on the rest of the game, and to weigh each part against what an average player at the same position did — so a sniper is judged against snipers rather than against the field.
This is not a first draft. The three-term formula below is what KTPR has been refined into across roughly nine seasons of KTP play, and it is what the day boards on this page used until now. Every term in it earned its place by being argued over and kept. Three ratios, each against the field average:
KTPR = [ (K/D ÷ field avg K/D) + (kills per half ÷ field avg) + 0.25 × (flags per half ÷ field avg) ] ÷ 2.25It is honest about what it measures, and what it measures is fragging with a nod to flags — because for nine seasons that was all the server could tell us. A player who never tops a kill count cannot rate well in it, however the match was actually won. The version below is not a replacement for that work so much as the same idea given more to work with: the HUD finally made assists, damage and capture breaks measurable, so they could join the formula rather than be argued about from memory.
Six contributions instead of three, each normalised against the median of the player’s own position, then blended. 1.00 is an average player. Every number below is printed from the weights the engine actually ran:
every stat becomes a ratio to the position median, then is clamped: R = clamp( value ÷ position median, 0.55, 2.5 ) capture breaks get +0.5 on both sides first — the median is near zero, so without it one extra break swamps everything else low kills lift the support terms, because they are saying something the kill count missed: kill term = R(kills) ^ 0.65 damage term = R(damage) × clamp( 1 + 1.2 × (1 − R(kills)), 0.6, 2.5 ) assist term = R(assists) × clamp( 1 + 0.6 × (1 − R(kills)), 0.6, 2.5 ) the blend — kill efficiency carries the most weight: score = ( 1.0 × kill term + 3.0 × R(K/D) + 1.0 × assist term + 0.5 × damage term + 0.7 × R(flags) + 1.0 × R(breaks) ) ÷ 7.2 deaths normalise rather than punish, and dying while out-fragging the field counts as pushing rather than failing: deaths = (deaths per half ÷ position median) ÷ ( 1 + 1.5 × max(0, R(kills) − 1) ) death adjust = 1 − clamp( 0.2 × (deaths − 1), −0.1, +0.15 ) KTPR = score × death adjustThe weekend was the use case. Damage already reaches the match record and this board now reads it from there; assists and capture breaks are the two the record does not log yet, which is the only reason they still carry a HUD badge. The work to log them through HLStatsX is planned for Season 10, and that matters for more than tidiness: the match record is gated to live play, so stats that arrive through it drop warmup automatically the way kills and flags already do. The LAN is what proved the stats were worth having and showed exactly which ones are missing.
Thanks are owed. To Cadaver, for the broadcast HUD — none of the stats beyond a scoreboard exist without it. To Krod and Chi for the work on KTPR itself, which is where the formula above came from. And to everyone else who has put time into KTP’s stats over the years, named here or not — this is a long list and leaving anyone off it would be the wrong kind of accuracy. It is a group effort, and it shows.
Positions are the league's four — Rifle, Heavy, 3rd, Sniper — finalized with staff after the event (dashed pill = read from class play; solid = named by staff). Click any column header to sort; rank, and the medal colours with it, recompute within whatever view, filter and sort you're looking at. Hover KTPR or Style for what the numbers and labels mean. Click detail on any row for hitboxes, class spawns and the rest of the ledger. Clan tags are stripped for display — the full in-game name is on each player cell's title, and a player who re-tagged between days appears under the name they played that day.
Counting basis: kills, deaths, K/D, flags, damage, headshot kills and the longest streak are from the match record — exact, and scoped to match time. The streak is rebuilt from the frag log rather than read off the HUD, which reported it short whenever a run was still going at its last save. Everything marked HUD — assists, capture breaks, prone, and the HUD rows in the detail panel — comes from the broadcast HUD's periodic save, and is pulled in two directions: the save runs whenever the server is up, so warmup and knife rounds can land in the total, while its final write comes before a half ends, so the tail of each half is missing (worth 11–19%, on every player alike). Treat HUD columns as close, not exact. On the weekend view, counts are the two days summed, the streak is the better day's run, and rates are recomputed from the summed totals.
Map boards (a map chip on a day view) are rated against that day's baselines with the breaks term omitted — breaks aren't recorded per map — and the remaining five weights renormalized; they also drop the streak/breaks columns and the detail panel, which are day-level. Weights kill_exp 0.65 · kills 1.0 · K/D 3.0 · assists 1.0 · damage 0.5 · flags 0.7 · breaks 1.0 · break smoothing 0.5. Generated from the published stats by build_season_board.py, so the board and the numbers behind it cannot disagree.
Each of these is a single day's performance, not a weekend average — the tag on each slot says which day. They are the best mark seen at each position, so they do not add up to a line-up that ever played together, and a Saturday figure and a Sunday one are not measured against the same field.
One match, not the weekend. Where the top mark is shared the card says so — a record several people hit isn't one.
Categories drawn from the WIP awards sheet in the 2025 stats workbook. Anything decided by a HUD column carries the same caveat it does on the stats board — those figures can include warmup, so treat a HUD-decided award as a strong indication rather than a settled count. Once awards are given they publish here permanently, with the edition they belong to.
242 HLTV demos were recorded across the weekend — every match, every half — plus per-player POV demos filed under the match rules. The tournament demos are published at fastdl.ktpdod.com/demos/LAN-PHILLY2026; every row of the match log links its files.
League demos live at fastdl.ktpdod.com/demos.
The weekend was cast by Corey Marko and Dyelife. VOD links publish here when the channels post them.
Want a photo taken down? Each one carries a Request removal line — staff read it and decide. Anything filed at the event that doesn't offer one, ask about in Feedback.
Uploaded by players during and after the event through the LAN site, which required a Discord sign-in — so each file is credited to whoever filed it. These are player POV demos, distinct from the HLTV recordings above; they are served as uploaded, unedited, and are zipped. A blank match means the uploader didn't tag one. Ten files were filed twice and are listed once, keeping whichever upload named a match.
Photos are credited to your Discord account — the same sign-in the rest of the site uses, sharing your username and ID and nothing else.
POV demos are filed against your roster entry, which is why this one needs a sign-in — the uploader column above is never blank.
Your Discord isn't on a roster. Demo uploads are credited to a player's roster entry, so this one needs staff to link your account first — ask in the WSDoD Discord or through Feedback. Photos don't need it; that form works as you are.
Feedback here is attributed — staff read it knowing who sent it, and can follow up with you on Discord. Signing in shares only your Discord username and ID; nothing is posted as you, anywhere.
This starts the Discord OAuth flow the LAN site used (identify scope only) and returns you to this section.
So staff can answer. Anonymous feedback can't be followed up, and this event is small enough that every note gets read. Your Discord name is the address a reply comes back to.
Something urgent or mid-event? Say it in the WSDoD Discord — it's faster.