Skip to content
← Back to Blog
March 20, 2026 · 12 min read · Elias Teubner
#chess #typescript #react #case-study

I Needed to Run a Chess Tournament. So I Built the Software.

I'm a chess player. Club championships, rapid tournaments, tandem events across Austria. I know what it's like to sit in a tournament hall squinting at a projector screen, trying to figure out which board you're on. And I know what it's like to be the person responsible for making that screen readable.

How It Started

A friend needed a chess tournament organized for her degree. She asked. I said yes without thinking too hard about it. The phrase "how hard can it be" showed up. It always does.

The standard tool is Swiss Manager — software built by Heinz Herzog that has been running chess tournaments for decades. Genuinely solid work: the pairing logic is trusted by arbiters worldwide and didn't earn that reputation by accident. That's years of real-world refinement by someone who knows the sport from the inside.

The issue wasn't the logic — it was the era and the platform. Swiss Manager is a Windows desktop app built before the web became the obvious home for tools like this. No browser access, no mobile, and a learning curve that expects you to already understand how Swiss pairings work before you try to run one. For an experienced arbiter, fine. For a university student running her first event, it's a wall.

And the display. When you project pairings onto a screen, the first name in each row is always White — but players who've never competed before don't know that. They see two names and a board number. They walk to the wrong side of the board. The tournament director answers the same question fifty times: "Am I White or Black?"

I could learn Swiss Manager. Or I could build something. I knew the problem, I knew what I needed, and I'm a developer. The choice was easy.

What I Built — And What It Actually Was

Honest version: the first build was a rough prototype. localStorage for everything, no backend, no real test coverage. The pairing logic worked most of the time — and had a bug at the first real tournament that I had to track down and patch mid-round. The architecture was "good enough to run this event" and not much beyond that.

But it ran the event. And the one after that. That was the point.

const stack = {
  frontend: 'React 19 + TypeScript',
  chess:    'chess.js + react-chessboard + Stockfish',
  ai:       'Claude API + WebLLM (on-device)',
  storage:  'localStorage (fully client-side)',
  i18n:     '5 languages (EN, DE, ES, FR, RU)',
};

The Display Problem

First thing I tackled. The projector display shows pairings with explicit color indicators — a white square next to the White player, a black square next to the Black player. No ambiguity. Board numbers large and readable even on a stretched projection. Auto-rotating between pairings, standings, and results. Web-native UX applied to a problem the old software predated and never had to solve.

The Pairing Engine

Implements the Dutch Swiss System. Covered what I needed for the events I ran — though the first tournament exposed a color balance bug that I fixed on the spot. The constraints it handles:

  • Score-group stratification — players grouped by points, paired within groups
  • Rematch prevention — no two players can face each other twice
  • Color balancing — tracks white/black history, alternates intelligently
  • Float-down mechanism — unpaired players float to next score group
  • Smart bye assignment — lowest-scoring player without previous bye

What Else It Does

FIDE Norm Calculator

Automatically detects when a player achieves a GM, IM, WGM, or WIM norm based on performance rating, opponent strength, federation diversity, and titled opponents. Most free tournament software doesn't touch this.

Achievement System

8 badges: Champion, Perfect Score, Giant Killer (beat 2+ opponents rated 200+ higher), Unbeaten, Decisive (zero draws), Overachiever, On Target, and Comeback (bottom half after Round 1, top 3 finish). Gamifies the tournament experience.

Elo Rating Engine

Full FIDE-compliant calculations with automatic K-factor rules: K=40 for unrated/juniors, K=10 for 2400+, K=20 for everyone else. Rating changes display live as results are entered.

6 Tiebreakers, Chess960, AI Assistant, 7 Export Formats

Buchholz, Buchholz Cut-1, Sonneborn-Berger, Progressive, Direct Encounter, Wins — all configurable. Full Chess960 support with 960 positions. Built-in AI copilot with 20+ tools (natural language pairing, result entry, analysis). Export to CSV, PGN, JSON, TRF, printable sheets, web archives, ZIP bundles. 10+ prize categories (age, rating, gender, title, club, custom). Cinematic reveal & podium mode. Stockfish analysis in-browser. Liga mode with Berger scheduling.

What It Adds Over Swiss Manager

Swiss Manager's pairing logic is battle-tested across decades. The gaps are in era and platform — things a web-first tool can fill:

Feature Swiss Manager This tool
PlatformWindows onlyAny browser
Color indicatorsNone□■ explicit
FIDE norm detectionNoAutomatic
Chess960No960 positions
AI assistantNo20+ tools
AchievementsNo8 types
Tiebreakers3-46 configurable
LanguagesLimited5
MobileNoResponsive
Dark modeNoYes
ExportsLimited7 formats + ZIP
PricePaid licenseFree tier

36 Pages, Zero Server

The entire app runs in the browser. No server, no database, no account. Tournament data lives in localStorage — which is also why the architecture is what it is. It's instant, offline-capable, and private. 36 pages: Dashboard, Players, Rounds, Standings, Crosstable, Analysis, Display, Reveal, Podium, Liga, Games, Reports, Clubs, Series, Calendar, Leaderboard, and more.

What I Took Away

  1. A rough prototype that solves a real problem is a valid product. The code wasn't clean. The first tournament had a bug I fixed mid-round. It still ran two events and solved the actual problem. That counts.
  2. Domain knowledge is the real advantage. I knew what the display problem was because I'd lived it as a player. That perspective isn't available in a brief or a spec — you either have it or you don't.
  3. Client-side first is underrated. No backend means no hosting costs, no latency, no GDPR headaches. The simplest architecture that works is usually the right one for the scope you're in.
  4. Sometimes the quick-and-dirty version becomes something else. I built this to avoid learning Swiss Manager. It ran real tournaments, attracted serious interest, and became the foundation for something bigger. But that's a different post.

The pairing logic had a bug the first time it ran under real conditions. The architecture is localStorage and React. But it ran two tournaments, solved the display problem, and nobody had to ask which side of the board they were on. That was the goal.

— Elias

Share this post