Publishing on CrazyGames
Use @irtio/crazygames to connect portal
invites, instant multiplayer, and room availability to irtio room codes. Add the portal game
origin to your project before testing. The examples assume an existing irtio game.
npm install @irtio/crazygames What the portal requires
The portal’s own requirements for multiplayer games are at docs.crazygames.com/requirements/multiplayer, and the SDK reference for every call below is at docs.crazygames.com/sdk/game. The parts that touch irtio:
| The portal asks for | Its SDK surface | What irtio answers with |
|---|---|---|
| Report the room a player is in, and whether others may join | updateRoom({ roomId, isJoinable, inviteParams }) | room.id, and your own seat count |
| An invite link a player can send a friend | inviteLink(params), read back with getInviteParam / inviteParams | the room code, the same one ?room= carries |
| Handle an accepted invite without a page reload | addJoinRoomListener(handler) | leave the room and join the new code |
| Launch straight into multiplayer when the portal says so | isInstantMultiplayer | matchRoom, quick-match |
| Signal loading and gameplay, as every published game must | loadingStart / loadingStop, gameplayStart / gameplayStop | nothing; they are portal telemetry |
The SDK itself is injected by the portal from https://sdk.crazygames.com/crazygames-sdk-v3.js.
The adapter never bundles it and never loads it: it reads window.CrazyGames when you call it, and
throws a CrazyGamesSdkError naming that script tag when it is not there. On your own dev server it
is not there, so guard with crazyGamesAvailable() if one build has to run in both places.
The boot path
crazyGamesEntry() collapses the portal’s intent into the one decision a multiplayer game makes at
boot: join a named room, find any room, or show a menu.
import { joinRoom, matchRoom } from '@irtio/client';
import { crazyGamesEntry, initCrazyGames, publishCrazyGamesInvite } from '@irtio/crazygames';
import { schema } from './schema';
await initCrazyGames(); // the SDK must finish init before any other call
const entry = crazyGamesEntry();
let room =
entry.kind === 'invite'
? await joinRoom(schema, { room: entry.room })
: entry.kind === 'instant'
? await matchRoom(schema, { queue: '1v1' })
: await joinRoom(schema); // your own front door: create by join
publishCrazyGamesInvite(room.id); An invite wins over the instant flag. A player who followed a friend’s link wants that friend’s room, not the next open one.
publishCrazyGamesInvite(room.id) is the portal-side counterpart of room.link. It reports the
room with updateRoom and returns the portal’s invite link. Use it instead of room.link here: a
link into your own origin is not something a player inside the portal’s page can open. Pass { inviteButton: true } if you also want the portal to draw its invite button.
Close the room when the match starts or the last seat goes, so the portal stops sending players into a game they cannot enter, and leave it when the player does:
closeCrazyGamesRoom(room.id); // reported, no longer joinable
leaveCrazyGamesRoom(); // the player is out Invites that arrive mid-game
A player can accept an invite while your game is already running. Handling it in place is one of the portal’s requirements, and it is a room swap:
const off = onCrazyGamesJoin(async (code) => {
await room.leave();
room = await joinRoom(schema, { room: code });
}); The room parameter
The room code travels on the invite link under the parameter room, matching the ?room= query
parameter joinRoom reads and publishRoomToLocation writes, so a code looks the same wherever it
came from. Every entry point takes { param: 'roomId' } if your game already ships another name.
Carry anything else you need beside it with { params: { region: 'eu' } }.
The origins step
Your build runs from games.crazygames.com, inside pages on the portal’s many domains. irtio
matches allowed origins on scheme, host and port, so the one origin to add is the iframe’s:
https://games.crazygames.com Add it on the dashboard under API keys. Without it the portal’s copy of your game cannot join anything, while your own site keeps working, which makes this the failure that looks like a bug in the adapter and is not. The portal’s full domain list, which matters for your bundle’s own sitelock rather than for irtio, is at docs.crazygames.com/resources/html5/sitelock.
If the portal serves your build and other games from games.crazygames.com, those games share your localStorage. With identity: true, read shared origins:
use JWT auth for progress that matters, or pass an Identity with storage: null if a new player
per load is acceptable.
Lifecycle calls
initCrazyGames, crazyGamesLoadingStart, crazyGamesLoadingStop, crazyGamesGameplayStart and crazyGamesGameplayStop are passthroughs, exported so a game that already imports this package
does not have to reach past it into window.CrazyGames. They are required of every published game,
multiplayer or not, and the portal decides what they mean. The rest of the SDK, ads and data and
user and banners included, stays yours to call directly.
Test the portal integration
Test invites, room changes, and instant multiplayer in the portal’s QA tool. Keep seat limits on
the server: isJoinable: false reports availability but does not enforce admission.
Use CrazyGames.SDK.user and CrazyGames.SDK.game.settings for friends, usernames, and disableChat. Test your matchmaking queue under expected traffic so instant multiplayer starts
promptly.
See matchmaking for the queue behind instant multiplayer, and the Poki playbook for its external-resource approval process.