Loading recipe…
Press Spin. The reel spins, lands on the round’s paying symbols, then a random winning combination cascades — those symbols are removed, the survivors collapse to close the gaps, and new symbols slide in from the feed side. It’s a single row above the reels, not the reels themselves.
Same contract as ReelSet — it’s one reel
A banner reel is still a reel. HorizontalReel reuses the engine’s own types, so
there’s nothing new to learn and nothing incompatible to trip over:
const reel = new HorizontalReelBuilder()
.visibleCount(5).cellSize(76, 76, { gap: 8 })
.symbols((r) => { for (const c of CARD_DECK) r.register(c.id, CardSymbol, c); })
.ticker(app.ticker)
.build();
app.stage.addChild(reel.container);
const spin = reel.spin(); // starts scrolling, returns a promise
reel.setResult([{ visible: await server.pay }]); // ColumnTarget[] — this reel is one column
const { symbols } = await spin; // SpinResult: symbols is [[...row]]
setResult takes the same ColumnTarget[] as ReelSet.setResult — this reel
is a single column, so you pass exactly one entry whose visible holds
visibleCount ids. spin() resolves the engine’s SpinResult (symbols,
wasSkipped, duration); for one reel symbols is a one-column grid [[...]].
skipSpin(), isSpinning, and spin:start / spin:complete all match ReelSet.
Cascade — a real tumble, one row wide
When a symbol on the main reels was part of a winning combination, cascade the matching cells here. It’s the real tumble mechanic, not a swap-in-place:
- the winning symbols are removed (they poof out);
- the survivors collapse toward the settle edge to close the gaps, keeping their order;
- new symbols slide in from the feed edge — the same side the spin brings
symbols from (
rtlfrom the right,ltrfrom the left) — to refill.
// main reel reports cells 0 and 2 were in a win:
await reel.cascade([0, 2], ['WILD', 'K']); // 0 & 2 removed, survivors collapse, WILD/K slide in
// pass every index for a full "they all drop":
await reel.cascade([0, 1, 2, 3, 4]); // newIds default to random
cascade(winners, newIds?) resolves when the tumble settles and fires
cascade:complete with the winners and the collapsed + refilled window.
Why it’s a separate class
The engine’s Reel wraps symbols on the Y axis and bakes that in throughout
(ReelMotion, the stop sequencer, buffer rows). A sideways reel is a different
axis, so HorizontalReel is a focused mechanism on the same shared primitives
(the symbol pool, TickerRef, the typed EventEmitter) — it just exposes the
familiar reel API on top.
The surface
| Member | Use |
|---|---|
spin() | start spinning; resolves with the SpinResult |
setResult(target) | ColumnTarget[] of length 1; triggers the stop |
cascade(winners, newIds?) | real tumble: remove winners, collapse survivors, refill from the feed side |
skipSpin() | slam straight to the pending result |
isSpinning / isCascading | lifecycle state |
symbolAt(slot) | the live ReelSymbol in visible slot slot, left→right |
events | spin:start, spin:complete (SpinResult), cascade:complete |
destroy() | drops the ticker subscription and pooled instances |
Related
- Board primitive: build your own board — the other “small mechanism on shared primitives” in the library