PR pixi-reels
All recipes

CardSymbol. debug / prototyping helper

A no-asset PIXI.Graphics symbol class for recipes, mechanic tests, and prototypes. Always crisp at any cell size. NOT for production. ship SpriteSymbol, AnimatedSpriteSymbol, or SpineSymbol instead.

Loading recipe…

CardSymbol is debug scaffolding, not production art. Real slots ship SpriteSymbol / AnimatedSpriteSymbol / SpineSymbol. This page exists to explain when CardSymbol IS the right tool. and when it isn’t.

What it is

CardSymbol lives in examples/shared/CardSymbol.ts. It’s a ReelSymbol subclass that renders a flat colored rectangle with a centered text label using PIXI.Graphics and PIXI.Text. no atlases, no loaders, no async asset boot. Two helpers ship with it:

  • CARD_DECK. eight high cards (7 8 9 10 J Q K A), each its own color. Ready to register as a full deck.
  • WILD_CARD. pale-yellow WILD card for sticky/expanding-wild prototypes.
import { CardSymbol, CARD_DECK, WILD_CARD } from '../../shared/CardSymbol';

builder.symbols((registry) => {
  for (const card of CARD_DECK) {
    registry.register(card.id, CardSymbol, { color: card.color, label: card.label });
  }
  // optional sticky-wild:
  registry.register(WILD_CARD.id, CardSymbol, {
    color: WILD_CARD.color,
    label: WILD_CARD.label,
    textColor: WILD_CARD.textColor,
  });
});

That’s the whole API. Pass { color, label, textColor? } per registration; the class handles its own resize, win flash, and text scaling.

When to use it

Use CardSymbol when:

  • Writing a recipe. Reviewers want a self-contained snippet they can read in 30 seconds, not a dependency on a sprite atlas.
  • Prototyping a mechanic. You’re fiddling with cascade physics or a feature mode, and you don’t want to wait for art.
  • Writing a test that needs visible cells. Debug __PIXI_REELS_DEBUG.snapshot() reads the cell ids regardless of rendering, but if you’re eyeballing the canvas during a manual test, CardSymbol is the fastest path.
  • Demonstrating cell-size behavior. MultiWays reshape, big-symbol blocks, pyramid layouts. all of these visibly change cell sizes. Sprites stretch ugly at extreme sizes; CardSymbol redraws crisply at every reshape.

When NOT to use it

CardSymbol is not what you ship to players:

If you want…Use
Pre-rendered art at one resolutionSpriteSymbol (cheapest, most common)
Frame-by-frame win/idle animationAnimatedSpriteSymbol
Vector skeletal animation that scales without quality loss across MultiWays reshapesSpineSymbol (via pixi-reels/spine)
Custom shader, particle effects, multiple Pixi DisplayObjects per cellSubclass ReelSymbol directly

The library exists to support those production symbols. CardSymbol is purely a developer-experience aid.

Why it lives in examples/shared/, not the library

This is intentional:

  • Library is small. pixi-reels is currently ~64 KB minified. Adding CardSymbol (with its PIXI.Text dependency on font rasterization) bloats the bundle for code 99% of consumers won’t ship.
  • Forces the right mental model. Importing from examples/shared/ makes it obvious this is debug code. Importing from pixi-reels would imply it’s a supported runtime path.
  • You can copy it. It’s ~100 lines. If you keep using it in your tests, vendor it into your own test/ folder; it’ll never need maintenance.

Resize behavior

resize(width, height) is called on every symbol swap and every reshape. The class redraws:

this._gfx.rect(0, 0, width, height).fill({ color: this._color });
this._gfx.rect(1, 1, width - 2, height - 2).stroke({ color: 0x000000, width: 2, alpha: 0.25 });
this._text.x = width / 2;
this._text.y = height / 2;
const labelLen = Math.max(1, this._label.length);
const fitH = height * 0.38;
const fitW = (width * 0.7) / (labelLen * 0.45);
this._text.style.fontSize = Math.max(7, Math.floor(Math.min(fitH, fitW)));

The font-size formula caps by both the cell height (~38%. leaves breathing room) and the label width (width * 0.7 / (labelLen * 0.45). the 0.45 factor matches Roboto Condensed’s narrow glyph ratio so multi-character labels like '10' or 'WILD' don’t overflow). The Math.max(7, …) floor keeps labels visible even on tiny 2-row MultiWays reshapes. Labels stay readable from 2-row MultiWays cells (~240 px tall) through 7-row cells (~68 px tall).

What CardSymbol skips

To keep it minimal, CardSymbol does not:

  • Cache anything. Every resize() redraws geometry from scratch. Cheap for Graphics but wasteful for production.
  • Pool internal Graphics/Text instances. One per CardSymbol.
  • Implement onActivate/onDeactivate beyond a no-op. The class’s identity is set at construction, not per-activation.
  • Handle pointer events, hover states, tooltips, or anything interactive.

A real production symbol would do most of the above. Don’t model your custom ReelSymbol on CardSymbol’s simplicity. model it on SpriteSymbol or AnimatedSpriteSymbol from the library.