pixi-reels
Building blocks

Buffer indexing

A reel is longer than what you see. ColumnTarget addresses the whole thing.

Why buffers exist#

Every reel keeps a few hidden cells past each end of the visible window.

They earn their keep three ways. They hold the next symbol about to wrap onto the strip. They scroll through view during a spin, so motion looks continuous. And they let a win animation pan past the edge without showing bare strip.

Set the count with .bufferSymbols(n). Most slots use 1 or 2. Above 2 is unusual.

ColumnTarget#

ColumnTarget
├─ visible       : string[]                    # length = visibleCells
├─ bufferStart?  : (string | undefined)[]      # [0] closest to visible
└─ bufferEnd?    : (string | undefined)[]      # [0] closest to visible

One object per reel. Hand an array of them to ReelSet.setResult or ReelSetBuilder.initialFrame. Same shape, both places.

Start is the smaller screen coordinate. Above on a vertical set, left on a horizontal one. It does NOT follow travel direction. See Orientation & direction.

Which index is which cell#

One reel. bufferSymbols(2), visibleCells(3). Seven cells total:

┌────────────────────┬──────────────────┬────────────────────────────┐
│ ColumnTarget field │ Cell             │ Player sees it at rest?    │
├────────────────────┼──────────────────┼────────────────────────────┤
│ bufferStart[1]     │ buffer start 1   │ no, furthest past the edge │
│ bufferStart[0]     │ buffer start 0   │ no, just past the edge     │
│ visible[0]         │ visible cell 0   │ yes, first                 │
│ visible[1]         │ visible cell 1   │ yes, middle                │
│ visible[2]         │ visible cell 2   │ yes, last                  │
│ bufferEnd[0]       │ buffer end 0     │ no, just past the edge     │
│ bufferEnd[1]       │ buffer end 1     │ no, furthest past the edge │
└────────────────────┴──────────────────┴────────────────────────────┘

Buffers hide behind the mask at rest. They flash through during a spin.

Example#

import type { ColumnTarget } from 'pixi-reels';

const grid: ColumnTarget[] = [
  { visible: ['A', 'B', 'C'], bufferStart: ['COIN', 'WILD'] },
  { visible: ['A', 'B', 'C'], bufferEnd: ['SCATTER'] },
];

reelSet.setResult(grid);
// Same shape at build time:
builder.initialFrame(grid);

bufferStart[0] is the slot nearest the visible window. [1] is one further out. Anything past bufferSymbols is dropped — and the engine throws rather than dropping it silently, so you find out at the call, not three spins later.

Cells you skip#

Anything you leave unset gets a random symbol.

Buffer cells are filled in the same pass. By default they draw from the same weight table as the strip, but they do not have to. A pool with slots: 'buffer' narrows the off-window cells only:

// A coin may blur past mid-spin, but must never park half-visible
// above or below the grid.
reelSet.randomSymbols.set({ exclude: ['COIN'] }, { slots: 'buffer' });

// Same thing at build time, so even the initial strip obeys it.
builder.randomSymbols({ exclude: ['COIN'] }, { slots: 'buffer' });

The two ends take their own pools, under the names this page already uses:

// Nothing peeks in from above; the cell below the grid is left alone.
reelSet.randomSymbols.set({ exclude: ['COIN'] }, { slots: 'bufferStart' });

Add { reel: n } to any of them for one reel only — { reel: 2, slots: 'bufferEnd' } is one end of one reel. Weights work the same way as exclusions: { weights: { COIN: 0 } } bans it just as surely. Each layer stacks on the wider ones: what the strip may not show, no buffer cell may show either, and what a 'buffer' pool bans is banned at both ends whatever the side pools say. An explicit bufferStart / bufferEnd target is the game speaking and always wins over a pool.

Big symbols in buffers#

A big-symbol anchor (size.cells > 1) can sit in bufferStart[i], or in a visible cell whose block spills into bufferEnd. Pass the anchor. The coordinator paints OCCUPIED across the rest of the block for you.

The rule: the block must fit on the STRIP, not inside the visible window. anchor + cells <= visibleCells + bufferEnd. That is what makes a tail-visible block possible — most of it hidden, one cell peeking in.

See also#