Slot Game Responses
This guide explains how to read the scenario field returned by placeBet for slot games. The shapes used below are published as @hizi.io/slot-types — install it to type-check your scenario handling on the frontend.
Overview
Slot game databases are generated by the slot engine that ships inside hizi-engine-creator (the creator's playGame() produces the IResult objects that get stored as scenarios). At runtime, gameResult.scenario contains exactly that structure.
For single-result spins, the scenario is a single IResult object. For multi-result spins (cascades, hold & win), the scenario is an array of IResult objects — one per step.
The IResult Structure
interface IResult {
featureName?: string;
board: TBoard;
wins?: IWin[];
winAmount: number;
symbolSubstitutions?: ISymbolSubstitutionData[];
duplicatingSymbolData?: IDuplicatingSymbolData[];
stuckSymbols?: TStuckSymbols;
bookSpinChosenSymbol?: string;
bookSpinWin?: IBookSpinWin;
spinMultiplierTotal?: number;
multiplierCollectionData?: IMultiplierCollectionData[];
cascadeData?: ICascadeData;
boardStates?: IBoardState[];
holdAndWinData?: IHoldAndWinData;
wheelWins?: IWheelWin[];
randomFeatureEntry?: string;
featureMultiplier?: number;
collectorTargetData?: ICollectorTargetData[];
collectorLevelData?: ICollectorLevelData;
collectedSpinMultiplierData?: ICollectedSpinMultiplierData;
reelRows?: number[];
cascadeMultiplierValue?: number;
featureRoundId?: number;
spinInfo?: ISpinInfo[]; // consolidated rounds only — see Feature progression below
nextFeature?: string; // consolidated rounds only — see Feature progression below
}Most fields are optional and only present when the corresponding feature is active. A minimal base game result without features contains just board, wins, and winAmount.
Reading the Board
board is a 2D array indexed by column (reel), then row (top to bottom):
type TBoard = Array<Array<string>>;const scenario = gameResult.scenario as IResult;
// A 5×3 slot: 5 columns, 3 rows each
// scenario.board[0] = ['cherry', 'lemon', 'bar'] ← reel 0
// scenario.board[1] = ['seven', 'cherry', 'cherry'] ← reel 1
// ...
for (let col = 0; col < scenario.board.length; col++) {
for (let row = 0; row < scenario.board[col].length; row++) {
const symbol = scenario.board[col][row];
// Render symbol at grid position (col, row)
}
}Reading Wins
wins is an array of IWin objects. Each describes one winning combination:
interface IWin {
mode: 'winline' | 'ways' | 'scatter' | 'cluster' | 'unconditional' | 'collector';
positions: Array<{ column: number; row: number }>;
winMultiplier?: number;
featureWin?: { featureName: string; featureCount: number };
overrideWins?: boolean;
symbolMultiplierTotal?: number;
spinMultiplierValue?: number;
multipayCount?: number;
collectedSymbols?: ICollectedSymbol[];
}Highlighting winning positions
if (scenario.wins) {
for (const win of scenario.wins) {
for (const pos of win.positions) {
// Highlight the symbol at (pos.column, pos.row)
}
console.log(`${win.mode} win: ${win.winMultiplier}x`);
}
}Win modes
| Mode | Description |
|---|---|
winline | Consecutive matching symbols along a configured payline |
ways | Matching symbols in adjacent columns (left-to-right or both ways) |
scatter | Matching symbols anywhere on the board |
cluster | Adjacent matching symbols forming a connected group |
unconditional | Awards that fire on every spin regardless of symbols |
collector | Collector symbols gathering target symbol values |
Feature triggers
A win can trigger bonus features via featureWin:
for (const win of scenario.wins ?? []) {
if (win.featureWin) {
console.log(`Triggered ${win.featureWin.featureName} × ${win.featureWin.featureCount}`);
}
}featureWin only tells you what triggered. To track progression — which feature a spin belongs to, how many spins remain, what plays next — read spinInfo / nextFeature / currentFeature. Where those live depends on the round type; see Feature progression for the unified accessor that works for both. For consolidated slot rounds they are baked onto the scenario IResult (scenario.spinInfo, scenario.nextFeature, scenario.featureName); otherwise they come from engineData.
Override wins
When overrideWins is set on a win, only the highest-paying override-win on the spin is paid; all other wins on that spin are suppressed. Use this to detect wins that should be rendered exclusively (e.g. a jackpot scatter that hides the line wins).
Multipay scatters
multipayCount is set on scatter wins when the configured award uses multipay. The win's winMultiplier already includes the multipay scaling — multipayCount is just the multiplier so you can show "2× scatter pay" alongside the win.
Win Multipliers
A win's payout (winMultiplier) can be modified by several multipliers, each surfaced on the win or the spin so you can render the breakdown without recomputing it:
| Field | Where | Meaning |
|---|---|---|
win.winMultiplier | per-win | Final win value, after every multiplier on this win has been applied |
win.symbolMultiplierTotal | per-win | Combined multiplier from symbols-with-multipliers in this win (uses the slot's symbolMultiplierBehaviour: highest / multiply / add). Only present when > 1 |
win.spinMultiplierValue | per-win | A spin-multiplier award contributed by this win (e.g. multiplier symbols on the board). Aggregated across all wins into result.spinMultiplierTotal |
result.spinMultiplierTotal | per-spin | Sum of every win's spinMultiplierValue on this spin, applied to result.winAmount. Only present when > 0 |
result.cascadeMultiplierValue | per-spin | Final cascade multiplier value after all cascade steps. Only present when cascadeMultiplier is configured |
result.featureMultiplier | per-spin | Effective feature-level Win Multiplier applied to this spin's wins. Source-agnostic — set whether it came from featureConfig.multiplier (static), randomMultiplierOptions (rolled once per run), or perSpinRandomMultiplierOptions (rolled fresh every spin). Only present when ≠ 1×. See Win Multiplier |
winAmount is the final value the spin paid out; the multipliers above are a breakdown for display purposes — multiplying them yourself will not always reproduce winAmount exactly because rounding happens at intermediate steps.
Symbol Substitutions and Duplications
When the slot config has symbolSubstitutions (e.g. wilds turning into specific paying symbols) or symbols with duplicatingCount (a symbol that copies itself onto another position), the engine reports what happened so you can animate the transformation:
interface ISymbolSubstitutionData {
position: { column: number; row: number };
originalSymbol: string;
newSymbol: string;
}
interface IDuplicatingSymbolData {
sourcePosition: { column: number; row: number };
targetPosition: { column: number; row: number };
symbol: string;
}for (const sub of scenario.symbolSubstitutions ?? []) {
// Animate sub.originalSymbol turning into sub.newSymbol at sub.position
}
for (const dup of scenario.duplicatingSymbolData ?? []) {
// Animate dup.symbol jumping from dup.sourcePosition to dup.targetPosition
}scenario.board is always the final post-transformation board. Use boardStates (see Board States) when you need the pre-transformation board too.
Cascades / Tumbles
When cascades are enabled, cascadeData tracks the full tumble sequence within a single spin:
interface ICascadeData {
count: number; // Number of cascade iterations (0 = no cascades)
steps: ICascadeStep[]; // Each step of the cascade
}
interface ICascadeStep {
inputBoard?: TBoard; // Board before transformations
board: TBoard; // Board at this step
wins: IWin[]; // Wins found at this step
winAmount: number; // Win from this step
cascadeMultiplier?: number; // Active multiplier on this step (only when cascadeMultiplier is configured)
}When the slot config has cascadeMultiplier enabled, the multiplier ramps up across cascade steps. Each step's cascadeMultiplier is the value applied to that step's wins; the final value is also surfaced on the spin's top-level cascadeMultiplierValue.
if (scenario.cascadeData) {
for (let i = 0; i < scenario.cascadeData.steps.length; i++) {
const step = scenario.cascadeData.steps[i];
// step.board = board state at this cascade step
// step.wins = wins found at this step
// step.winAmount = payout from this step
// Animate: remove winning symbols, drop new ones, show next board
}
}Step 0 is the initial evaluation. Steps 1+ are cascade iterations after winning symbols were removed and new symbols dropped in.
TIP
When cascade is active with multi-result scenarios, each placeBet call returns one step. Use scenarioInfo.inProgress to check if more cascade steps follow.
Hold & Win
holdAndWinData tracks the state of a hold-and-win (respin) feature:
interface IHoldAndWinData {
multipliers: (number | null)[][]; // Payout multiplier at each position
symbols?: (string | null)[][]; // Symbol at each stuck position
livesRemaining: number; // Remaining respins
totalMultiplier: number; // Sum of all placed multipliers
ended: boolean; // Feature complete?
}if (scenario.holdAndWinData) {
const hw = scenario.holdAndWinData;
// Render the grid: non-null entries in hw.multipliers are stuck symbols
for (let col = 0; col < hw.multipliers.length; col++) {
for (let row = 0; row < hw.multipliers[col].length; row++) {
if (hw.multipliers[col][row] !== null) {
// Show stuck symbol with its multiplier value
}
}
}
console.log(`Lives: ${hw.livesRemaining}, Total: ${hw.totalMultiplier}x`);
}Hold & Win results are bundled as a multi-result scenario — the trigger spin and all H&W respins come as sequential steps.
Sticky Symbols
During features with sticky symbols, stuckSymbols tracks which positions are locked:
type TStuckSymbols = Array<Array<string | null>>;
// Same shape as board - null means the position is not stuckif (scenario.stuckSymbols) {
for (let col = 0; col < scenario.stuckSymbols.length; col++) {
for (let row = 0; row < scenario.stuckSymbols[col].length; row++) {
if (scenario.stuckSymbols[col][row] !== null) {
// This position has a locked symbol that persists across spins
}
}
}
}Multiplier Collection
multiplierCollectionData tracks accumulated multiplier progress within a feature:
interface IMultiplierCollectionData {
symbol: string; // Symbol being collected
count: number; // Total collected across all spins
multiplier: number; // Current active multiplier
target: 'winline' | 'ways' | 'scatter' | 'cluster' | 'bookwinline' | 'totalwin';
}if (scenario.multiplierCollectionData) {
for (const mc of scenario.multiplierCollectionData) {
console.log(`${mc.symbol}: collected ${mc.count}, multiplier ${mc.multiplier}x on ${mc.target} wins`);
}
}Collected Spin Multiplier
For games like Gates of Olympus where multiplier symbols accumulate within a cascade or feature:
interface ICollectedSpinMultiplierData {
collectedMultiplier: number; // Accumulated multiplier across spins
appliedMultiplier: number; // Multiplier applied to this spin's wins (0 = not applied)
}Win Multiplier
Every feature spin can carry a Win Multiplier from one of three sources, all exposed on the result via the same featureMultiplier field — so consumers don't need to branch on the source:
| Source | Config | Behaviour |
|---|---|---|
| Static | featureConfig.multiplier | Flat multiplier applied to every spin of every run of the feature. |
| Run-level random | featureConfig.randomMultiplierOptions | Engine rolls one value from the weighted list at the start of each feature run; the same value applies to every spin of that run. |
| Per-spin random | featureConfig.perSpinRandomMultiplierOptions | Engine rolls a fresh value from the weighted list on every individual feature spin, so the multiplier varies spin-to-spin within one run. Cascades within a single spin keep that spin's value (cascades are intra-spin). |
Precedence when more than one is configured: per-spin > run-level > static.
if (scenario.featureMultiplier !== undefined) {
// e.g. show "5x" alongside the feature wins
console.log(`Feature multiplier: ${scenario.featureMultiplier}x`);
}The field is omitted when the effective multiplier is 1× (no feature multiplier configured, or base spins).
Per-spin random and the DB shape. Per-spin random forces the feature into the consolidated/grouped DB path, the same way randomMultiplierOptions does — outcomes can't be pre-baked when the multiplier is rolled at runtime, so all spins of the run land in a single grouped scenario entry. Configurations that need each freespin to come from an independent scenario table should use multiple feature configs with different static multipliers and route entries through a player-choice or weighted award instead.
Hold-and-Win. Per-spin random is ignored on Hold-and-Win features — H&W settles cumulatively at the end of the respin sequence, where a per-spin pick has nowhere meaningful to land. H&W falls back to the run-level pick (or static), and featureMultiplier reflects that single applied value.
Random-entry preview spins. When a base spin is replaced by a feature-style preview via featureRandomEntryConfig, the preview spin picks its own multiplier (per-spin if configured on the substituted feature, otherwise run-level) independently of any surrounding feature run.
Wheel Features
wheelWins contains results from wheel bonus spins:
interface IWheelWin {
selectedSegment: number; // Index of the landing segment
winAmount?: number; // Cash prize (if any)
featureAward?: IFeatureWin; // Feature triggered (if any)
}Board States
boardStates provides named snapshots showing intermediate board transformations:
interface IBoardState {
name: string; // e.g. 'Generated', 'After Sticky', 'Book Expanded'
board: TBoard;
}These are useful for animating board transformations step by step — for example, showing the raw generated board before expanding wilds or applying sticky symbols.
Book Spins
During book-spin features:
bookSpinChosenSymbol— the expanding symbol selected for the featurebookSpinWin— additional win data from the book expansion:
interface IBookSpinWin {
symbolWinAmount: number; // Win from the chosen expanding symbol
totalWin: number; // Total book spin win multiplier
}Collector Features
When collector mechanics are active, the spin reports per-symbol target values as well as the player's level progression:
interface ICollectorTargetData {
symbol: string; // Target symbol on the board
position: { column: number; row: number };
value: number; // Weighted random value assigned to this target instance
}
interface ICollectedSymbol {
symbol: string; // Target symbol gathered by the collector
position: { column: number; row: number };
value: number; // Payout value assigned to this target
}
interface ICollectorLevelData {
currentLevel: number; // 0-based index into the levels array
collectedCount: number; // Collected in the current level (resets on level up)
totalCollected: number; // Total across all levels in this feature
}scenario.collectorTargetDatalists the values rolled for each target symbol present on the board this spin (independent of whether they were gathered).win.collectedSymbols(oncollectormode wins) lists the targets actually gathered by a collector win and the value awarded for each.scenario.collectorLevelDatais the level-progression state after this spin, when the slot has acollectorLevelConfig.
if (scenario.collectorLevelData) {
const cl = scenario.collectorLevelData;
console.log(`Level ${cl.currentLevel}, collected ${cl.collectedCount} this level, ${cl.totalCollected} total`);
}Megaways (Variable Reel Heights)
When the slot has megaways configured, each reel generates a random number of rows per spin. The chosen heights are reported on reelRows so you can size the grid before rendering symbols:
if (scenario.reelRows) {
// scenario.reelRows[col] = number of rows on reel col for this spin
// scenario.board[col].length matches scenario.reelRows[col]
}reelRows is only present when the active config (base or feature) has megaways enabled.
Random Feature Entry
If the slot config has featureRandomEntryConfig, a base spin can be replaced with a feature-style spin (the feature's symbols, awards, and multiplier are applied). The spin's randomFeatureEntry carries the name of the feature whose config was used:
if (scenario.randomFeatureEntry) {
console.log(`This spin used the "${scenario.randomFeatureEntry}" feature config`);
}A random-entry spin can also queue additional spins of the same feature (when the feature's count > 1) — those queued spins are normal feature spins and have featureName set instead.
Feature Run Identification
featureRoundId is a 1-based counter that distinguishes separate runs of the same feature within one round. If a feature triggers, completes, then re-triggers from a base spin or wheel award later in the round, the second run gets featureRoundId: 2:
// Group feature spins by run
const runs = new Map<string, IResult[]>();
for (const r of allResults) {
if (r.featureName && r.featureRoundId !== undefined) {
const key = `${r.featureName}#${r.featureRoundId}`;
if (!runs.has(key)) runs.set(key, []);
runs.get(key)!.push(r);
}
}Hold-and-win in particular uses this to count respins within a single trigger: every step of the run carries the same featureRoundId, so filtering on featureName + featureRoundId gives you the spins of one respin sequence.
Single vs Multi-Result Scenarios
Single-result (most spins): gameResult.scenario is one IResult object.
Multi-result (cascades, H&W): gameResult.scenario is an array of IResult objects, and engineData.scenarioInfo.inProgress is true. Each placeBet call returns the next step in the sequence.
// Check if this is a multi-result scenario
if (gameResult.engineData.scenarioInfo.inProgress) {
// Current step index
const step = gameResult.engineData.scenarioInfo.currentScenarioIndex;
// More steps to come — call placeBet({ backendURL, token })
}Minimal Example
import { placeBet, IGameResult } from '@hizi.io/engine-sdk';
// Define your scenario type matching hizi engine slot's IResult
interface ISlotScenario {
board: string[][];
wins?: Array<{
mode: string;
positions: Array<{ column: number; row: number }>;
winMultiplier?: number;
featureWin?: { featureName: string; featureCount: number };
}>;
winAmount: number;
cascadeData?: {
count: number;
steps: Array<{ board: string[][]; wins: any[]; winAmount: number }>;
};
holdAndWinData?: {
multipliers: (number | null)[][];
livesRemaining: number;
totalMultiplier: number;
ended: boolean;
};
featureMultiplier?: number;
// ... other optional fields as needed
}
const response = await placeBet({ backendURL, token, stake });
if (response.success) {
const { scenario, engineData, totalWin } = response.result.result;
const slot = scenario as ISlotScenario;
// Render the board
renderBoard(slot.board);
// Highlight wins
for (const win of slot.wins ?? []) {
highlightPositions(win.positions);
}
// Handle cascades
if (slot.cascadeData) {
for (const step of slot.cascadeData.steps) {
await animateCascade(step);
}
}
// Continue if round is in progress
if (engineData.inProgress) {
await placeBet({ backendURL, token });
}
}Supported Features
A complete reference of every mechanic the slot engine can model. The "Example" column points to a recognisable commercial slot using the same mechanic — useful shorthand when discussing configurations with designers. The configuration field shows the relevant key on ISlotConfig, ISymbol, IAwardTrigger, or IFeatureConfig.
Grid & reels
| Mechanic | Config | Example | Notes |
|---|---|---|---|
| Fixed grid | columns, rows | Starburst (NetEnt) | Standard fixed grid. |
| Variable reel heights (Megaways) | megaways | Bonanza Megaways (Big Time Gaming) | Each reel rolls a random number of rows per spin within [minRows, maxRows]; per-reel ranges via reelRowRange. |
| Fixed reel strips (reelsets) | reelSetConfig | Mechanical-style NetEnt slots | Board generation picks a window of rows consecutive symbols from each strip; multiple reelsets can be weighted against each other. When enabled, per-symbol reel weights and stacking are bypassed. |
Symbols
| Mechanic | Config | Example | Notes |
|---|---|---|---|
| Wilds | symbol.isWild | 20 Super Hot (EGT) | Substitutes for paying symbols. |
| Expanding wilds | symbol.isExpandingWild | Starburst (NetEnt) | Wild expands to fill its reel. |
| Wild scatters | symbol.isWildScatter | Book of Ra (Novomatic) | Wild that also counts toward scatter awards (per-award opt-in via canIncludeWildScatters). |
| Per-reel symbol weighting | symbol.reelWeights[] | virtually every modern slot | Different probability per reel. |
| Reel spacing | symbol.reelSpacing | high-volatility scatter symbols | Minimum stop distance between occurrences on a reel. |
| Symbol stacking | symbol.minStacksymbol.maxStacksymbol.averageStack | Buffalo (Aristocrat) | A symbol either appears alone or in a contiguous group on a reel; averageStack skews stack sizes toward a target. |
| Symbol multipliers | symbol.multipliersymbolMultiplierBehaviour | Sweet Bonanza (Pragmatic Play) | Multiplies the win when this symbol is part of it. Combination across multiple multiplier symbols: highest / multiply / add. |
| Multi-position symbols | symbol.winlineCount | "double-wide" symbols in various Pragmatic Play / BTG titles | A symbol counts as N adjacent positions for win length. |
| Duplicating symbols | symbol.duplicatingCount | various "cloning wild" mechanics | Symbol clones itself onto another position before evaluation. |
| Symbol substitutions | symbolSubstitutions[] | Mystery Symbols (Fishin' Frenzy series — Reel Time Gaming) | One symbol transforms into another via weighted pick. uniformSubstitution makes all source occurrences resolve to the same target; noSubstitutionWeighting lets the source survive untransformed. |
Win evaluation modes
| Mechanic | Config | Example | Notes |
|---|---|---|---|
| Paylines | award.mode: 'winline'winlines[] | Book of Ra (Novomatic) | Configured payline patterns. |
| Ways pays | award.mode: 'ways' | 243 Ways (Microgaming) | Adjacent-reel matching, no payline patterns. |
| Scatter pays | award.mode: 'scatter' | universal | N matching symbols anywhere on the board. |
| Multipay scatter | award.multipay | various | Scatter pays floor(count / N) times for big counts. |
| Cluster pays | award.mode: 'cluster'adjacency | Aloha! Cluster Pays (NetEnt), Reactoonz (Play'n GO) | Connected groups of matching symbols. adjacency is fourWays (default) or eightWays. |
| Unconditional awards | award.mode: 'unconditional' | bonus picks, "always pays" mechanics | Fires every spin regardless of board state. |
| Collector awards | award.mode: 'collector'collectsSymbols | Money Train (Relax Gaming) | A collector symbol gathers values from target symbols on the board into one win. |
| Override wins | award.overrideWins | jackpot lines, super-wild wins | Only the highest override-win pays — suppresses other wins on the spin. |
| After-cascade evaluation | award.afterCascadeaward.cascadePersist | various | Award is evaluated only on the final post-cascade board (or its symbols persist through cascades). |
| Range-based scatter triggers | award.maxCount | bonus tiers (e.g. 3/4/5 scatters → different awards) | Award only triggers when the symbol count is within [count, maxCount]. |
Cascades (tumble mechanics)
| Mechanic | Config | Example | Notes |
|---|---|---|---|
| Cascading wins | cascade | Gonzo's Quest (NetEnt) | Winning symbols are removed; remaining drop down; new symbols fill from the top; repeats until no win. |
| Progressive cascade multiplier | cascadeMultiplier | Sweet Bonanza (Pragmatic Play), Sugar Rush (Pragmatic Play) | Multiplier ramps up across cascade steps. |
| First-step substitutions only | cascadeSubstitution | various | Substitutions apply only on the first cascade step (default is every step). |
Bonus features
| Mechanic | Config | Example | Notes |
|---|---|---|---|
| Free spins | featureConfig.type: 'freespins' | universal | Standard bonus spins. |
| Bonus spins (lower priority) | featureConfig.type: 'bonusspins' | Book of Holla: Bonus Spins (Hoelle Games) | Only run when no free-spins feature has remaining spins. |
| Wheel features | featureConfig.type: 'wheel' | Mega Moolah (Microgaming), Wheel of Fortune (IGT) | Single weighted segment pick; segment can pay cash, trigger another feature, or both. |
| Hold and Win (respin) | featureConfig.holdAndWin | Lightning Link (Aristocrat) | Coin symbols stick; respin counter resets on every hit (lives); ends on grid-full or 0 lives. fullScreenBonus, symbolWeights, blankWeight, payouts shape the maths. |
| Book Spins (expanding feature symbol) | featureConfig.bookSpinSymbolAllowList | Book of Ra (Novomatic), Book of Dead (Play'n GO) | One symbol from the allow-list is rolled at feature start; on each spin it expands to fill all positions and pays as a winline regardless of position. |
| Random feature entry | featureRandomEntryConfigfeatureRandomEntryBaseWeighting | random hold-and-win drops on a base spin | Weighted chance to replace a base spin with a feature-style spin (using the feature's full config); can also queue additional feature spins. |
| Static feature multiplier | featureConfig.multiplier | "wins multiplied by 3 during free spins" | Flat multiplier on every win during the feature. Surfaced on the result via featureMultiplier. |
| Random feature multiplier (run-level) | featureConfig.randomMultiplierOptions | Random Multiplier Free Spins variants | Engine rolls one multiplier from a weighted list at the start of each feature run; applies to the entire run. Surfaced via featureMultiplier. Forces the feature into a grouped DB entry. |
| Per-spin random feature multiplier | featureConfig.perSpinRandomMultiplierOptions | "every freespin a fresh global multiplier" variants | Engine rolls a fresh multiplier from the weighted list on every individual feature spin, so the multiplier varies spin-to-spin within one run. Overrides both multiplier and randomMultiplierOptions. Regular feature spins only — Hold-and-Win and wheel features fall back to the run-level pick. Surfaced via featureMultiplier. Forces the feature into a grouped DB entry. |
Sticky mechanics (feature-scoped)
All fields below live on IFeatureConfig (featureConfig.X).
| Mechanic | Config | Example | Notes |
|---|---|---|---|
| Sticky wilds / sticky symbols | stickySymbols | Dead or Alive (NetEnt) | Listed symbols persist on the board across feature spins once they land. |
| Sticky wins | stickyWins | various | All symbols involved in any winning combination become sticky. |
| Sticky winlines | stickyWinlines | various | Symbols on a winning payline become sticky. |
| Sticky symbol limit | stickySymbolLimit | various | Caps how many symbols can be sticky simultaneously. |
| Sticky overwrite rules | stickySymbolOverwriteRules | upgrading stuck symbols (low → high) | A new sticky can replace an existing stuck symbol per defined replacement rule. |
Multiplier mechanics (per-feature)
All fields below live on IFeatureConfig (featureConfig.X).
| Mechanic | Config | Example | Notes |
|---|---|---|---|
| Multiplier collection | multiplierCollectionConfigs | various "multiplier symbol counter" free-spins features | Counts collected symbols across all feature spins; thresholds map count → active multiplier; targets a specific win type (winline/ways/scatter/cluster/bookwinline/totalwin). |
| Collected spin multiplier | collectedSpinMultiplier | Gates of Olympus (Pragmatic Play) | Spin multiplier values accumulate across feature spins (add = sum, multiply = product); application is always or onHit. |
Collector mechanics
| Mechanic | Config | Example | Notes |
|---|---|---|---|
| Target value pool | collectorTargetValues | Money Train (Relax Gaming) | Weighted random values assigned to each target symbol type when a collector evaluation runs. |
| Collector level progression | collectorLevelConfig | Big Bass Bonanza (Pragmatic Play) | Collector symbols progress through ranked levels. Each level has its own multiplier, can grant additionalSpins, and can transition to a different feature via nextFeature. Shared between base game and features. |
Triggers, awards & round control
| Mechanic | Config | Example | Notes |
|---|---|---|---|
| Feature awards from wins | awardResult.featureAward | universal scatter-bonus triggers | A win awards featureCount spins of a named feature. |
| Spin multiplier awards | awardResult.spinMultiplier | Gates of Olympus (Pragmatic Play) — board multiplier symbols | A win contributes a spin-multiplier value, summed into result.spinMultiplierTotal and applied to the spin's win amount. |
| Pot awards (combination-aware) | potAwards | 3 Pots of Egypt (3 Oaks) — combined trigger tables | Multiple scatter groups evaluated together against one outcome table; allows correlated triggers and shared probability budgets. |
| Progression counters | progressionCountersawardResult.progressionAwards | various "meter/charge/level-up" mechanics | Wins increment named counters; counter at 1.0 awards configured features; can be stakeSpecific for separate counters per stake. |
| Random-choice / player-choice award resolution | progressionCounter.onComplete.type | various player-choice bonus picks | When a counter completes, the awards are either rolled randomly or surfaced as a player choice via the SDK. |
| Max payout cap | maxPayoutconsolidateRounds | Pragmatic Play 5,000× / 25,000× max-win cap | When cumulative win reaches the cap the round ends immediately. |
| Consolidated rounds | consolidateRounds | bundled bonus/H&W games | Bundles all feature spins into the base scenario instead of separate feature DBs. Required for maxPayout. |
| Scenario variety cap | maxScenariosPerEntry | (generator-level tuning) | Caps stored scenarios per unique entry — higher = more variety, larger DB. |
| Zero-win scenario cap | maxScenariosPerZeroWinEntry | (dead-spin tuning, default 2000) | Separate, larger cap for 0-win spins (~60% of volume) so dead-spin variety isn't starved. |
| Loose scenario cap | looseScenarioCap | (default on in the creator) | Pools the scenario cap across outcomes that share win + feature spins + progression but differ only in cosmetic tags — keeps scenarios.jsonl small on the large sims needed to reach jackpot-odds wins, without changing entries or weights. |
Provably Fair Verification
When a slot round runs with config.rng === 'pf', every spin's outcome is fully determined by the RNG draws committed to in the round's pf block. Use pfVerify to have the engine replay the round from its revealed seeds and compare the result to what you recorded live.
Request
The slot replay needs the opening stake and any featureToBuy it used. Each continuation step in the live round (wager-feature decisions, random-choice or player-choice awards) is replayed by passing the matching playerChoiceIndex in actions. Free-spin / feature cascades that resolve internally — no player choice — need no entry in actions; the engine cascades them itself.
import { pfVerify } from '@hizi.io/engine-sdk';
const reply = await pfVerify({
backendURL,
token,
serverSeed: pf.revealedServerSeed,
clientSeed: pf.clientSeed,
stake: 100,
featureToBuy: 'freespin', // only if the round opened on a buy feature
actions: [
{ playerChoiceIndex: 1 }, // first wager-feature / playerChoice resolution
{ playerChoiceIndex: 0 }, // second
],
});Reading the response
rngData mirrors the live round's pf.rngData row-for-row — same pf:start-end ids, same nonces[], same values[]. steps[] is the per-step IGameResult chain that placeBet returned in order; the terminal step is steps[steps.length - 1]. Compare against your recorded round:
if (!reply.success) {
// handle reply.error
return;
}
const { rngData, steps } = reply.result;
// 1. RNG audit
const audited = rngData.length === pf.rngData.length &&
rngData.every((row, i) => deepEqual(row, pf.rngData[i]));
// 2. Scenario / totalWin (terminal step)
const terminal = steps[steps.length - 1];
const resultOk = deepEqual(terminal.scenario, lastLiveResult.scenario) &&
terminal.totalWin === lastLiveResult.totalWin;If both line up the engine couldn't have rigged the outcome — every draw and every cascade trace back to (serverSeed, clientSeed, your actions).
Next Steps
- Response Handling —
IGameResultstructure andengineDatafields. - Plinko Responses — Reading plinko game responses.
- Types & Interfaces — Full SDK type reference.