CS-GO-Stats-Analysis Turns Demo Files Into a Theory of Awareness

A Python pipeline that syncs kills to game state, measures crosshair geometry, and clusters players into roles without relying on gut feel.

8 min read View on GitHub More from rspet123

A CS:GO player sits at a monitor while the fight around them is reduced to geometry. Enemy positions, a crosshair line, and a tick timeline surround the desk like a forensic overlay. The image explains that this repo treats a kill as a moment of awareness, not just an outcome.
The project’s core move is to translate a gunfight into geometry, then turn that geometry into a measurable stat.
Key Takeaways

The stat CS:GO players actually feel

Most stat pages can tell you who got the kill. This repo asks the sharper question: who was already lost before the duel began? That is why the most useful metric here is not K/D, but Angle From Killer, a way to quantify how surprised a player was when danger arrived.

That shift matters because tactical shooters are full of invisible advantages. Good crosshair placement, late information, and bad timing all show up as a death in the final log. The repo’s premise is that the real story lives one frame earlier.

This repository contains the code for a CS:GO performance analysis.

rspet123, Project Creator and Maintainer · CS-GO-Stats-Analysis GitHub Repository README

How a demo becomes a question about perception

The pipeline starts with a .dem file and ends with per-player metrics. In between, the code parses frames, matches kill events to the immediately preceding game state, and turns spatial relationships into numbers.

A single kill becomes a derived stat only after the event is snapped to the frame just before it, then translated into angle and rolled up into a player-level aggregate.

That is the key technical trick. The function the research points to, matchKillToFrame, is doing more than bookkeeping. It is anchoring a human event to a machine-readable moment so the stat can measure context, not just outcome.

# Conceptual shape of the analysis
kill_frame = matchKillToFrame(demo_frames, kill_event)
victim = kill_frame['victim']
attacker = kill_frame['attacker']
aafk = angle_between(victim['crosshair'], attacker['position'])
player_stats[player_id]['afk'].append(aafk)