FideLite.ArtFideLite.Art code golf · chess arbiter · bytes · FIDE · JavaScript · single file

The world's smallest chess game that enforces every official rule.

In front ends ranging from 2.7 KB down to 1,815 bytes. It runs in the browser and is written in JavaScript; no libraries, no install, no server. The single HTML file you download opens on almost any device.

5d37b3d59999999900000000000000000000000000000000888888884c26a2c4

The entire starting position: 64 hexadecimal (base 16, written with 0–9 and a–f) digits, a1 through h8 in order. Each digit is one square, each colour one piece type. The engine's board representation is nothing more than this.

Empty 0Bishop 2/3Rook 4/5Queen 6/7Pawn 8/9King a/bKnight c/d

“Perfection is reached not when there is nothing left to add, but when there is nothing left to remove.”Antoine de Saint-Exupéry

Every square carries four things: its name, the piece's hexadecimal code, its glyph (the piece's Unicode symbol — ♞, ♛), and the index the engine uses.

The index starts at zero on a1, rises left to right, wraps from h1 to the a-file of the next rank up, and ends at 63 on h8. What that layout buys the engine is on the engine itself tab.

In the L3 numerical build the indices are shifted up by one to sit closer to player intuition, so each is one more than the internal index: a1 = 01, e4 = 29, h8 = 64.

Golfstack

The code itself, its worth, and the craft of Golfstack

A complete, tournament-grade chess arbiter packed into a single HTML file of 2.7 KB (the measure of how much room a file takes on disk; 1 KB = 1024 bytes). Built on the Golfstack idea, the project is an engineering exercise that pushes code optimisation to its limit: the most function in the least space.

Two players share one device, and the engine delivers a fully featured game of chess without reaching for a single external library.

Nothing is missing on the rules side. The whole FIDE book is implemented. You can find how each article behaves, one by one, on the Official rules of play tab. Even in the thorny scenarios where resignation and flag fall meet insufficient material, the ruling follows 5.1.2 and 6.9 exactly. On the blocked-position branch of 5.2.2 every ruling the engine makes is correct: a position it calls dead really is dead. Measured coverage is 93%; the remaining 7% are edge cases involving a bishop. Anything it does not recognise, it plays on — so the margin of error always falls on the safe side.

