A chess game with browser (WebGL), terminal (Sixel), and Vulkan GUI rendering backends, built in C# on .NET 10.
▶ Play it now in your browser — sebgod.github.io/chess — nothing to install; the full engine runs client-side as WebAssembly.
- Full chess rules: movement, captures, check, checkmate, stalemate, pawn promotion
- Player vs Player, Player vs Computer, and Custom Game (board editor) modes
- UCI protocol support — the engine runs as a separate process, communicating via standard UCI commands
- Terminal app: graphical board rendered using FreeType2 and the Sixel protocol (no ImageMagick dependency)
- Vulkan GUI app: standalone windowed app using SDL3 + Vortice.Vulkan
- Browser app: the full engine compiled to WebAssembly, rendered with WebGL2 — play online, nothing to install
- Play by Link: serverless correspondence chess in the browser, the GUI and the terminal — the whole game travels in the URL, so you can play someone on the other side of the planet by swapping links over any messenger. No accounts, no server, no logins
- Play online (browser): the same correspondence game, delivered for you — post a game in the lobby, or send a link, and moves arrive as they are played. Still no sign-up and no password: you are an anonymous player the moment you open the lobby. The one thing a link cannot do is find you an opponent you have not already met
- Move history panel with algebraic notation — click any move or use Ctrl+Arrow to review past positions
- Cross-platform: Windows, Linux, and macOS (x64 and ARM64)
- Native AOT compiled for fast startup and small footprint
- A terminal with Sixel support (e.g. Windows Terminal, mlterm, foot, WezTerm) is preferred for the console app; there is an ASCII-only fallback
- Vulkan-capable GPU for the GUI app
- A browser with WebGL2 for the browser app (anything from 2017 onwards); there is no software fallback
- .NET 10 SDK (to build from source)
The browser version is live at sebgod.github.io/chess — it runs entirely in your browser as WebAssembly, so there is nothing to install. It draws through WebGL2, which every browser has shipped since 2017; anything older (iOS 14 or earlier, Internet Explorer) gets an explanation instead of a board.
It includes Play by Link: choose Play by Link from the menu and pick your colour, make your move, then copy, email, or share the link with your opponent. The link is the game — opening one means it's your turn. The titlebar's "Play by link" panel explains the full flow.
It also includes online play: choose Network game, and either join a game somebody has posted or post one of your own and wait. Moves then arrive on their own, whether the game takes five minutes or five days. Sharing that game's link works too — it names the game rather than carrying it, so unlike a Play-by-Link URL it stays short and stays valid for the whole game.
Both are correspondence chess and both are optional: Play by Link needs no server at all, and online play needs no account — signing in is anonymous and happens only when you open the lobby. A build of this repository with no backend configured simply doesn't offer the online option, and everything else works unchanged.
Pre-built binaries are available on the Releases page for:
| Platform | Console | GUI |
|---|---|---|
| Windows x64 | chess-console-win-x64.tar.gz |
chess-gui-win-x64.tar.gz |
| Windows ARM64 | chess-console-win-arm64.tar.gz |
chess-gui-win-arm64.tar.gz |
| Linux x64 | chess-console-linux-x64.tar.gz |
chess-gui-linux-x64.tar.gz |
| Linux ARM64 | chess-console-linux-arm64.tar.gz |
chess-gui-linux-arm64.tar.gz |
| macOS ARM64 | chess-console-osx-arm64.tar.gz |
chess-gui-osx-arm64.tar.gz |
| macOS x64 | chess-console-osx-x64.tar.gz |
chess-gui-osx-x64.tar.gz |
git clone https://github.com/sebgod/chess.git
cd chess
dotnet build -c Release
dotnet run --project Chess.Console -c Release # Terminal app
dotnet run --project Chess.GUI -c Release # Vulkan GUI app
dotnet run --project Chess.Web -c Release # Browser app (Blazor WASM dev server)dotnet test -c Release| Key | Action |
|---|---|
a–h |
Select file (column) |
1–8 |
Select rank (row) — combines with pending file to select a square |
Esc |
Clear current selection |
F1 |
Toggle help |
Select a piece by typing its file + rank (e.g. e2), then type the target square (e.g. e4) to move. When a piece is already selected, typing just a rank moves it along the same file.
The desktop GUI can register itself as the handler for chess:// links, so clicking one opens the
game in the app rather than a browser. It is never registered automatically — chess ships as a
folder rather than an installer, so claiming a URL scheme is an explicit, reversible action:
chess-gui --register-protocol # per-user; no admin needed
chess-gui --unregister-protocolWindows writes HKCU\Software\Classes\chess; Linux writes a .desktop file under
~/.local/share/applications. macOS needs a real .app bundle, which this build doesn't produce, and
says so rather than pretending.
Clicking a link while the app is already running hands it to that window instead of opening a second one — including when the window is minimized, which it restores. A second plain launch (no link) always opens its own window, because two instances on one machine is how you play yourself over LAN.
| Key | Action |
|---|---|
Ctrl+L |
Copy the reply link for the game as it stands |
Ctrl+O |
Open a link your opponent sent you |
Or start straight from a link: chess-console --link "https://sebgod.github.io/chess/#g=e2e4".
The copy goes through the terminal's own clipboard (OSC 52), which works over SSH but is disabled by
default in a few terminals — if nothing lands, that is a terminal setting, not an error the app can
see. Ctrl+O opens a prompt you paste into with whatever your terminal uses for paste; it is
deliberately not bound to Ctrl+V, which is the paste shortcut itself in Windows Terminal and
others.
During a game, you can review past positions by navigating the move history. Click a move in the history panel, or use:
| Key | Action |
|---|---|
Ctrl+Left / Ctrl+Right |
Step back / forward by one ply |
Ctrl+Up / Ctrl+Down |
Step back / forward by one full move |
Esc |
Exit playback and return to the game |
When a pawn reaches the last rank, a popup appears with four piece choices. Click the desired piece, or use:
| Key | Piece |
|---|---|
n |
Knight |
b |
Bishop |
r |
Rook |
q |
Queen |
In Custom Game mode, you place pieces on the board before playing. The popup appears above the selected square.
| Key | Action |
|---|---|
a–h + 1–8 |
Select a square to place a piece on |
p |
Place Pawn |
n |
Place Knight |
b |
Place Bishop |
r |
Place Rook |
q |
Place Queen |
k |
Place King |
Tab |
Toggle between placing White and Black pieces |
Delete / Backspace |
Clear the selected square |
Esc |
Cancel the piece popup |
s |
Finish setup and start the game |
| Key | Action |
|---|---|
Up / Down |
Navigate menu items |
Enter |
Confirm selection |
1–3 |
Quick-select by number |
| Project | Description |
|---|---|
Chess.Lib |
Core chess library: board, pieces, rules, AI engine (negamax with alpha-beta pruning) |
Chess.UCI |
Shared UCI protocol library (parsing, formatting, client/server) |
Chess.Engine |
Standalone UCI engine executable (chess-engine), supports go depth N |
Chess.GUI |
Vulkan chess app (SDL3 + Vortice.Vulkan windowing/rendering) |
Chess.Console |
Terminal chess app with Sixel and ASCII display backends |
Chess.Web |
Browser app: Blazor WebAssembly, WebGL2 renderer, Play by Link, online play |
Chess.Tests |
xUnit v3 test suite |
| Package | Description |
|---|---|
| DIR.Lib | Device-independent rendering: geometry, colour, abstract Renderer<T>, RgbaImage, FreeTypeGlyphRasterizer |
| Console.Lib | Terminal I/O, dock-based layout, widgets, RgbaImageRenderer, Sixel encoding, truecolor/SGR-16 styling |
| SdlVulkan.Renderer | SDL3 + Vortice.Vulkan renderer: VkRenderer, VulkanContext, VkFontAtlas |
| WebGl.Renderer | WebGL2 backend for Blazor WASM: command-buffer JS interop, MSDF text from a shared atlas |
graph TD
DIR["DIR.Lib<br/><i>Geometry, colour, Renderer<T>,<br/>RgbaImage, FreeType rasterizer</i>"]
Lib["Chess.Lib<br/><i>Board, rules, AI engine (negamax)</i>"]
ConLib["Console.Lib<br/><i>Terminal I/O, layout, widgets,<br/>RgbaImageRenderer, Sixel encoding</i>"]
SdlVk["SdlVulkan.Renderer<br/><i>SDL3 + Vulkan renderer</i>"]
WebGl["WebGl.Renderer<br/><i>WebGL2 backend for Blazor WASM</i>"]
UCI["Chess.UCI<br/><i>UCI protocol library</i>"]
Engine["Chess.Engine<br/><i>Standalone UCI engine</i>"]
GUI["Chess.GUI<br/><i>Vulkan chess app</i>"]
Console["Chess.Console<br/><i>Terminal chess app</i>"]
Web["Chess.Web<br/><i>Browser app (Blazor WASM)</i>"]
Tests["Chess.Tests<br/><i>xUnit v3 + Shouldly</i>"]
Lib -- "NuGet" --> DIR
ConLib -- "NuGet" --> DIR
SdlVk -- "NuGet" --> DIR
UCI -- "project ref" --> Lib
Engine -- "project ref" --> Lib
Engine -- "project ref" --> UCI
GUI -- "NuGet" --> SdlVk
GUI -- "NuGet" --> DIR
GUI -- "project ref" --> Lib
GUI -- "project ref" --> UCI
GUI -. "launches as<br/>child process" .-> Engine
Console -- "NuGet" --> ConLib
Console -- "NuGet" --> DIR
Console -- "project ref" --> Lib
Console -- "project ref" --> UCI
Console -. "launches as<br/>child process" .-> Engine
WebGl -- "NuGet" --> DIR
Web -- "NuGet" --> WebGl
Web -- "project ref" --> Lib
Web -- "project ref" --> UCI
Tests -- "project ref" --> Lib
Tests -- "project ref" --> UCI
Tests -- "NuGet" --> ConLib
Console <-- "UCI over<br/>stdin/stdout" --> Engine
GUI <-- "UCI over<br/>stdin/stdout" --> Engine
