Single random outcome per bet.
You define the possible multipliers. The platform calculates probabilities to guarantee 97% RTP. Good for slots, dice, wheel spins, scratch cards.
How It Works
You provide:
A list of multipliers (e.g., [0, 0.5, 2, 5, 10])
Platform handles:
Probabilities, RNG, payouts — always 97% RTP
Game Config
Declare this in your HTML file. The platform reads it on upload.
1window.GAME_CONFIG = {2 primitive: 'one-shot',3 multipliers: [0, 0.5, 1, 1.5, 2, 3, 5, 10, 25, 50]4};Config Rules
| Rule | Detail |
|---|---|
| At least 2 multipliers | Need at least 2 unique values |
| At least one < 1 | Must have a losing outcome (0 counts) |
| No duplicates | Each value must be unique |
| Max 100 multipliers | Performance limit |
| Max value: 100,000x | Platform payout cap |
| Max 4 decimals | Values rounded to 4 decimal places |
[2, 5, 10] — no losing outcome (need at least one value < 1)[0, 2, 2, 5] — duplicate value (2 appears twice)[0] — only one multiplier (need at least 2)[0, 200000] — exceeds max value cap (100,000×)PostMessage API
Place a Bet
1parent.postMessage({2 type: 'bet',3 id: Date.now(), // Unique request ID4 stake: window.GAME_STAKE, // Crypto amount (read-only, set by platform UI)5 currency: window.GAME_CURRENCY // 'ETH' | 'SOL' | 'USDT' (read-only)6}, '*');Receive Result
1window.addEventListener('message', (e) => {2 if (e.data.type === 'betResult' && e.data.id === myRequestId) {3 const r = e.data.result;4
5 // Errors are inside result for one-shot bets6 if (r.error) {7 showError(r.error);8 return;9 }10
11 // r.multiplier — winning multiplier (e.g., 5)12 // r.result — 'WIN' (multiplier > 1) | 'PUSH' (multiplier = 1) | 'LOSS' (multiplier < 1)13 // r.payout — crypto payout amount14 // r.payoutUsd — USD payout amount15 // r.profit — crypto profit (can be negative)16 // r.profitUsd — USD profit17 // r.balance — new balance after bet18 }19});Possible Errors
When a bet fails, r.error inside the result will contain one of these strings:
| Error | Cause |
|---|---|
| 'Insufficient balance' | Player doesn't have enough funds for the bet |
| 'Rate limited' | Bets sent faster than 1 per 25ms |
| 'Invalid stake' | Stake is zero, negative, or not a number |
| 'Game not found' | GAME_CONFIG is missing or game hasn't been uploaded |
| 'Session expired' | Game session timed out (rare — usually after long inactivity) |
Stake Updates
The platform sends updated stake info when the player changes their bet amount:
1window.addEventListener('message', (e) => {2 if (e.data.type === 'stakeUpdate') {3 stake = e.data.stake;4 stakeUsd = e.data.stakeUsd;5 currency = e.data.currency;6 }7});Variants
If your game has multiple multiplier sets — for example, different risk levels or board sizes — use variants instead of multipliers. Each variant is a named multiplier array. The player's selection determines which set is used for each bet.
1window.GAME_CONFIG = {2 primitive: 'one-shot',3 variants: {4 'low-8': [0.5, 1, 1.1, 2.1, 5.6],5 'medium-8': [0.4, 0.7, 1.3, 3, 13],6 'high-8': [0.2, 0.3, 1.5, 4, 29],7 'low-12': [0.5, 1, 1.1, 1.4, 1.6, 3, 10],8 'medium-12': [0.3, 0.6, 1.1, 2, 4, 11, 33],9 // ... more variants10 }11};Include the variant key in your bet message to select which set to use:
1parent.postMessage({2 type: 'bet',3 id: Date.now(),4 stake: window.GAME_STAKE,5 currency: window.GAME_CURRENCY,6 variant: 'medium-12' // Selects which multiplier set7}, '*');multipliers or variants, not both. If your game has only one multiplier set, use multipliers. If it has multiple, use variants.Continuous Mode
Optional alternative: instead of fixed multipliers, the player picks any target from 1.01x to 100,000x. The result is binary — they either win their target or get 0x.
P(win) = 0.97 ÷ target
Example: 2x target → 48.5% win chance
1window.GAME_CONFIG = {2 primitive: 'one-shot',3 mode: 'continuous',4 maxMultiplier: 100000 // Optional, defaults to 100,0005};The bet postMessage is the same, but include the player's chosen target:
1parent.postMessage({2 type: 'bet',3 id: Date.now(),4 stake: window.GAME_STAKE,5 currency: window.GAME_CURRENCY,6 targetMultiplier: 2.5 // Player's chosen target7}, '*');Balance Reveal Timing
By default, the player's visible balance updates the moment the bet API responds — before any outcome animation plays. To preserve the dopamine hit, send balanceReveal at the moment you show the outcome (e.g. ball lands, wheel stops, card flips).
1// Inside your animation callback:2parent.postMessage({ type: 'balanceReveal' }, '*');balanceReveal per outcome — they are processed in FIFO order. A 30-second fallback fires automatically if you never send it.Complete Template
1<!DOCTYPE html>2<html>3<head><title>My Slots</title></head>4<body>5 <button onclick="spin()">SPIN</button>6 <div id="result"></div>7
8 <script>9 window.GAME_CONFIG = {10 primitive: 'one-shot',11 multipliers: [0, 0.5, 1, 2, 5, 10, 50]12 };13
14 let pendingId = null;15
16 function spin() {17 if (pendingId) return;18 pendingId = Date.now();19 var payload = {20 type: 'bet',21 id: pendingId,22 stake: window.GAME_STAKE || 1,23 currency: window.GAME_CURRENCY || 'USDT'24 };25 // In preview mode, include game config so the platform26 // can validate without an upload27 if (window.TEST_MODE) {28 payload.gameConfig = window.GAME_CONFIG_FOR_API;29 }30 parent.postMessage(payload, '*');31 }32
33 window.addEventListener('message', (e) => {34 if (e.data.type === 'betResult' && e.data.id === pendingId) {35 pendingId = null;36 const r = e.data.result;37 if (r.error) return alert(r.error);38
39 document.getElementById('result').textContent =40 r.result === 'WIN'41 ? `Won ${r.multiplier}x — $${r.payoutUsd.toFixed(2)}`42 : r.result === 'PUSH'43 ? 'Push — money back'44 : 'Lost!';45 }46
47 if (e.data.type === 'stakeUpdate') {48 // Update local stake variables49 }50 });51
52 parent.postMessage({ type: 'gameReady' }, '*');53 </script>54</body>55</html>window.TEST_MODE is true and the platform gives you a $1,000 test balance. Game logic runs normally — just no real money.