Technical features

  • An interface close to a modern site. In the L3 (the build you play by clicking the board) build the board is clickable: the selected square and its legal targets are marked, a promotion picker opens, the check indicator sits on the status line and a pending draw offer highlights the draw button. After each move the board flips to the perspective of the player to move — that flip is in the L3 input and L3 prompt builds too. The piece glyphs, clock symbol and indicators in L3 all come from Unicode, with no image file, font download or CDN request. The other builds draw the board in ASCII or full-width characters; the blindfold build and L3 numerical draw nothing at all.
  • Fischer time control and a readable clock. The time control is 10+5: ten minutes, plus a five-second increment added to that player's clock on every move. Both clocks run on screen in MINUTES : SECONDS with their emoji, and the increment visibly lands on each move. The moment a flag falls, the result follows by itself.
  • Move generation. The engine does nothing odd where chess logic is concerned: an illegal move never passes, a legal one is never refused, and no result code fires in the wrong position. The claim rests on measurement — the engine passes the standard chess-programming test suites without error to reasonable depths: the CPW positions, van Kervinck's tricky list, and the 6,838-position Vajolet corpus.
  • Rule accuracy and game endings. When a game ends it does not just say “draw”; it names whichever of the fifteen results actually occurred. The codes are two characters yet mnemonic — you can tell at a glance which letter stands for what — and all fifteen map one-to-one onto articles in the FIDE handbook. Stalemate, 3-fold repetition, a dead position: never left ambiguous.
  • The hard half of the draw. Beyond ordinary move generation an arbiter has two questions to answer: can mate still be reached in this position at all? and when the flag fell, or when one side resigned, was the opponent in a position to mate? Each has a cheap half and a hard half. The cheap half is counting material: is there enough left on the board to mate with. That is the common one, and the engine does it too.
  • The hard half does not come from counting pieces. A position can be dead with material to spare: the pawns have locked each other, nothing can move, and no mating sequence exists even with both sides cooperating. At flag fall the real question is not “does the opponent hold enough material” but “can a mating position be constructed on this board” — since the losing side can help by shutting off its own king's escape squares with its own pieces, the defender's material counts too. The engine implements both halves.
  • And the hard half costs less than you would think. A blocked position needs no move search; it falls out of the static picture of the board — the squares the pawns and kings can reach are grown until none of them grows any further, and if none does, the position is dead. Measured coverage is 93%, and every case it misses involves a bishop; the engine never calls a position dead by mistake, it only fails to see some. In other words nearly every blocked position that actually occurs can be caught for very little (another 1 KB would bring in almost all the bishop cases as well, taking 93% to 99.94%; I decided to draw the line here for now).
  • Robustness. Neither a bug nor invalid input nor deliberate abuse can derail the engine. In L3 bad input never arises in the first place: moves are made by clicking and only legal squares are marked. In the text-input builds the engine rejects anything it does not accept and simply asks the same question again — including empty, nonsensical or intentionally malformed input. The game never locks up and the state is never corrupted; where the notation is valid but the promotion letter is not, the piece promotes to a queen automatically.
  • A modular structure. The parts are not fused together: the rule layer stands on one side, what the interface does on the other. Take out the draw offer, the clock or the resign button if you want to — everything else carries on working, and the same goes for automatic draw limits like 5-fold repetition and the 75-move rule. The starting position — from a FEN, if you like — the time control, even the threshold of the repetition counter can all be set. Code golf chess rarely draws these lines: check is not detected by a test of its own, move generation and evaluation run through each other, and you can no longer tell where the rules end and playing the game begins.
  • Readability and mnemonics. The source was written to run, not to be read: single-letter names, one line, inlined functions. Even so the naming is not arbitrary — as far as the byte budget allowed, every variable and function name was chosen to echo its English counterpart. Real care went into this, down to the distinction between upper and lower case: readability was preserved wherever Golfstack permitted it. What actually happens is unpacked line by line in a separate section.
  • Speed and depth. Most of an arbiter's work is over in a single half-move: the legal moves of the side to move, and whether the game has ended. The hard half of the draw falls outside that — there the engine really does look ahead, and it does so during the game, at the moment a flag falls, not in testing. Speed is therefore not decoration. The second place it counts is testing: perft (the standard correctness test that counts every move sequence from a position) counts millions of nodes, where seconds turn into minutes. engine_4x serves both — a variant that enforces the same rules, 48 bytes longer and logically identical; between 2.4× and 9.7× on move generation depending on the workload, and a great deal more on the adjudication layer. Where those bytes go, and the measurements behind them, are under a separate heading.
  • The bot is a separate layer. In the main build two people play; the engine does not suggest moves, it runs the game. The claim is about rule density per byte, so no bot is required — a build where two people play comes out cheaper than one driven by a bot picking moves at random. And because the rule layer stands apart, putting a search on top does not disturb the core: there is a separate build with a bot, one that leaves the clock and the interactions between players out and keeps the rule side in place. Its measured strength is around 2400 Elo; the evidence, the tests and further bot variants are still to come. The next question is how much Elo fits into a single 10 KB HTML file, rules and interface included, on a reasonable processor budget — the target is 3000, and whatever the result, it will be published in full.

The real claim is not these items one by one, but the ratio itself: bytes per unit of service. Everything above comes out of a single downloadable file of 2.7 KB.

The engine was tested thousands of times during development, edge cases included, and every known bug was cleared. Even so, if something catches your eye during a game, report it; feedback improves the engine directly.

Play

Pick a build, start it

Each build runs in its own frame, and the clocks only start ticking once you start it. Changing builds closes the running game.

If the board feels small in the L3 and L3 input builds, use the browser's zoom — Ctrl +, or + on macOS. The game scales along with the frame; Ctrl 0 restores it.

Two of the eight builds — L3 prompt and L3 numerical — take moves through a prompt() dialog and announce the result with alert(). If your browser blocks those dialogs — most offer a “prevent this page from creating dialogs” option after the second one — the game stops silently. If you have blocked them, reload the page and allow dialogs again. The remaining six builds use no dialogs at all.

Build

Selected build L3. Start the game or show its code.

Ready

The story

Carving the marble — a chess universe fitted into bytes

Code golf is the art of writing a program in as few characters as possible without giving up anything of its function, its rules or its logical correctness. There is one constraint: the behaviour stays the same, the volume shrinks.

From beginning to end this project chased a single question:

What is the smallest number of bytes a complete, 100% FIDE-compliant game of chess can take, skipping no rule from first move to last?

The answer is below. But to ask the question at all, I first had to decide where the word “complete” begins.

