Vector and CSS, not video: a few kilobytes, crisp at any resolution, every number driven by real match state. The vortex is the wait, the coins are the pot, and the seal in the corner is the fairness commitment — published before an opponent exists.
Same scene, same viewBox, slice cropping. A phone gets the vortex and the near rail; the room falls off the sides instead of the whole thing letterboxing into a stripe.
Nothing here is decoration for its own sake. Every element carries a piece of match state a player would otherwise have to be told in words.
| Portal on the cloth | Breathes while the queue is open. It is the only thing still moving if a connection drops, so it doubles as a liveness indicator |
| Ring speed | Slow while searching, slower still while widening, fast the instant a pairing lands, collapsing into the cloth on the countdown |
| Orbiting Ł coins | Three to eight, never more. Once the count saturates the coins are struck larger and darker-rimmed instead, on a log curve. Monotonic by construction — a bigger pot can never render as fewer or smaller coins |
| Shockwave and flash | Fire once, on pairing only. Being matched is the one moment in this screen worth a hard cut |
| Sweep meter | Cyan while searching, amber while widening. Colour carries the change before the words do |
| Draw seal | The commitment hash, shown before an opponent exists. That ordering is the proof — it cannot have been chosen to suit the pairing |
| Region lines | Owerri against Dakar is the product working. Surface it, because it is the part a screenshot sells |
| Centre scrim | A radial wash behind the state text. Without it, white type lands on the white vortex core and vanishes — the one place on this screen where legibility could fail outright |
| Pot size | Changes the coin count, the strike size and the ring weight. Switch it on the controls below and the scene rebuilds — it is data, not a painted backdrop |
| Rating gap | Named on pairing. A player who can see the gap is a player who does not suspect the matchmaker |
| Identicons | Derived from the handle with a hash, mirrored on the vertical. No upload means no moderation queue, no storage, no CDN — and the same player is the same tile on every device |
| Link chip | Latency and bars, because a player on a thin connection needs to know it is the network and not the game |
| Lamp pools | Two warm pools on the cloth under the outer lamps. The centre lamp's pool is eaten by the vortex, which is the point |
| Cancel copy | Says what happens to the money. "Minus nothing" is what makes a first-time player willing to queue at all |
Performance, because this is the screen most at risk. Cheap Android handsets over uneven data, and a full-screen effect is the fastest way to turn a four-minute queue into a hot phone. Everything animates transform and opacity only, so it stays on the compositor and never triggers layout. The Gaussian blurs are the expensive part — lite mode drops the depth-of-field and the lamp bloom along with the motes, tumble and twinkle, and it auto-enables under four cores or on a saveData hint rather than waiting to be found in settings. prefers-reduced-motion freezes everything into a composed still that also works as a store screenshot.
Accessibility. State changes go through a polite live region, so a screen-reader user hears "opponent found, ndiaga_s, Dakar" rather than nothing at all. The cancel button takes the hidden attribute at locked rather than going grey, so it leaves the tab order entirely once the window has closed. Escape cancels while that window is open and does nothing once it is not. In the app, move focus to Cancel when the screen mounts — this page does not, because stealing focus on a documentation page is rude.
| States | searching → widening → found → locked → table, with declined falling back to searching |
| Driven by | One data-state attribute. CSS does the rest — no JS animation loop anywhere |
| widening | Fires when the stake-keyed pool stays thin. Names the new band, so a player can decline by cancelling |
| found | Matchmaker pairing. Shockwave, flash, both cards slide in from their own sides |
| declined | Opponent never signed. Says plainly that the stake never moved rather than promising a refund — there is nothing to refund |
| locked | Both escrows co-signed. Countdown runs, vortex collapses into the portal |
| Suggested timings | Widen at 45 s, widen again at 90 s, offer a bot or a walk-away at 180 s. Never leave a player in searching with no change of state for longer than a minute |
| Mount API | CueMatchmaking.setState(), .setPot(), .setLang(), .setLite(), .getState() and .on('cancel'). Everything on this page runs through it, so there is no second code path to keep in sync |
| Languages | English and French, decimal commas included. French runs about 20% longer, which is why it is here — it is the layout test, not a nicety, given Dakar and Abidjan |
| Scene | 1280 × 720 viewBox, preserveAspectRatio="xMidYMid slice", instantiated twice with suffixed ids |