Inside

Oblique is the game. Strata is the engine that plays it — a multidimensional rules engine, a from-scratch search and a direct WebGPU renderer, written in strict TypeScript with no game engine, no chess library and no imported camera controls. The constraint was the point, not an obstacle.

The constraint

Every piece of behaviour here had to be built: legality under royal safety in three dimensions, an exact undo, position hashing, a serialisation format, a renderer, a search. Reaching for a library would have answered a question the project exists to ask.

It buys something concrete. Because movement is data — a piece type maps to a primitive and a named direction set, and two primitives interpret every spec — there is no per-piece movement code anywhere, and a variant is a different table rather than a different program. The direction sets themselves are generated and frozen at load, with tests for their counts, their uniqueness and their inverses, because a hand-typed table of 24 signed permutations is a bug waiting for a quiet afternoon.

The rules engine

The domain layer is headless: no DOM, no canvas, no renderer. It runs in Node, in a Web Worker and in the test suite unchanged. State is immutable — applying a move returns a new position, and undo is its exact inverse, tested as such rather than assumed.

Legality is generated the honest way: pseudo-legal moves, applied, then rejected if they leave your own Sovereign attacked. Check, double check and pins fall out of that without special cases — and they are tested anyway, with constructed three-dimensional mating and stalemate nets, because a Sovereign with 26 flight cells makes for mating patterns that intuition from the flat game does not supply.

Position identity is a seeded Zobrist key maintained incrementally and checked against a full recomputation. The move tree is measured against recorded ground truth: 91 legal moves at depth one and 8,113 at depth two.

Two renderers, one game

The Canvas 2D layer view is the complete game and always has been: five level boards painted under a semantic grid of labelled buttons that owns every interaction, pointer and keyboard alike. The canvas is decorative; the meaning lives in the DOM, which is why keyboard play and screen-reader support are properties of the architecture rather than a later retrofit.

The WebGPU spatial projection is an enhancement over the same selection state: five translucent planes whose grid is computed procedurally in a fragment shader, instanced token solids, signed-distance selection marks, rays drawn through the lattice, a constrained orbit camera and 4× MSAA. If WebGPU is missing, fails, or is lost mid-session, the projection degrades to a single line of text and the game continues — enforced by a test that deletes navigator.gpu and plays a move anyway.

A third surface isolates one plane of the lattice — a level, a file-plane or a rank-plane — which is how you read a vertical line of attack without mentally rotating five boards. Below 640 px it stops being an aid and becomes the board itself.

The pieces are drawn from their own rules

A piece's form states its direction set. The Sentinel is two posts at the 2:1 offset it leaps by; the Lancer is a spire whose edges face the body diagonals; the Tower has one arm per planar axis and a finial for up. The flat token is the spatial model seen from above, because a flat board is a plan — so the two renderers show one object rather than two designs.

Height groups pieces by role and never by price: pawn, then minors, then Regent, then Sovereign. What a piece is worth is a research question, and the drawing is not allowed to answer it.

The opponent: Strata

Strata is iterative-deepening negamax with alpha-beta, a Zobrist-keyed transposition table, move ordering by transposition, promotion, captures, killers and history, and a quiescence search — running in a Web Worker at roughly 210,000 nodes per second in a browser tab. It never touches the main thread, and every change to the position cancels it.

Search speed came from a second board representation: a mutable make/unmake structure with packed moves and precomputed ray geometry, which took throughput from about 13,000 to about 145,000 nodes per second headless. It is a duplicate of the domain's semantics, which is dangerous, so a test suite compares the two cell by cell. That suite has already caught two real bugs.

The defect worth admitting. Quiescence originally searched every capture with no value-based pruning and no depth cap. In an open lattice that is ruinous: a fixed depth-2 search cost 2.3 million nodes by ply four, and research batches simply never finished. Static exchange evaluation, delta pruning and a depth cap cut the opening position 5.4× at depth 2 and 7.8× at depth 6. A twelve-game batch that had run for 26 minutes without finishing now takes 16 seconds.

Research, with its caveats attached

A new game has no received wisdom about what its pieces are worth, so the project measures rather than asserts. A headless self-play runner plays deterministic batches — fixed depth, seeded tie-breaking, a fresh engine per game, any single game reproducible in isolation — and records every metric as JSON and CSV.

The important negative result: symmetric self-play cannot price a piece. When both sides share a material table, a wrong price produces perfectly consistent games. Pricing needs an asymmetric match — two material tables, colours swapped every pair — which is a separate runner. It has so far settled the ordering — the Bishop is worth more than the Lancer, by 131 Elo over 400 games, against a null control that reads 50.3% — and put a floor under the gap: a ladder of matches says it is wider than the 40 centipawns the table currently uses, and cannot tell 120 from 200. Which of the two pieces is mispriced, the ladder cannot say, because it moved both at once.

The published material values remain hypotheses, and are labelled as such everywhere they appear. Presenting them as tuned would be the easiest lie in the project.

Things the tests are allowed to fail over

Some commitments are only real if something checks them, so several are measured rather than claimed:

There is also a diagnostics panel, off by default, reporting what a frame costs to build and what the worker boundary costs around a search. It is off by default in a strict sense: with it closed, the call sites do not so much as read a clock.

What is deliberately unfinished

Play it Read the rules