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 |
| Platform | Windows only | Any browser |
| Color indicators | None | □■ explicit |
| FIDE norm detection | No | Automatic |
| Chess960 | No | 960 positions |
| AI assistant | No | 20+ tools |
| Achievements | No | 8 types |
| Tiebreakers | 3-4 | 6 configurable |
| Languages | Limited | 5 |
| Mobile | No | Responsive |
| Dark mode | No | Yes |
| Exports | Limited | 7 formats + ZIP |
| Price | Paid license | Free 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
- 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.
- 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.
- 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.
- 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
Ich musste ein Schachturnier organisieren. Also habe ich die Software gebaut.
Ich bin Schachspieler. Vereinsmeisterschaften, Rapidturniere, Tandem-Events in ganz Österreich. Ich weiß, wie es ist, in einem Turniersaal zu sitzen und auf einen Projektor zu starren, um herauszufinden, an welchem Brett man spielt. Und ich weiß, wie es auf der anderen Seite ist — als die Person, die dafür sorgen muss, dass dieser Bildschirm lesbar ist.
Wie es begann
Eine Freundin brauchte ein Schachturnier für ihre Abschlussarbeit. Sie fragte. Ich sagte ja, wahrscheinlich zu schnell. Der Satz "wie schwer kann das sein" war beteiligt. Das ist er immer.
Das Standardtool ist Swiss Manager — Software von Heinz Herzog, die seit Jahrzehnten Schachturniere am Laufen hält. Wirklich solide Arbeit: Die Paarungslogik wird von Schiedsrichtern weltweit vertraut und hat diesen Ruf nicht ohne Grund. Das sind Jahre echter Weiterentwicklung durch jemanden, der den Sport von innen kennt.
Das Problem lag nicht in der Logik — es lag in der Ära und der Plattform. Swiss Manager ist eine Windows-Desktop-Anwendung, die gebaut wurde, bevor das Web die naheliegende Heimat für solche Tools wurde. Kein Browser-Zugang, kein Mobil-Support, eine Lernkurve, die voraussetzt, dass man Schweizer Paarungen bereits versteht. Für erfahrene Schiedsrichter kein Problem. Für eine Studentin bei ihrem ersten Event: eine Wand.
Und die Anzeige. Wenn man Paarungen auf einen Bildschirm projiziert, ist der erste Name in jeder Zeile immer Weiß — aber Spieler, die noch nie ein Turnier gespielt haben, wissen das nicht. Sie sehen zwei Namen und eine Brettnummer. Sie gehen zur falschen Seite. Der Turnierleiter beantwortet fünfzig Mal die gleiche Frage: "Bin ich Weiß oder Schwarz?"
Ich konnte Swiss Manager lernen. Oder ich konnte etwas bauen. Ich kannte das Problem, ich wusste was ich brauchte, und ich bin Entwickler. Die Wahl war einfach.
Was ich gebaut habe — und was es wirklich war
Ehrliche Version: die erste Fassung war ein grober Prototyp. localStorage für alles, kein Backend, keine nennenswerten Tests. Die Paarungslogik funktionierte meistens — und hatte beim ersten echten Turnier einen Bug, den ich in der Runde aufspüren und patchen musste. Die Architektur war "gut genug für dieses Event" und nicht viel mehr.
Aber das Event lief. Und das danach auch. Darum ging es.
const stack = {
frontend: 'React 19 + TypeScript',
schach: 'chess.js + react-chessboard + Stockfish',
ki: 'Claude API + WebLLM (on-device)',
speicher: 'localStorage (komplett clientseitig)',
i18n: '5 Sprachen (EN, DE, ES, FR, RU)',
};
Das Anzeigeproblem
Als erstes. Der Projektor-Display zeigt Paarungen mit expliziten Farbindikatoren — ein weißes Quadrat neben dem Weißspieler, ein schwarzes neben dem Schwarzspieler. Keine Mehrdeutigkeit. Brettnummern groß und lesbar, auch bei gestreckter Projektion. Automatische Rotation zwischen Paarungen, Tabelle und Ergebnissen. Web-natives UX auf ein Problem angewendet, das die alte Software noch nicht lösen musste.
Die Paarungs-Engine
Implementiert das Niederländische Schweizer System. Deckte ab, was für die Turniere gebraucht wurde — wobei das erste Event einen Bug im Farbausgleich offenbarte, der dort behoben wurde. Die Kernregeln:
- Punktgruppen-Aufteilung — Spieler nach Punkten gruppiert, innerhalb der Gruppen gepaart
- Rematch-Verhinderung — keine zwei Spieler treffen zweimal aufeinander
- Farbausgleich — Weiß/Schwarz-Verlauf wird verfolgt und intelligent alterniert
- Float-Down-Mechanismus — ungepaarte Spieler rutschen in die nächste Punktgruppe
- Intelligente Freilos-Vergabe — niedrigster Spieler ohne bisheriges Freilos
Was es sonst noch kann
- FIDE-Normerkennung — Automatische Erkennung von GM/IM/WGM/WIM-Normen
- Achievement-System — 8 Badges: Champion, Perfektes Ergebnis, Riesenkiller, Ungeschlagen, Entscheidend, Überperformer, On Target, Comeback
- Elo-Rating-Engine — FIDE-konforme Berechnung mit automatischen K-Faktor-Regeln
- 6 Feinwertungen — Buchholz, Buchholz Cut-1, Sonneborn-Berger, Progressive, Direkter Vergleich, Anzahl Siege — alle konfigurierbar
- Chess960 & Varianten — Alle 960 Startpositionen plus Atomic, Bughouse, Crazyhouse und mehr
- KI-Assistent — 20+ Tools, natürliche Sprache, erstellt Spieler, generiert Paarungen, analysiert Turniere
- 7 Exportformate — CSV, PGN, JSON, TRF, druckbare Blätter, Webarchive, ZIP-Bundles
- 10+ Preiskategorien — Alter, Rating, Geschlecht, Titel, Verein, Spezial, benutzerdefiniert
- Reveal & Podium — Kinoreife Enthüllung der Endplatzierungen und Siegerehrung
- Stockfish-Analyse — Partieanalyse direkt im Browser
- Liga-Modus — Mannschaftskämpfe mit Berger-Algorithmus, Hin- und Rückrunde, Auf-/Abstieg
Was es gegenüber Swiss Manager hinzufügt
Swiss Managers Paarungslogik ist über Jahrzehnte bewährt. Die Lücken liegen in Ära und Plattform — Dinge, die ein webbasiertes Tool adressieren kann:
| Feature | Swiss Manager | Dieses Tool |
| Plattform | Nur Windows | Jeder Browser |
| Farbindikatoren | Keine | □■ explizit |
| FIDE-Normerkennung | Nein | Automatisch |
| Chess960 | Nein | 960 Positionen |
| KI-Assistent | Nein | 20+ Tools |
| Achievements | Nein | 8 Typen |
| Feinwertungen | 3-4 | 6 konfigurierbar |
| Sprachen | Begrenzt | 5 |
| Mobil | Nein | Responsiv |
| Dark Mode | Nein | Ja |
| Exporte | Begrenzt | 7 Formate + ZIP |
| Preis | Kostenpflichtig | Gratis-Version |
36 Seiten, null Server
Die gesamte App läuft im Browser. Kein Server, keine Datenbank, kein Konto. Turnierdaten im localStorage — weshalb die Architektur auch so ist wie sie ist. Sofort, offline-fähig, privat. 36 Seiten: Dashboard, Spieler, Runden, Tabelle, Kreuztabelle, Analyse, Display, Reveal, Podium, Liga, Partien, Berichte, Vereine, Serien, Kalender, Bestenliste und mehr.
Was ich mitgenommen habe
- Ein grober Prototyp, der ein echtes Problem löst, ist ein valides Produkt. Der Code war nicht sauber. Das erste Turnier hatte einen Bug, den ich in der Runde behoben habe. Es hat trotzdem zwei Events durchgeführt und das eigentliche Problem gelöst. Das zählt.
- Domänenwissen ist der echte Vorteil. Ich wusste, was das Anzeigeproblem war, weil ich es als Spieler selbst erlebt hatte. Diese Perspektive ist nicht in einem Brief oder einer Spezifikation verfügbar — man hat sie oder nicht.
- Client-side first ist unterschätzt. Kein Backend heißt keine Hosting-Kosten, keine Latenz, keine DSGVO-Probleme. Die einfachste Architektur, die funktioniert, ist meistens die richtige für den Umfang, den man baut.
- Manchmal wird die Schnell-und-schmutzig-Version zu etwas anderem. Ich habe das gebaut, um Swiss Manager nicht lernen zu müssen. Es hat echte Turniere durchgeführt, ernsthaftes Interesse geweckt und wurde zur Grundlage für etwas Größeres. Aber das ist ein anderer Post.
Die Paarungslogik hatte beim ersten echten Einsatz einen Bug. Die Architektur ist localStorage und React. Aber es hat zwei Turniere durchgeführt, das Anzeigeproblem gelöst, und niemand musste fragen, auf welcher Seite des Bretts er sitzt. Das war das Ziel.
— Elias