A correct cascade drop has a simple invariant: a symbol only moves if there are cleared slots beneath its original row. Getting this wrong is the classic cascade bug — naive implementations shift every visible cell up by the winner count, then tween back down, making untouched symbols visually jump.
pixi-reels computes per-cell fall distance from the winners list.
Survivors with no winners below them are never touched — no
animation job is created, their view.y stays exactly where
placeSymbols put it.
The algorithm, in one column
Per-row fall distance formula
For a column with visible rows indexed 0..V-1 and winner
rows winnerRows (sorted ascending), let
nonWinnerRows be the surviving row indices in order. For
each final row R:
| Final row R | Is it a new symbol? | Starting y offset (slots above target) |
|---|---|---|
R < winnerRows.length | yes — falls from above the viewport | R + 1 |
R ≥ winnerRows.length | no — survivor from nonWinnerRows[R - winCount] | R - originalRow (0 means: don't touch it) |
Crucially: when offsetRows === 0, the code returns early
and never creates an animation job for that cell. Its view.y
is already correct from placeSymbols, and tweening it would
be visually wrong.
Four canonical cases
One new symbol falls in at row 0. Every survivor stays put.
Survivors above the winner each fall 1 slot. Survivors below don't move.
Every survivor falls exactly 1 slot. New symbol fills row 0.
Two new symbols fall in. No survivor moves.
API surface
tumbleToGrid(reelSet, nextGrid, winners, opts)— the primitive. Takes aCell[]of real winners; per-reel fall distance math happens here.runCascade(reelSet, stages, opts)— orchestrates multiple stages. Default winner detection isdiffCells(prev, next); pass a customwinners: (prev, next) => Cell[]for match-pattern cascades.diffCells(prev, next)— utility, equal cells → zero. Fine for gravity-clean sequences; wrong for patterns where survivors' new rows hold different symbol ids than before.
The trap with diffCells
Consider prevCol = [A, C, X], X at row 2 wins.
The correct next col is [N, A, C] — new symbol at row 0,
survivors shifted down. diffCells reports three
changed cells (row 0: A→N, row 1: C→A, row 2: X→C). Treating those as
"three winners" would animate three new symbols falling from above,
instead of one new + two survivors sliding by one slot.
For pattern cascades, always pass your own winner list derived from the actual match, never from the diff. The remove-symbol recipe does exactly this.