What can we remove and still have chess

Writing the engine, I found myself in the middle of the Ship of Theseus paradox: what can you strip out of a game of chess and still be left with chess?

An unavoidable question of identity surfaces here. I did not take pre-fifteenth-century chess as the starting point — there the bishop leaps exactly two squares, the queen moves one square diagonally, and there is no castling and no double pawn step. They share a name, but that game and the one we play today are fundamentally two different identities; taking it as the baseline would have meant answering the question for a different game.

What I took as the starting point was this: today's piece movement, check, checkmate and stalemate, the pawn's single-square advance, and automatic promotion to a queen. That is the core left over once everything removable has been removed. Take one more article away and the game stops being chess.

With the core in hand there was only one direction left: adding.

From core to full arbitration

The engine did not arrive in one piece; it grew through overlapping rounds.

Choice of promotion piece came first, releasing the pawn from its sentence to the queen and making knight, bishop and rook options too. Then the pawn's two-square advance, and with it the en passant capture that has to come along — the two are inseparable, because the second closes the hole the first opens. Then castling. At that point what was happening on the board was chess as we play it today.

After that I moved off the board. The two theoretical limits that keep a game from running forever, the 50-move rule and the clock, entered the engine at this stage.

Normally I would have stopped there. But having come this far I wanted to finish the rest of the rulebook: repetition of position, insufficient material, the draw offer and the draw claim, flag fall, resignation. That brought in the rulings an arbiter makes without ever looking at the board.

I cannot claim this path was a straight line. Again and again I had to go back after noticing that a rule I had already added did not match FIDE exactly, or behaved wrongly in a particular position. Sometimes a new discovery threw out the entire solution I had written up to that point. Every reversal meant a structural revision and then a fresh round of byte optimisation; the code grew, then shrank again.

It was a long and instructive process, on both the coding and the chess-logic side. Which rule broke what, which discovery forced a rewrite of what — I will tell that step by step and in far more detail in a separate “Build log” section.

Where the carving stops

Asked how he made his sculptures, Michelangelo is famously said to have answered:

The sculpture is already complete inside the block of marble. I merely carve away the parts that are not needed.Michelangelo

That is what I did in Golfstack. What the rules were was clear from the outset; the work was carving away every excess that described them.

Claude Shannon's information theory tells you where that carving ends: every message has a lower bound below which it cannot be compressed losslessly, and any saving past that bound is no longer a saving but a loss of information. Code golf is exactly the walk toward that bound. The difference here is that the compression has to be lossless: drop a single article from the logical data the rules require and what is left is not a smaller chess; it is something that is not chess.

The answer to the question is therefore a single number: 1,815 bytes. numerical_packed.html arbitrates an official game between two human players from the first move to the last.

Why the browser, and why JavaScript

One of the first questions that comes up is this: why the browser, why JavaScript?

The first half of the answer is about reach. Measured by which device, which operating system and which environment it will open in, the browser comes first by a wide margin. No compiler, no runtime install, no package manager; double-clicking the downloaded file is enough. On top of that HTML hands you the visual layer almost for free — no separate UI infrastructure is needed for the board, the clocks and the indicators, and the file even opens when <html>, <head> and <body> are never written at all. If the distance the engine had to cover to reach a player were any longer, the 2.7 KB claim would mean nothing in practice.

The second half is about the language itself. JavaScript suits Golfstack far better than you would expect: it requires no variable declaration keyword, it imposes no mandatory skeleton of entry point and imports, and implicit coercion means the board's numeric encoding never forces you to write a cast. Arrow functions, the ternary operator and the comma operator working together let an entire function collapse into one expression with no braces and no return. Self-extracting packers are native to this language too; the packed form of the engine rests directly on that.

An objection follows from this: the browser is hundreds of megabytes, and it supplies both the JavaScript engine and the runtime. Isn't 1,815 bytes just a play on words, then?

There is no such thing as a zero baseline. Written in C it would need libc and an operating system; written in assembly, an instruction set and a machine. A measure that also counts the runtime has nowhere to stop; measured that way, no size claim could be made in any language. Code golf therefore always draws the line in the same place: the code you write and the libraries you pull in count; the environment and the baseline do not. Neither the browser nor the operating system nor the processor goes on the tab. Cross-language comparison is done on this measure, and record attempts are open to any language.

Anyone who takes the end user's view and wants a stricter measure is also right: same environment, same language, zero libraries. Both are legitimate, and under both a single file stands on this side of the line — nothing downloaded, nothing bundled, nothing added.

