Rising multiplier that can crash at any moment — cash out before it does.
A multiplier climbs from 1.00×. A crash point is secretly determined before the round starts. Cash out before the crash to win; wait too long and lose everything. Good for crash, rocket, moon games.
How It Works
You define:
Growth speed, curve shape, min/max crash points
Platform handles:
Crash point generation, multiplier math, 97% RTP
Game Config
1window.GAME_CONFIG = {2 primitive: 'multiplier',3 curve: 'exponential', // 'exponential' | 'linear'4 growthRate: 0.1386, // Controls speed (0.01–10)5 // For exponential: ln(2)/growthRate = seconds to reach 2x6 // Default 0.1386 → 2x at ~5 seconds7 maxMultiplier: 1000000 // Cap (default 1,000,000)8};Config Parameters
| Parameter | Type | Description |
|---|---|---|
| curve | 'exponential' | 'linear' | Curve shape. exponential = classic crash, linear = steady climb |
| growthRate | number | Growth speed (0.01–10). Higher = faster climb. Required. |
| maxMultiplier | number | Optional. Cap on multiplier (min 2, max 10,000,000) |
Curve Types
exponential
Classic crash curve. Multiplier accelerates over time. Most rounds crash low, rare rounds reach high multipliers. Standard crash game behavior.
linear
Steady climb at a constant rate. More predictable timing. Good for simpler game styles.
PostMessage API
Multiplier games use two messages: start (place bet) and cashout.
1. Start a Round
1parent.postMessage({2 type: 'multiplierStart',3 id: Date.now(), // Unique request ID4 stake: window.GAME_STAKE,5 currency: window.GAME_CURRENCY6}, '*');7
8// Response (matched by id):9// {10// sessionId: "abc123",11// commitHash: "fa3b...", — provably fair commitment12// serverSeedHash: "ef01...", — hash of server seed13// curve: "exponential",14// growthRate: 0.1386,15// maxMultiplier: 1000000,16// startTime: 1700000000000, — server timestamp (ms)17// stake: 0.01,18// stakeUsd: 1019// }2. Cash Out
1// Capture the server-corrected time at the moment the user clicks,2// then freeze your animation immediately for a snappy feel.3const clickTimeMs = Date.now() + clockOffset;4
5parent.postMessage({6 type: 'multiplierCashout',7 id: Date.now(),8 sessionId: state.sessionId,9 cashoutAtMs: clickTimeMs // optional — locks multiplier at click time10}, '*');11
12// Response if BEFORE crash (matched by id):13// {14// success: true,15// multiplier: 2.34,16// payout: 0.0234,17// payoutUsd: 23.40,18// profit: 0.0134,19// profitUsd: 13.40,20// stakeUsd: 10,21// crashPoint: 5.67, — the actual crash point (revealed)22// serverSeed: "abc...", — for verification23// commitHash: "fa3b...",24// gameOver: true25// }26
27// Response if AFTER crash:28// {29// error: 'Round crashed - too late!',30// crashed: true,31// crashPoint: 1.45,32// payout: 0,33// payoutUsd: 0,34// profitUsd: -10,35// stakeUsd: 10,36// serverSeed: "abc...",37// commitHash: "fa3b...",38// gameOver: true39// }3. Server Events
The platform sends two server-pushed events to your iframe. These are NOT responses to your messages — they arrive automatically:
1// Clock sync — sent periodically to sync animation timing2// {3// type: 'serverSync',4// serverNowMs: 1700000005000 — server's current timestamp5// }6
7// Crash — sent when the round crashes8// {9// type: 'serverCrash',10// crashMultiplier: 1.45, — the crash point11// serverSeed: "abc...", — for verification12// commitHash: "fa3b..."13// }serverSync to calibrate your local clock offset, ensuring your multiplier animation matches the server's timeline precisely.Active Sessions
Multiplier games are stateful. If a player refreshes mid-round, the platform restores their session:
1window.addEventListener('message', (e) => {2 if (e.data.type === 'sessionResume' && e.data.primitive === 'multiplier') {3 const s = e.data.session;4 // s.sessionId, s.stake, s.stakeUsd5 // s.curve, s.growthRate, s.maxMultiplier6 // s.startTime, s.commitHash7 // Resume the multiplier animation from where they left off8 }9});Provably Fair
The crash point is determined before the round starts:
| When | What Happens |
|---|---|
| Round start | Server generates serverSeed + crashPoint, sends commitHash |
| Round end | Server reveals serverSeed and crashPoint |
| Verification | SHA256(crashPoint:serverSeed) === commitHash |
Animating the Multiplier
Your game HTML is responsible for animating the multiplier visually. The platform tells you the growthRate and curve — you render the animation:
1// Example: exponential curve animation2const startTime = Date.now();3const growthRate = 0.1386; // from start response4
5function animate() {6 const elapsed = (Date.now() - startTime) / 1000;7 // Exponential: multiplier = e^(growthRate * t)8 const multiplier = Math.exp(growthRate * elapsed);9 // Linear alternative: multiplier = 1 + growthRate * elapsed;10 updateDisplay(multiplier.toFixed(2) + 'x');11 if (!gameOver) requestAnimationFrame(animate);12}13animate();serverCrash event to know when the round actually ends. Don't rely on your local multiplier value for game logic.Handling Lag & Desync
Your client clock and the server clock will always drift slightly. Use serverSyncmessages to calculate an offset, then apply it to your animation so the displayed multiplier stays accurate:
1let clockOffset = 0; // ms difference: server - client2
3window.addEventListener('message', (e) => {4 if (e.data.type === 'serverSync') {5 // serverNowMs = what the server thinks "now" is6 clockOffset = e.data.serverNowMs - Date.now();7 }8});9
10// In your animation loop, use the corrected time:11function animate(startTime, growthRate) {12 const serverNow = Date.now() + clockOffset;13 const elapsed = (serverNow - startTime) / 1000;14 const multiplier = Math.exp(growthRate * elapsed);15 updateDisplay(multiplier.toFixed(2) + 'x');16 if (!gameOver) requestAnimationFrame(() =>17 animate(startTime, growthRate));18}Common Pitfalls
Latency Best Practices
Network round-trips add 50-200ms between a player clicking "Cash Out" and the server processing it. During that delay the multiplier keeps climbing, which feels unfair. Follow these three patterns to make cashouts feel instant:
Balance Reveal Timing
The player's visible balance updates as soon as multiplierCashout completes on the server. To show a cashout celebration animation before the balance updates, send balanceReveal when the animation finishes.
1// In your cashout response handler, after playing the celebration:2parent.postMessage({ type: 'balanceReveal', id: betId }, '*');3// Pass the original request id so the platform can match it to the correct payout.4// If omitted, the platform falls back to the oldest pending payout (FIFO).balanceReveal. If the player gets crashed out (no cashout), no update is queued since there is no payout.Complete Template
1<!DOCTYPE html>2<html>3<head><title>My Crash Game</title></head>4<body>5 <button onclick="startRound()">START</button>6 <div id="multiplier">1.00x</div>7 <button onclick="cashout()">CASH OUT</button>8 <div id="status"></div>9
10 <script>11 window.GAME_CONFIG = {12 primitive: 'multiplier',13 curve: 'exponential',14 growthRate: 0.138615 };16
17 let sessionId = null;18 let gameOver = false;19 let clockOffset = 0;20 const pendingRequests = new Map();21 let requestId = 0;22
23 // Request/response helper (id-based matching)24 function sendToParent(data) {25 return new Promise((resolve, reject) => {26 const id = ++requestId;27 pendingRequests.set(id, { resolve, reject });28 var payload = Object.assign({}, data, { id: id });29 // In preview mode, include game config for validation30 if (window.TEST_MODE) {31 payload.gameConfig = window.GAME_CONFIG_FOR_API;32 }33 parent.postMessage(payload, '*');34 setTimeout(() => {35 if (pendingRequests.has(id)) {36 pendingRequests.delete(id);37 reject(new Error('Timeout'));38 }39 }, 30000);40 });41 }42
43 async function startRound() {44 gameOver = false;45 try {46 const d = await sendToParent({47 type: 'multiplierStart',48 stake: window.GAME_STAKE || 1,49 currency: window.GAME_CURRENCY || 'USDT'50 });51 if (d.error) return alert(d.error);52 sessionId = d.sessionId;53 startAnimation(d.growthRate, d.startTime);54 } catch (err) { alert(err.message); }55 }56
57 async function cashout() {58 if (!sessionId || gameOver) return;59 gameOver = true; // freeze animation immediately60 const clickTime = Date.now() + clockOffset;61 document.getElementById('multiplier').textContent += ' CASHING OUT...';62 try {63 const d = await sendToParent({64 type: 'multiplierCashout',65 sessionId: sessionId,66 cashoutAtMs: clickTime67 });68 showWin(d.multiplier, d.payout);69 sessionId = null;70 } catch (err) {71 // "Round crashed - too late!" or other errors72 gameOver = true;73 sessionId = null;74 alert(err.message);75 }76 }77
78 function startAnimation(growthRate, startTime) {79 function tick() {80 if (gameOver) return;81 const t = (Date.now() - startTime) / 1000;82 const m = Math.exp(growthRate * t);83 document.getElementById('multiplier').textContent =84 m.toFixed(2) + 'x';85 requestAnimationFrame(tick);86 }87 tick();88 }89
90 window.addEventListener('message', (e) => {91 const d = e.data;92
93 // Resolve pending requests by id94 if (d.id && pendingRequests.has(d.id)) {95 const h = pendingRequests.get(d.id);96 pendingRequests.delete(d.id);97 if (d.error) h.reject(new Error(d.error));98 else h.resolve(d);99 return;100 }101
102 // Server says round crashed103 if (d.type === 'serverCrash') {104 gameOver = true;105 document.getElementById('multiplier').textContent =106 d.crashMultiplier.toFixed(2) + 'x CRASHED';107 sessionId = null;108 }109
110 // Clock sync (calibrate animation + cashoutAtMs accuracy)111 if (d.type === 'serverSync') {112 clockOffset = d.serverNowMs - Date.now();113 }114
115 // Restored session after refresh116 if (d.type === 'sessionResume' && d.primitive === 'multiplier') {117 sessionId = d.session.sessionId;118 startAnimation(d.session.growthRate, d.session.startTime);119 }120
121 if (d.type === 'stakeUpdate') {122 // Update local stake variables123 }124 });125
126 parent.postMessage({ type: 'gameReady' }, '*');127 </script>128</body>129</html>