Appearance
Launch and loading screens
The remote gaming server (RGS) supplies the launch and session data. The frontend owns the loading screens and input controls.
Launch contract
Read login and token from the query string with URLSearchParams. Preserve other launch parameters for the operator adapter. Do not decode a parameter twice.
| Value | Source | Use |
|---|---|---|
| Login URL and launch token | Query parameters login, token | Exchange once. A launch token can be single-use. |
| Session token | Connect reply token | Send with each engine request. Keep it in memory. |
| API address | SDK backendURL; raw reply backendUrl | Send SDK operations to this address. Do not construct endpoint paths. |
| Refresh and logout addresses | SDK refreshURL, logoutURL | Retain the complete URLs, including their query strings. |
| Socket address | SDK webSocketURL | Optional for slots. See transport. |
| Settings | gameSettings | Apply before enabling any control. Reapply after refresh. |
| Game and player | tokenData.gameId, tokenData.playerId, tokenData.operatorId | Use for Operator Interface setup. |
| Language, currency, mode | tokenData.language, tokenData.currency, tokenData.mode | Select translations and display units. The mode is REAL or DEMO. Do not infer real mode from the balance. |
| Currency factor | tokenData.currencyMultiplier | Returned stakes are already in account units. Do not multiply them again. See stakes and money. |
| Open round | tokenData.gameRound | A hash here means an unfinished round. Restore it before play. |
| Operator protocol | operatorProtocol | Use the negotiated protocol. Do not select one from the hostname. |
| Reality-check offset | Raw gameVariables.rcElapsed | Time already elapsed towards the next reminder. See jurisdictions for the unit. |
| Operator data | Top-level passThroughData on the connect reply | Route through the integration for that operator. |
Use the platform launch flow from Studio Quickstart. A page opened without launch parameters needs a launch instruction. It must not place a real bet.
Connect with login()
Use SDK login(). It is the supported connect call. Do not write your own replacement for it.
login() and refresh() return tokenData, and tokenData carries the session identity: playerId, operatorId, gameId, language, currency, currencyMultiplier, mode, and any open gameRound. The typed name of the game mode is mode, not gameMode. ITokenData is an open record ([key: string]: unknown), so the SDK also passes through any extra field your operator puts in tokenData.
Use the SDK error helper to retain operator error data.
ts
// Read the launch parameters the operator puts on your frontend URL, in the shape
// SDK login() takes: { loginURL, launchToken }. login() builds the request itself.
//
// There is deliberately no replacement for login() here. login() is the supported
// connect call. Re-implementing it to recover a dropped field risks exchanging a
// single-use launch token twice, and drifts from the SDK on every release.
export function launchParams(search: string): { loginURL: string; launchToken: string } {
const params = new URLSearchParams(search);
const loginURL = params.get('login');
const launchToken = params.get('token');
if (!loginURL || !launchToken) throw new Error('Launch URL requires login and token');
return { loginURL, launchToken };
}typescript
import { loadConfig, login } from '@hizi.io/engine-sdk';
import { launchParams } from './launch';
import { SdkRequestError } from './sdk-error';
const connected = await login(launchParams(location.search));
if (!connected.success) throw new SdkRequestError(connected.error);
const connection = connected.result;
const session = { backendURL: connection.backendURL, token: connection.token };
const loaded = await loadConfig(session);
if (!loaded.success) throw new SdkRequestError(loaded.error);
// Read the player, game, language and currency from connection.tokenData.
// Restore loaded.result before enabling the first paid spin.A launch token can be single-use. Exchange it once. Do not call login() twice, and do not add a second connect path beside it.
What login() does not return
parseConnectReply() copies eleven top-level keys: token, backendURL, refreshURL, logoutURL, webSocketURL, balance, gameSettings, tokenData, freePlaysAvailable, featuresAvailable, and operatorProtocol. It discards every other field of the raw connect reply.
Most of what it drops you get again on the next call. Only login() and refresh() use this narrowing step. Every other SDK operation returns the raw reply unchanged, so loadConfig gives you gameRoundInfo, amountToCollect and the resumed round state straight after connecting. An error reply keeps passThroughData.
Two fields do not come back:
| Field | Consequence |
|---|---|
gameVariables.rcElapsed | The elapsed time towards the next reality check. Without it a relaunch restarts the reminder interval at zero. |
freePlayInfo | The singular package-progress block (used, granted, won, isLast, serial). The plural freePlaysAvailable survives; this one does not. |
Do not fork login() to recover them. A second exchange of a single-use launch token is a real failure, and a hand-written copy of parseConnectReply() drifts from the SDK on every release. The cost of that workaround is higher than the two fields are worth.
Handle them as integration requirements instead:
- Where the operator contract gives the wrapper the reality check, the wrapper owns the elapsed time. Send
realityCheck(...)through the Operator Interface and do not track the offset yourself. - Where the game owns the reminder, ask hizi.io support to carry
rcElapsedintokenData, whichlogin()does return. Record the gap in your integration notes until it is carried. See jurisdictions. - For freeplay progress, drive the ticket picker from
freePlaysAvailable. The RGS putsfreePlayInfoon every gameround reply once freeplays are active, andplaceBetis not narrowed, so the block reaches you there.IPlaceBetReplydoes not declare it, so read it as an extra field and validate it yourself.
Catch fetch failures and invalid JSON at the application boundary. Keep play disabled. Follow error handling.
Do not log the launch URL, session token, or complete connection object. Restrict allowed launch hosts in your deployment configuration. Remove consumed credentials from visible history only after the wrapper has read its launch parameters.
Loading sequence
- Display a loading screen immediately. Disable all inputs that can place a bet.
- Read saved preferences. Apply the operator's storage restrictions.
- Exchange the launch token. Preserve metadata that your operator needs.
- Register Operator Interface listeners. Await
OperatorInterface.setup. - Call
OperatorInterface.loadProgress(0). - Fetch
loadConfig. Validate its game type, settings, currency, and available stakes. - Load the required fonts, translations, symbol textures, rules, and sounds. Report progress from
0to1. - Call
gameDataLoaded()after configuration and required game data are ready. - Restore any interrupted round. Display its last board or pending player choice.
- Keep
loadMsgvisible for at leastminLoadTimemilliseconds after the message appears. - Show the paytable when
displayPaytableOnEnterGameis true. Require acknowledgement before play. - Call
loadProgress(1)andloadComplete()when loading ends. - Send
initialGameState(muted). Send the current stake and balance. - Call
gameReady()when the player can interact with the restored game.
gameReady() does not mean a new paid spin is available. A restored game can require collection or a bonus choice.
Use gameUIUpdated(buyMenuOpen, splashVisible, waitingForConfirmation) when those states change. Keep a splash acknowledgement separate from a spin request.
loadingScreens and messages
The engine SDK defines no loadingScreens API or response field. Implement the loading screens in your frontend. The standard settings are loadMsg and minLoadTime.
Treat loadMsg as text. Use textContent or your framework's escaped text binding. Do not insert it as HTML. Use an approved translation when the operator supplies a message identifier instead of prose.
Keep essential instructions readable on narrow screens. Do not cover a required message with progress animations. If an asset fails, show a clear error with recovery or exit controls. Do not report successful loading.
The RGS does not supply a complete set of translated jurisdiction messages. Include approved messages in your game assets. See jurisdiction messages.
Audio, background tabs, and mobile
Start the browser audio context from a player gesture. A successful unmute request can still require that gesture. Keep the audio icon consistent with the effective state.
Track three independent conditions: the player's mute preference, an operator mute request, and suspension while hidden or paused. Play audio only when all conditions permit it. An operator resume must not override the player's mute preference.
Handle mute, unmute, and toggle requests through one audio controller. Report actual changes with muted() or unmuted(). Stop sounds, music, and queued win sounds when muted.
On visibilitychange, suspend presentation and new input. A hidden tab does not stop server time or a Crash round. Reconcile state before continuing after disconnection.
Apply orientation and fullscreen restrictions before presenting controls. Support resize, safe areas, keyboard focus, and touch. Never stretch a fixed board into inaccessible buttons. See settings.