Counter-Strike Analytics Platform
August 2026
An analytics platform for Counter-Strike: Automated pipelines, mostly serverless, deployed and now in private beta!

What is this?
Most Counter-Strike analytics tools take a match recording (a “demo”: a replay of everything that happened) and produce personal performance stats: kills, deaths, accuracy, and so on.
This project uses the same data, but instead aims to quantify a player’s style of play.
It sits downstream of my earlier behavioural modelling work: that project asked whether playstyle is measurable, now this is where those ideas can ship :)
Working title: CS-Bait (using one’s teammates as “bait”). Final name and domain are TBD; the live site is on a temporary Vercel URL. The source is private, so the high level architecture is described here.
Status: Private beta. Whitelisted testers can sign in with Steam, sync their recent matches, and have a personal dashboard along with match pages. CI/CD and automated tests are in place.
How it works today
- A user signs in with Steam (their Counter-Strike login) and pastes a couple of codes
- Users then sync their matches
- Their demos get ingested and parsed automatically (and backfilled if required)
- The frontend shows the user all their awesome stats!
This gives a user access to many bespoke statistics not available in-game nor on similar platforms.
Architecture
An event-driven, mostly serverless platform on Google Cloud Platform:
| Layer | What lives there | Role |
|---|---|---|
| Lake | Per-match Parquet | Parse-once stats source of truth |
| Serving Bucket | Per-match JSON | Match-page payload |
| OLTP | Neon (Postgres) | Users, match index, dashboard facts |
| Gold (planned) | Aggregated platform insights | Leaderboards, public rollups |
Current stack
- Frontend: Next.js on Vercel (Steam login, Sync, dashboard, match pages)
- Ingest: Valve APIs + a small GCE bot + Cloud Tasks
- Compute: Cloud Run (Python / FastAPI) for parse and backfill; scale-to-zero when idle
- Storage: GCS for Parquet, serving JSON, and job status; Neon for product state
- Parsing / analytics: Python demoparser → Polars feature extraction
- CI/CD: GitHub Actions with path-filtered deploys
Design choices
- Dedupe before download so two friends sharing a match only pay for one (expensive) parse
- Schema versioning so stats can be recomputed from the lake without re-parsing
- Match pages don't hit the OLTP: the browser fetches match data straight from GCS
- Cost-aware defaults: scale-to-zero parsers, a tiny always-on bot, short-lived Postgres sessions so the database can idle
What’s next
Product: Richer stats and a sharper UI. The things that make someone want to open it after a game.
Scale: Private beta scale works, next is raising the ceiling. A clear cost/capacity scaling roadmap, perhaps a cheaper region, and compact the lake layer.