About crazy-games-js-sdk
A reference skill for integrating the CrazyGames JavaScript SDK into games. It is for game developers and covers SDK initialization, lifecycle hooks, ads, player access, storage, and optional Xsolla payments.
Key features
- 01SDK loading and initialization guidance
- 02Gameplay and loading lifecycle hooks
- 03Interstitial and rewarded ad flows
- 04Player authorization and profile access
- 05Key-value storage integration
- 06Optional Xsolla payment flow
Use cases
- 01Adding CrazyGames SDK to a game
- 02Mapping gameplay states to lifecycle calls
- 03Showing interstitial or rewarded ads
- 04Saving player data with SDK storage
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ hungify/ playables-skills --skill crazy-games-js-sdkAfter installing, type/crazy-games-js-sdkin Claude Code to confirm it loaded.
Details
From the project READMECrazyGames JavaScript SDK
Integration Checklist
Follow this order when integrating the SDK into a game:
- Load SDK — add the CDN
<script>tag before your game bootstraps - Init — call
SDK.init()and await resolution before touching any SDK module - Lifecycle hooks — wire
loadingStart/StopandgameplayStart/Stopthroughout the game loop - Ads — add interstitial and/or rewarded ad calls at natural break points
- Optional: Auth & Storage — add sign-in and persistent key/value storage if needed
- Optional: Payments — Xsolla Pay Station integration (separate concern; skip if not applicable)
Installation
Load the SDK from the official CDN before bootstrapping the game.
<script src="https://sdk.crazygames.com/crazygames-sdk-v3.js"></script>
If you load it dynamically, wait until window.CrazyGames.SDK.init is defined before calling it. If the script is blocked (e.g. by CSP), the window.CrazyGames global will be undefined — add sdk.crazygames.com to your script-src directive.
Initialization
Call SDK.init() once. It resolves when the SDK is ready. Only after resolution are SDK.user, SDK.data, SDK.ad, SDK.banner, SDK.game, and SDK.analytics safe to use.
async function initCrazyGames() {
if (!window.CrazyGames?.SDK) {
throw new Error('CrazyGames SDK script not loaded');
}
const sdk = window.CrazyGames.SDK;
try {
await sdk.init();
} catch (err) {
throw new Error(`CrazyGames SDK init failed: ${err.message}`);
}
const isUserAccountAvailable = sdk.user.isUserAccountAvailable;
if (isUserAccountAvailable) {
const language = sdk.user.systemInfo.countryCode.toLowerCase();
const deviceType = sdk.user.systemInfo.device.type.toLowerCase(); // 'desktop' | 'mobile' | 'tablet'
console.log('lang/device', language, deviceType);
}
return sdk;
}
Verification
After calling initCrazyGames(), confirm the SDK is working before proceeding:
- Console check — no
CrazyGames SDK init failederror should appear;sdk.user.isUserAccountAvailableshould return a boolean without throwing. - Lifecycle hooks — open the CrazyGames sandbox URL for your game and verify that
loadingStart/loadingStop/gameplayStart/gameplayStopcalls appear in the CrazyGames developer dashboard event log. - Ad callbacks — use the CrazyGames sandbox environment (append
?crazygames_sandbox=1to the URL) to trigger test ads and confirmadStartedandadFinishedfire correctly. - Adblock — call
sdk.ad.hasAdblock()while running with an ad blocker enabled and confirm it resolvestrue.
Player / Authorization
Use showAuthPrompt() to ask the player to sign in, then getUser() to read profile data, and optionally getUserToken() for a signed JWT to verify on your backend.
async function authorizePlayer(sdk, { useUserToken = false } = {}) {
if (!sdk.user.isUserAccountAvailable) {
// Guest session — no auth possible on this domain
return null;
}
await sdk.user.showAuthPrompt();
const user = await sdk.user.getUser();
if (!user) {
return null;
}
const profile = {
name: user.username || null,
photo: user.profilePictureUrl || null,
extra: user
};
if (useUserToken) {
profile.extra.jwt = await sdk.user.getUserToken();
}
return profile;
}
Storage
CrazyGames provides synchronous key/value storage via SDK.data. Values are strings; serialize JSON yourself.
function storageSet(sdk, key, value) {
const data = typeof value === 'string' ? value : JSON.stringify(value);
sdk.data.setItem(key, data);
}
function storageGet(sdk, key, tryParseJson = true) {
const raw = sdk.data.getItem(key);
if (!tryParseJson || raw == null) {
return raw;
}
try {
return JSON.parse(raw);
} catch (_) {
return raw;
}
}
function storageRemove(sdk, key) {
sdk.data.removeItem(key);
}
// Batch helpers — just iterate, the SDK has no native multi-key API
function storageSetMany(sdk, keys, values) {
for (let i = 0; i < keys.length; i++) {
storageSet(sdk, keys[i], values[i]);
}
}
Advertisement
Interstitial ad (midgame)
Call SDK.ad.requestAd('midgame', callbacks). The callbacks fire synchronously as the ad lifecycle progresses.
function showInterstitial(sdk) {
sdk.ad.requestAd('midgame', {
adStarted: () => {
// Mute audio and pause the game here
},
adFinished: () => {
// Resume audio and gameplay here
},
adError: (error) => {
console.warn('interstitial failed', error);
}
});
}
Rewarded ad
Same API, pass 'rewarded' as the type. adFinished means the user watched to completion and should receive the reward.
function showRewarded(sdk, onReward) {
sdk.ad.requestAd('rewarded', {
adStarted: () => {
// Pause the game
},
adFinished: () => {
onReward();
},
adError: (error) => {
console.warn('rewarded failed', error);
}
});
}
Adblock detection
async function isAdblockActive(sdk) {
return sdk.ad.hasAdblock();
}
Responsive banners
Banners are bound to existing DOM containers by id. CrazyGames renders into them and picks an appropriate size.
async function showBanner(sdk, containerId) {
// The container must already be in the DOM and visible
try {
await sdk.banner.requestResponsiveBanner([containerId]);
} catch (e) {
console.warn('banner failed', e);
}
}
async function showAdvancedBanners(sdk, containerIds) {
// Request several banners in parallel
await Promise.all(containerIds.map((id) => sdk.banner.requestResponsiveBanner(id)));
}
function hideBanners(sdk) {
sdk.banner.clearAllBanners();
}
Lifecycle / gameplay events
CrazyGames REQUIRES the host game to call gameplay and loading hooks at the right moments — they drive ad pacing, in-game UI dimming, and analytics.
Map generic game lifecycle events to SDK calls as follows:
| Game event | SDK call |
|---|---|
level_started, level_resumed, gameplay_started | sdk.game.gameplayStart() |
level_paused, level_completed, level_failed, gameplay_stopped | sdk.game.gameplayStop() |
in_game_loading_started | sdk.game.loadingStart() |
in_game_loading_stopped | sdk.game.loadingStop() |
player_got_achievement | sdk.game.happytime() |
There are no separate audio / pause / visibility callbacks on the CrazyGames SDK — drive your audio and pause state from the ad callbacks (adStarted / adFinished) and the standard document.visibilitychange event.
Payments (optional, via Xsolla Pay Station)
Skip this section entirely if you do not have an Xsolla project ID. CrazyGames itself does not sell IAPs; this integration uses CrazyGames-issued Xsolla user tokens with the Xsolla Pay Station widget and Xsolla Store API.
Key constants:
const XSOLLA_PAYSTATION_EMBED_URL =
'https://cdn.xsolla.net/payments-bucket-prod/embed/1.5.0/widget.min.js';
const XSOLLA_SDK_URL = 'https://store.xsolla.com/api/v2/project';
Obtain the Xsolla user token via sdk.user.getXsollaUserToken(). Use it as a Bearer token for all Xsolla Store API requests (catalog, purchase, inventory, consume).
Catalog — GET /{projectId}/items?limit=50
Purchase flow:
POST /{projectId}/payment/item/{sku}→ returns{ token, order_id }- Load
XSOLLA_PAYSTATION_EMBED_URLdynamically if not already present XPayStationWidget.init({ access_token: token, sandbox: isSandbox })then.open()- Listen for
XPayStationWidget.eventTypes.STATUS; whenpaymentInfo.statusmatches/done|charged|success/i, fetchGET /{projectId}/order/{orderId}and callsdk.analytics.trackOrder('xsolla', order)to record the purchase - Listen for the
'close'event to handle cancellation
Inventory — GET /{projectId}/user/inventory/items
Consume — POST /{projectId}/user/inventory/item/consume with { sku, quantity }
All fetch calls must include Authorization: Bearer <xsollaToken>. Throw on non-OK responses. Use isSandbox: true during development.