MultiWays
Each reel lands on a different cell count. Every spin.
The reel’s LENGTH is fixed. The cell size is derived:
reelExtent / visibleCells[i]. So a 2-cell reel has big cells, a 7-cell reel
has small ones.
The whole mechanic#
const reelSet = new ReelSetBuilder()
.reels(6)
.multiways({ minCells: 2, maxCells: 7, reelExtent: 480 })
.pinMigrationDuration(300) // ms for the pin-overlay tween. 0 = snap
.pinMigrationEase('power2.inOut') // any GSAP ease
.symbolSize(68, 68) // the SPIN-time cell size
.build();
const promise = reelSet.spin();
reelSet.setShape([3, 5, 7, 4, 6, 2]); // BEFORE setResult
reelSet.setResult(serverGrid); // serverGrid[i].visible.length === shape[i]
await promise;
Order matters#
spin()— reels scroll at the previous shape’s cell size.setShape(cellsPerReel)— records the target, emitsshape:changed, then migrates pins (pin:migrated). BeforesetResult. After it, the engine throws: frames are already cached by then.setResult(grid)—grid[i].visible.lengthmust equalshape[i].- Reshape, between SPIN and STOP. Per reel:
adjust:start, commit the geometry, refresh pin overlays,adjust:complete. StopPhaselands at the new shape.
Pin migration#
A pin remembers where it started (originCell). Every reshape moves it
according to its policy, and fires pin:migrated with
{ fromCell, toCell, clamped, reelIndex }.
'origin' — the default. Clamp, then restore.#
A pin squeezed down by a shrink climbs back when the reel grows again. This is what a sticky wild should do.
| Spin | Cells on reel 2 | originCell | Lands at | Clamped? |
|---|---|---|---|---|
| 1 | 5 | 4 | 4 | no |
| 2 | 3 | 4 | 2 | yes |
| 3 | 7 | 4 | 4 | restored |
| 4 | 4 | 4 | 3 | yes |
'frozen' — clamp, and stay.#
No restoring. Each clamp rewrites originCell, so the pin never climbs back.
Right for walking wilds, or anything where where-it-is-now is the truth and
the old position is history.
| Spin | Cells on reel 2 | originCell before | Lands at | originCell after |
|---|---|---|---|---|
| 1 | 5 | 4 | 4 | 4 |
| 2 | 3 | 4 | 2 (clamped) | 2 |
| 3 | 7 | 2 | 2 | 2 |
| 4 | 4 | 2 | 2 | 2 |
reelSet.pin(reel, cell, id, { migration: 'frozen' });
What animates, and what does not#
pinMigrationDuration and pinMigrationEase move pin overlays only.
The cells underneath snap. They have to: the reel is still spinning at full speed during the reshape, and tweening a cell would fight the motion layer for the same coordinate.
No pins on a reel? No tween is built. adjust:start and adjust:complete
still fire around the reshape, because they come from the reshape step, not
the tween. Nothing changed at all? Nothing fires.
skipSpin() bypasses the tween. Slam means now.
Events only MultiWays fires#
shape:changed—setShape()accepted a targetadjust:start/adjust:complete— per reel, around the reshapepin:migrated— a pin moved because the shape changed
Limits#
- No big symbols. A 2x2 cannot fit a reel that reshaped to 2 cells.
Throws at
build(). - No pyramid.
visibleCellsPerReel()andmultiways()both claim the cell count. Throws atbuild(). reelExtentis fixed. Change it, rebuild.
Cascades DO work with MultiWays (ADR 015). See multiways-cascade.
See also#
- Per-reel geometry — the overview
- Big symbols — the thing you cannot combine with this
multiways— basic per-spin reshapemultiways-cascade— reshape plus tumblesticky-wild-multiways— pin migration in action