What I built
VALIION v2 is the version live today. v1 proved the idea (bot plus a basic site) but could not grow into full match storage, replay, or the performance the product needed. v2 is a deliberate rewrite: profiles, rank and MMR history, match lists, and deep dives into individual games, on a stack I am comfortable extending for years.
Where v1 focused mainly on competitive stats, v2 ingests a much wider slice of match data. The same tooling works whether you are reviewing a ranked loss or a quick deathmatch session, which was never realistic on the first codebase.
Match pages are where v2 really pulls ahead of what v1 could do. I process round data into a timeline of plants, defuses, and kills, derive team-level insights (performance trends, estimated round swing, economy and combat breakdowns), and feed that into a custom interactive map. You can step through rounds like a replay, toggle map callouts, and switch to per-player kill or death heatmaps overlaid on the correct map geometry.
The site is Laravel with Discord sign-in, dashboards, a public API, and admin tooling. The bot still shares the same data layer, so a stat in chat and on the web comes from one place. That unified backend was part of the v2 plan from day one, not something bolted on later.
Performance
A fast data layer
Waiting on a slow upstream API killed the vibe in Discord and on the web. I fixed that at the source.
Player and match lookups went through third-party services that were fine for a prototype and painful at scale. Responses lagged, rate limits showed up at the worst times, and the bot and the site were both stuck behind the same bottleneck.
I built my own API service in Python with a MySQL-backed cache: popular endpoints stay warm, repeated requests hit stored results instead of chaining slow calls, and both the Discord bot and the Laravel app consume the same versioned HTTP API. The web app adds another layer of sensible caching for heavy match payloads so map replay and heatmaps stay snappy once a game is loaded.
That investment paid off in product terms. Features that would have felt too heavy before (full match storage, richer history, replay UI) became realistic because reads were predictable and fast enough for real users.
Round-by-round events on the map
Kills and deaths per player
Derived stats from raw match data
Stack
How I built it
The Laravel app owns auth, dashboards, match pages, and the outward-facing API. Livewire keeps match views dynamic without turning every screen into a separate SPA. When you open a match, the server shapes player stats, round narratives, heatmap coordinates, and insight numbers before the front end renders the map module.
The interactive map is its own JavaScript module: tabbed views for round replay versus heatmaps, canvas-based density layers, and map images with calibrated coordinates so in-game positions land in the right place. It is wired to the match payload the backend prepares, so adding a new visualization is mostly data plus UI, not a second fetch pipeline.
I also shipped a mobile client against the same API, so progression and lookup features are not locked to desktop. The through-line is one data platform, multiple surfaces: Discord for quick answers, the site for depth, mobile for checking in between games.
Next project
Boek met Mij
Online booking born at a barber shop: embeddable widgets for any site plus hosted reservation pages when there is no website.
Continue reading