This is a solo project: product, design, frontend, sync engine, backend services and infrastructure. It is also the project I think about in the shower.
The gap
Chess is having its biggest boom in decades, but most chess software still looks and feels like it was frozen somewhere around 2008. Database tools are desktop relics, analysis interfaces feel like spreadsheets, and even the big platforms carry years of legacy UI. There is no state-of-the-art chess platform. Nothing that does for chess what Linear did for issue tracking: software so fast and so carefully designed that using it becomes a pleasure in itself. WhitePawn Web exists to close that gap.
Why this is hard
Chess is a data problem wearing a game’s costume. A single active player brings thousands of games. Every game is a tree of moves, variations and comments. An engine evaluation is not a value, it is a stream that updates many times per second while the engine digs deeper. Add live broadcasts, shared analysis and puzzles, and you have a product that has to move a lot of data all the time, while feeling completely weightless.
That combination is exactly why I love this project. Making something beautiful is a design problem. Making something fast is an engineering problem. Making a data-heavy application beautiful and instant at the same time is the kind of challenge where both disciplines have to be pushed at once, and where most software quietly gives up on one of the two.
The foundation for this is sp00ky, the local-first sync engine I built for SurrealDB. Every screen in WhitePawn Web is a live query against a local replica: reads never leave the device, writes apply instantly and sync in the background, and every change streams to every connected client. Even public share links are live. Someone opens your analysis without an account and watches your moves appear in realtime.
Taming the data
WhitePawn Web connects to Lichess and Chess.com and imports your entire game history, continuously. Import, statistics and deep game analysis run as background jobs behind an outbox pattern, so heavy work never blocks the interface and failed jobs retry on their own.
The payoff is simple: the app feels instant. Opening a game is a read from the local replica, not a request to a server, so the position is on the board before your hand leaves the mouse.
Pages that stay live
A lifetime of chess is too much to preload. Pulling thousands of games up front would mean megabytes of transfer and a long wait before the first row appears. So the list is virtualized twice over. On the rendering side, the DOM holds only the rows near the viewport, however deep the database goes. On the data side, the database loads in pages, and each page is its own windowed live query against the local replica.
Scroll position decides which pages exist. As you approach the edge of the loaded range, the next page’s query is registered before its first row reaches the viewport, so the games are already on screen by the time you arrive, while the sync engine pulls anything missing from the replica in the background. Pages far behind you can be torn down again to keep memory flat, and scrolling back re-registers them against data that never left the device.
The unusual part: none of this trades away realtime. In most software, pagination and realtime are enemies, because a paginated list is a snapshot, stale the moment it renders. In sp00ky, every page is a window over one incrementally maintained sorted set. When a new game syncs in, the engine computes exactly which windows change: a game entering the first page pushes that page’s last row onto the second, and both pages receive their delta in the same step. Nothing re-sorts, nothing re-fetches. Scroll back up twenty pages and the rows there are not stale history, they have been live the whole time.
Analysis as a shared resource
Engine analysis is expensive, so WhitePawn Web treats it as something to keep, not to throw away. Every evaluation is cached per position and engine, shared with everyone who has access to the game, and only overwritten by a deeper result.
The sync engine turns that cache into something better: analysis shared in realtime. An evaluation is just data, and data syncs, so everyone looking at the same game watches engine lines appear and deepen live without running an engine themselves. Analyzing a game with a friend over Discord, only one of you keeps Stockfish running; the other sees the same lines stream in. And when you open a game a friend analyzed yesterday, their depth is simply there, and your engine continues from that point instead of starting over.
The engine doing the digging is Stockfish with an NNUE network, compiled to WebAssembly and running in a Web Worker, so the interface never stutters while it burns CPU. It is an opt-in download of about 7MB rather than something forced into the initial load, because respecting the first visit is part of the product too. Everything it finds flows back into the shared cache.
The surface is the product
The interface is built the way I wish more chess software were built: a real design system with consistent tokens, tight letter spacing, a calm dark theme, hairline borders, keyboard shortcuts for everything. The board itself is rendered by a Skia engine compiled to WebAssembly. It boots once per page, deferred to idle time so the move list paints first, and its repaints are throttled so piece animations stay smooth even while engine lines update underneath.
None of these details is impressive on its own. Nobody notices a border color or an idle-deferred boot. But everybody feels the sum. That is the whole thesis of this project: quality in software is a large number of small decisions, made stubbornly. Three of those decisions, up close.
Navigating a game without a mouse
Reviewing a game should feel like reading, and reading does not involve aiming at targets. The arrow keys walk the moves: right advances, left goes back, and when a position branches, down steps into the subline. The whole review, main line and detours, happens without touching the mouse.
A dot that carries a conversation
A move with a comment gets the quietest indicator I could get away with: a small dot, and nothing else. A move list is dense enough already. The interface should tell you a note exists without insisting that you read it.

The comments themselves do more than sit in the margin. A move mentioned in a comment is a live reference: click it and the board jumps to that position, even when the move is buried in a subline you and your coach dug into. The conversation and the analysis stay one click apart.
Statistics that slide in, not load in
Every game collection has its own analytics: record, performance rating, win rate by color, rating trend, strongest openings. Opening them is one click, and the motion matters as much as the numbers. The sidebar slides in while the game list yields to make room, one continuous movement, no reflow, no spinner. It can afford an entrance like that because the numbers were computed off the hot path long before the click. A panel that has to wait for a server cannot move like this.
Beyond the board
The platform keeps growing past the core experience. A broadcast system lets players stream games with camera rooms on LiveKit, a WebSocket relay written in Go fans live board states out to viewers, and a scene renderer composes boards, cameras and overlays into a produced stream. Drivers for electronic chess boards from Millennium, Certabo and Chessnut connect the physical game, a thread that runs through all my chess projects since the original WhitePawn app. And the whole thing is an offline-capable PWA, because a chess database you cannot open on a plane is only half useful.
Where it stands
WhitePawn Web is in private beta, and I keep iterating on it with stubborn attention. The lesson it keeps teaching me: categories only look finished until someone refuses to accept how they have always looked. Chess software has waited long enough for that refusal.
