Skip to content

2026-07-29 — Table lab parity: the public lab drives the real felt

The interactive geometry lab in the public explainer (/notebook/table-geometry, and its standalone /lab) used to be a drawing of the table: 530 lines of SVG sprite builders on a virtual viewBox, fed by the real engine's numbers. It is now the table. Engine mode mounts the shipped BratTableV3 — the same component the game renders, with a fixture roster and spectator-inert props — and every knob drives it through the felt's own geometryOverrides. Drag seatSpacingH in the article and the production component re-solves in front of you.

The stage that hosts it is real px, not a CSS scale: the felt measures its own container, so it is fitted into the article's canvas (a transformed box would report one size and lay out at another).

Parity, and only one definition of it. The two labs — the in-app /dev/table-lab and the public one — had been drifting into two copies of the same thing: band guides, frame-path sampling, gap ticks and the aspect-regime readout were duplicated line-for-line, so a geometry change meant two lockstep edits and a lab that quietly disagreed with the felt. All of it now lives in packages/game-ui/src/lab/, next to the knob registry that cured the same disease for the sliders: the measured stage (LabStage), the geometry overlay and its band/frame definitions (LabOverlay, labBandGuides, labFramePath), the stage presets + free width/height range, the copy-config emitter, and the regime readout. One definition, both labs — and a grep gate fails the build if a lab grows its own copy back.

What the public lab gained on the way: the real felt, the shared overlay's pot and frame-center markers and two-ray bet-anchor rings, device presets, free width/height stage controls, and Copy config — so a tuning session in the article produces the same paste-ready constants block the in-app lab emits, and can be carried straight back into the source.

The article's teaching modes are unchanged and deliberately still drawn: equal arc and equal angle place seats where the engine would not, and BratTableV3 owns seat placement itself — so those two modes keep the sprite renderer, and the shared overlay never claims the engine's rim over a wrong placement.