Scenario Compression
Scenarios (see Core Concepts) are the per-spin game-state snapshots the engine returns to your game client. For rich games — slots especially — they are large and highly repetitive, so the hizi pipeline stores and transmits them in a minified form and expands them back to the full shape on the client.
This is transparent: with one line of setup, your game client sees exactly the same scenario objects it always has. This page explains what changes on the wire and how to opt in.
Why
- Smaller payloads — less bandwidth per
placeBet, and smaller items at rest. - Harder to read — a minified scenario is not trivially inspectable by a third party watching network traffic.
What "minified" means
Two transforms are applied to each scenario:
- Short keys — verbose keys become short codes (
board→b,winAmount→wa,winMultiplier→wm, …). - Integer symbols — symbol/enum string values become integers (
"wild"→6,"winline"→0, …).
A slot board step looks like this on the wire:
// full (what your client sees after decoding)
{ "board": [["low1", "wild", "scatter"], ["low2", "low1", "wild"]],
"winAmount": 7,
"wins": [{ "mode": "winline", "positions": [{ "column": 0, "row": 1 }], "winMultiplier": 5 }] }
// minified (what travels on the wire)
{ "b": [[0, 6, 7], [1, 0, 6]],
"wa": 7,
"w": [{ "m": 0, "p": [{ "c": 0, "r": 1 }], "wm": 5 }] }The integer ↔ symbol-name mapping is published in the game's config as symbolMap, and a scenarioSchema marker flags the game as using minification. Both arrive on the loadConfig response:
const cfg = await loadConfig({ backendURL, token });
// cfg.result.config.scenarioSchema -> e.g. 1 (present only for minified games)
// cfg.result.config.symbolMap -> { "0": "low1", "1": "low2", "6": "wild", "7": "scatter", ... }How it flows
Creator ── builds game-data.zip with scenarios stored minified
│
Engine ── stores minified, sends minified on the wire (placeBet / loadConfig / pfVerify)
│ (expands internally where it needs to: game-history rendering, RTP simulation,
│ and games whose logic reads the scenario, e.g. mines/keno)
▼
SDK ── decodes back to full string-symbol scenarios for your clientDecoding in the SDK
The easiest path: pass the config you got from loadConfig into placeBet (and pfVerify). The SDK decodes the returned scenario for you before resolving.
const cfg = await loadConfig({ backendURL, token });
const config = cfg.success ? cfg.result.config : undefined;
// placeBet decodes the scenario when you pass `config`.
const bet = await placeBet({ backendURL, token, stake, config });
if (bet.success) {
const scenario = bet.result.result.scenario; // full, string-symbol form
}loadConfig automatically decodes any scenarios embedded in its own reply (a resumed round or history in previousResults), so those are already expanded.
To decode a result you are handling yourself (for example one you stored), call decodeScenario directly:
import { decodeScenario } from '@hizi.io/engine-sdk';
decodeScenario(gameResult, config); // mutates gameResult.scenario in placeSafe by default / backward compatible
- Passing
configis always safe:decodeScenariois a no-op for games that don't minify (noscenarioSchema) and whenconfigis missing. You never need to branch on game type. - Games that don't minify, and rounds recorded before minification was enabled, are returned unchanged.
- If you do not pass
config,placeBetreturns the raw (minified) reply untouched — useful if you want to handle decoding yourself.
Notes
- The scenario codec is defined once, canonically, in
@hizi.io/engine-sdk; the engine (wire minify) and the builder (storage compression) import it, so the three sides can never disagree on the format. TheSCHEME_VERSIONconstant guards the scheme. - Server-rendered game history expands scenarios automatically — nothing to do there.