The task
Players who want to improve have hours of match recordings and no quick way to see where rounds were lost.
I wanted a tool that reads a match file and answers three questions: where did I die, why, and what should I practise next. Files had to stay private, so the analysis needed to run on the player’s own device.
What I did
I designed and built the whole product: the interface, the analysis engine, the server, accounts and billing.
The analysis is a static site that works on its own. The accounts server sits on top of it and adds sign-in, plans, payments, Discord and an admin panel.
Key features
- Analysis in the browser.dem, .dem.gz and .dem.zst files are processed on the device in 20–60 seconds. Nothing is uploaded.
- Deep breakdownDeaths, kills, duels, shooting and time-to-kill, rounds, positions, and what each type of mistake costs in Elo.
- 2D round replayRadars for 44 maps, with upper and lower levels on Nuke and Vertigo, a timeline, drawing, zoom and hotkeys. A 3D view is built in three.js.
- Mistake playlistEvery death with the same mistake, back to back.
- Progress over timeCharts across the last 10 or 50 matches: rating, ADR, headshot rate, first-bullet accuracy and opening deaths.
- Post-match homeworkThe three most expensive deaths and a drill for the main mistake, with a daily streak.
- My matchesMatches stored privately in the browser and reopened instantly.
- Accounts and plansEmail sign-up, Steam and FACEIT sign-in, Free, Pro, Team and Tier 1 plans with Paddle billing, Discord and an admin panel.
Stack
- JavaScript (ES modules)
- Web Workers
- IndexedDB
- three.js
- esbuild
- Node.js 22
- node:sqlite
- node:test
- No framework
- Steam OpenID
- FACEIT OAuth 2.0 + PKCE
- Paddle
- Discord API
- Custom i18n build (Node + Python)
Challenges
Large files, no uploads
ProblemA full match file is big, and players shouldn’t have to send it to a server.
SolutionParsing runs in a Web Worker, so the interface stays responsive while the match is read. Compressed files are unpacked on the device.
Maps with more than one level
ProblemOn Nuke and Vertigo, areas sit on top of each other, so a single radar puts players in the wrong place.
SolutionSeparate upper and lower radars, with each player shown on the level they’re actually on.
Accounts people can trust
ProblemReal sign-ups and payments mean real security requirements.
SolutionPasswords are stored only as salted scrypt hashes. Session cookies are HttpOnly, and only a hash of each token is kept. One-time email links are hashed and expire. Cross-site requests are rejected, sign-in and sign-up are rate limited, and “forgot password” answers the same way for any address, so it can’t be used to find out who is registered.
Three ways in, one account
ProblemPlayers use email, Steam or FACEIT, and often more than one.
SolutionAll three link to a single profile. Steam responses are verified with Steam itself, and Steam and FACEIT sign-in is bound to the browser that started it.
Result
A complete, working product: analysis, accounts, plans and billing in one place.
Authentication, admin, billing and the Discord integration are covered by automated tests.
Screens

Map radar images © Valve Corporation. ProReview is an independent project and is not affiliated with Valve.
Need a web app like this?
Logins, payments, dashboards and data. Tell me what you’re building and I’ll reply within one working day.