Loading recipe…
Anatomy of a cascade drop
A real cascade is not a new spin. and crucially, survivors with no cleared slots beneath them must not move at all. Three beats, all owned by the library:
- Pop.
reelSet.destroySymbols(winners)defers to each symbol’splayDestroy()(sprite implode by default; Spine subclasses route toout). - Replace.
reel.placeSymbols(nextGrid[reelIndex])swaps the symbol identities in place. Done internally byrefill(). - Drop. per survivor, the library computes a fall distance from how many winners sat below its original row. Survivors with 0 winners below them keep their y unchanged. New symbols at the top fall in with a staggered entrance.
The whole sequence lives behind two verbs:
const winners = detectMyWins(reelSet.getVisibleGrid());
await reelSet.destroySymbols(winners);
const next = await server.cascade(winners);
await reelSet.refill({ winners, grid: next.map((visible) => ({ visible })) });
refill({ winners, grid }) reads the gravity convention from grid and animates only the cells that actually need to move.
Winners are semantic, not diffed
The legacy “diff the two grids and call everything different a winner” trick is wrong for any cascade where survivors slide past cleared slots. The survivor’s new row holds a different symbol id than it used to. diffing would treat it as a new arrival from above, even though it should slide down instead.
Always compute winners from your match-detection logic:
import type { Cell } from 'pixi-reels';
function winnersOfX(grid: string[][]): Cell[] {
const out: Cell[] = [];
for (let reel = 0; reel < grid.length; reel++) {
for (let row = 0; row < grid[reel].length; row++) {
if (grid[reel][row] === 'x') out.push({ reel, row });
}
}
return out;
}
await reelSet.destroySymbols(winnersOfX(reelSet.getVisibleGrid()));
Chain it with runCascade
For multi-cascade rounds, reelSet.runCascade({ detectWinners, nextGrid }) is the one-call wrapper. same three beats, looped:
await reelSet.runCascade({
detectWinners: (grid) => winnersOfX(grid),
nextGrid: (prev, winners) => server.cascade(winners),
onCascade: ({ chain }) => hud.setMultiplier(chain),
});
detectWinners is your match logic. nextGrid is the gravity-correct post-refill grid: per reel, the top winners.length rows are new symbols, the rest are survivors in their original top-to-bottom order. Your server most likely already emits this shape; if not, do the transform client-side before returning.
Recycling
Recycled symbols reset view.alpha and view.scale automatically on acquisition, so you don’t need cleanup logic between stages.
Related
- Cascade 6×5 tumble. full cascade slot end-to-end.
- Cascades guide. the mental model behind
destroySymbols+refill+runCascade. cascade-multiplierdemo. same pop + drop, with a multiplier bumped per cascade.