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 forIts SDK surfaceWhat irtio answers with
Report the room a player is in, and whether others may joinupdateRoom({ roomId, isJoinable, inviteParams })room.id, and your own seat count
An invite link a player can send a friendinviteLink(params), read back with getInviteParam / inviteParamsthe room code, the same one ?room= carries
Handle an accepted invite without a page reloadaddJoinRoomListener(handler)leave the room and join the new code
Launch straight into multiplayer when the portal says soisInstantMultiplayermatchRoom, quick-match
Signal loading and gameplay, as every published game mustloadingStart / loadingStop, gameplayStart / gameplayStopnothing; 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.