pixi-reels
All recipes

Horizontal reel (paying-symbol banner)

The "these symbols pay this round" banner above the reels, built with HorizontalReel — a single sideways reel. Same spin()/setResult()/SpinResult contract as ReelSet (one ColumnTarget), plus a real cascade() tumble that removes the winning cells, collapses the survivors, and refills from the feed side.

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:

  1. the winning symbols are removed (they poof out);
  2. the survivors collapse toward the settle edge to close the gaps, keeping their order;
  3. new symbols slide in from the feed edge — the same side the spin brings symbols from (rtl from the right, ltr from 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

MemberUse
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 / isCascadinglifecycle state
symbolAt(slot)the live ReelSymbol in visible slot slot, left→right
eventsspin:start, spin:complete (SpinResult), cascade:complete
destroy()drops the ticker subscription and pooled instances