The “smallest in the world” claim rests on that measure. I have been looking for a good while and have not come across anything smaller that enforces every official rule, in JavaScript or anywhere else. The claim is falsifiable, and it should be: it falls the moment someone ships something smaller. Until then it stands.

The rule layer is one thing, the bot another

The second question is harder: why is there no AI opponent in the main build?

The usual route is clear enough. A board is drawn on the screen, the pieces are set out, and the player takes on a bot that plays a little better than random; the great majority of attempts so far have run along that line. The code-golf world has chess bots squeezed into a few hundred bytes, but to make room they give up almost every rule that forms the backbone of the game — castling, en passant, the 50-move rule, 3-fold repetition. Bot logic inflates the code considerably; a build where two people play is far cheaper in byte budget. And adding a bot contributes nothing to FIDE compliance.

The real issue is measurability. What counts as a bot is vague: is the thing that fits under 100 bytes and plays random but legal moves a bot? Is one that counts material a bot, or is the threshold higher? Who decides? Rules are not like that; they are exact. Whether a piece of code implements an article is visible on inspection and leaves no room for argument. I tried writing a bot along the way too; then I put all my energy into the side that can be measured.

The two stand apart in the code as well, and that is no accident. The engine core knows nothing about the driver, so putting a search on top does not touch the rule side. That is exactly how the build with a bot came about: the same rule layer, the clock and the interactions between players left out, a search on top. Its measured strength is around 2400 Elo. I do not consider it finished, because the criterion I have set for bringing a bot here is the same one the arbiter engine had to meet: it has to be a masterpiece in ratio of bytes to quality. That is a longer job — but since it is a hobby I enjoy a great deal, I will make a concrete attempt at a record on that side too.

What remains is what the measurable side bought: at 1,815 bytes this build is ahead of the large platforms on the hard half of the draw. The common practice, even where pure FIDE is applied, is to stop at the material test: is there enough left on the board to mate with. The side that looks at the position itself — whether a mating position can be constructed once the flag has fallen, and the detection of blocked positions — is not common. And that has been measured: CHA-Solver scanned a large public game database end to end and counted around 200,000 wrongly decided games50,000 of them from blocked positions, the remaining 150,000 from the question that goes unasked at flag fall. Chess.com applies a FIDE-USCF hybrid for some articles; not a shortcoming but a choice, blending the rules of two federations deliberately. The choice here is a different one: one book, one measure. On those same articles the engine is therefore bound more tightly to FIDE.

Beyond the bytes

Chess projects in the code-golf world generally compromise on the 50-move limit, 3-fold repetition and insufficient material — often on castling and en passant too — and focus on the basic core alone. I looked at the side hardly anyone visits, and I think that is exactly where the project's originality came from. As far as I know, Golfstack is the world's smallest complete arbiter engine able to fit the full 100% FIDE standard into a byte space this narrow.

It is not a commercial product and makes no such claim. But layering all the rules of chess this way and fitting them into the fewest bytes is pure engineering, pushing the limit on the code side.

Getting to this point I had substantial help from models like Claude and Gemini. Work that would normally take months — debugging, code optimisation, picking apart browser behaviour — I compressed enormously with those tools. The decisions stayed mine; what I gained was speed.

As Chekhov put it: writing long is easy, writing short is the hard part. In this project I tried to turn that vast world of chess into its shortest form without losing anything of its function.

FideLite — what the name and the logo mean

The FideLite logo: on a grid five squares wide, the piece moves leading out of the centre square are marked in colour The FideLite logo: on a grid five squares wide, the piece moves leading out of the centre square are marked in colour

The name has two parts: FIDE + Lite. Together they land on the sound of the French fidélitéfidelity, adherence. Which is precisely the point: the file shrinks, the adherence to the rulebook does not. “Lite” is the lightness, “Fide” the federation; the two at once, an arbiter that discards no article for the sake of being light.

The logo is built around a single square. The square at the centre is the origin; the remaining twenty-four squares are where pieces moving from that square can go.

  • Origin — the single square at the centre
  • Knight — eight neutral squares, the L-jumps
  • Rook — eight turquoise squares, four rays
  • Bishop — eight lilac squares, two diagonals
  • Queen — every rook and bishop square
  • King — the crimson frame, a one-step range
  • Pawn — the square its single-step advance lands on: the FIDElite wordmark