pixi-reels
All recipes

Beginner: carry a value on a coin

The simplest thing — build your own symbol class that draws a coin with a number on it. Learn what a ReelSymbol is, the few methods the engine calls on it, and how to read the value back.

Loading recipe…

Press Run. Three coins land, each drawn by a tiny class you wrote, each showing the value it carries — and the HUD reads those values straight back off the symbols.

What is a symbol?

In pixi-reels a symbol is any subclass of ReelSymbol. The engine creates one instance per visible cell and calls a small set of methods on it as the reel spins and lands. You never construct symbols yourself — you register a class and the engine instantiates it.

Every symbol has a view — a PIXI.Container the engine puts on screen at the right spot. Whatever you addChild to this.view is your symbol.

The methods you must provide

class ValueCoin extends ReelSymbol {
  constructor(options) {
    super();
    this.value = options.value;   // the number this coin carries
  }
  onActivate() { this._draw(); }  // this cell just became this symbol
  onDeactivate() {}               // it's leaving — clean up if needed
  async playWin() {}              // win flourish (none here)
  stopAnimation() {}              // snap back to rest
  resize(width, height) {         // runs on EVERY swap, with the cell's pixels
    this._w = width; this._h = height;
    this._draw();
  }
}

The one that trips beginners up is resize(). It runs on every symbol swap with the cell’s pixel size, so anything position- or size-related goes here, not in the constructor — the constructor runs once, before the symbol knows how big its cell is.

Drawing the coin

_draw() clears the view and adds a gold disc plus the number, centered:

_draw() {
  if (this._w === 0) return;               // resize() hasn't run yet
  this.view.removeChildren();
  const r = Math.min(this._w, this._h) / 2 - 4;
  this.view.addChild(new PIXI.Graphics()
    .circle(this._w / 2, this._h / 2, r).fill(0xf6c945).stroke({ color: 0xb8860b, width: 3 }));
  const label = new PIXI.BitmapText({ text: String(this.value), style: { fontFamily: 'GoldDigits', fontSize: r * 1.1 } });
  label.anchor.set(0.5);                        // anchor + center = visually centered
  label.position.set(this._w / 2, this._h / 2);
  this.view.addChild(label);
}

The number is the game’s gold digit bitmap font (GoldDigits), not a system font — bitmap fonts stay crisp at any cell size and are what slot games ship. Load it once before building (await PIXI.Assets.load('/hw-spine/goldfont.fnt')). anchor.set(0.5) plus positioning at the cell’s center keeps it centered on the disc.

Registering and reading the value

The value is baked into the registration, so each id always carries its number:

.symbols(r => { for (const v of [1,2,5,10,20,50]) r.register(`coin${v}`, ValueCoin, { value: v }); })

Because the value is a property on the instance, you can read it back off any landed cell:

reelSet.reels[col].getSymbolAt(row).value   // → 25

Where to go next

This bakes the value into the class — perfect when the values are fixed. A real Hold & Win server sends a different amount per coin, so the next lesson keeps one generic coin and carries the number as data instead.