A graph of TF2 competitive players, built from public match logs. Each node is a player; each edge means they appeared in the same log. Edge weight = number of shared logs; same-team count distinguishes teammates from opponents.
Rewrite of an older project (kyleflett/logMap, ~2021) that used Python + Gephi to render a static 14000×14000 PNG. This version is a trends.tf-fed pipeline into SQLite plus an interactive sigma.js frontend.
pipeline/ # Python, uv-managed. Discovers + fetches logs, exports graph JSON.
frontend/ # Vite + TS + sigma.js static site that renders the graph.
The seam between them is a graph JSON file. Schema: frontend/src/graph/schema.ts.
# 1. Pull logs into SQLite (one-time, ~5 min for a season-sized slice)
cd pipeline
uv sync
uv run logmap discover --format sixes --league rgl --from 2021-05-17 --to 2021-08-15
uv run logmap fetch
uv run logmap resolve-names
uv run logmap status
# 2. Export to JSON the frontend can render
uv run logmap export \
--format sixes --league rgl \
--from 2021-05-17 --to 2021-08-15 \
--min-logs 5 \
--out ../frontend/public/sample-graph.json
# 3. Run the frontend
cd ../frontend
npm install
npm run dev # http://localhost:5173/Pass ?graph=<url> to load a different graph JSON without rebuilding.
- trends.tf is rate-limited to 10 req/min site-wide. The client enforces this globally; do not bypass.
- logs.tf has no documented limit; the fetcher runs 5 concurrent workers conservatively.
- Idempotent + resumable. Re-running
discoverorfetchskips work already done. - Raw log JSON is stored zstd-compressed in
log_rawso re-shredding is offline.
- trends.tf — log discovery (search by format, league, date range).
- logs.tf — per-log JSON (players, classes, kills/deaths/damage, names).
- RGL via [unofficial RGL API] — team affiliations (planned, not yet wired in).
Phase 1 (data pipeline) and Phase 2 (aggregator) work end-to-end against real data. Phase 3 (frontend) renders. Phase 4 (name resolution from raw logs) is in.
Not yet:
- Wider discovery (the original goal mentioned decades of logs going back to ~2012 — current slice is one RGL season).
- RGL team-membership enrichment.
logs.map/ scores backfill in thelogstable (data is inlog_rawbut never re-shredded).- Real frontend polish: 42k edges in a season is a hairball without edge bundling, smarter alpha tuning, or higher default
--min-logs.
The 2021 version lives at https://github.com/kyleflett/logMap. Kept for historical reference. Not maintained.