· Hasan Alp Güngör
A Major-style live broadcast overlay for CS2 with Game State Integration
How I get live match data out of Counter-Strike 2 with Game State Integration, the Python server in between, and how I work out the killfeed the game doesn't send.
- Python
- Flask
- JavaScript
- CS2

Watching Major tournaments, the broadcast overlay always caught my eye: every player's health, money and weapons, the killfeed, the bomb timer. I wanted to build something like it for my own matches.
Getting data out of the game
CS2 has a feature called Game State Integration (GSI). You put a file in the game's cfg folder, and during a match the game sends the data you asked for as JSON to the address you gave it. Mine asks for updates every 0.1 seconds, sent to a local server:
cfg"Manyaksisko35 GSI v1"
{
"uri" "http://127.0.0.1:3000"
"throttle" "0.1"
"data"
{
"map" "1"
"round" "1"
"player_state" "1"
"allplayers_state" "1"
"allplayers_weapons" "1"
"allplayers_match_stats" "1"
"phase_countdowns" "1"
}
}A Python server in between
The JSON the game sends is big and messy. A small Flask server I wrote in Python takes it and boils it down: scores, the round, the bomb state, the time left, and for every player their name, health, money, main weapon, grenades and K/D. To find the main weapon I skip the knife, the C4 and the pistol, and fall back to the pistol if there's nothing else. The overlay pulls this clean data from a separate address.
An overlay that doesn't flicker
The overlay is HTML, CSS and JavaScript, and it pulls from the server 10 times a second. My first version redrew every panel each time and it flickered on stream. Now each field only updates when its value actually changes, and the health bars slide smoothly with a CSS transition.
The killfeed the game doesn't send
GSI doesn't say who killed whom. So on every update I compare each player's kills and deaths with the previous state. Players whose kills went up are the killers, players whose deaths went up are the victims; I pair them up and add them to the killfeed with the weapon icon. Each line fades out after five seconds.
jsplayers.forEach((p) => {
const before = previous[p.name];
if (before && p.kills > before.kills) killers.push(p);
if (before && p.deaths > before.deaths) victims.push(p);
});
for (let i = 0; i < Math.max(killers.length, victims.length); i++) {
addKillItem(killers[i], victims[i]);
}The bomb timer
When the bomb is planted, the round timer gives way to a 40-second red bomb bar. When a defuse starts, the bar turns white. The game doesn't say how long the defuse takes, so I look at the time left: under 5.5 seconds means the player has a kit and the bar runs over 5 seconds, otherwise over 10.
The code is open on GitHub, and I also walked through building it on my YouTube channel.

