Integrate

Formats & triggers

There are two formats. The takeover is the Bonus Round itself: 15 seconds of branded play. Ambient is a branded prop that lives in your world. A takeover starts from one of three triggers, and each trigger trades reach for engagement differently.

Format · triggerWho starts itYou addPays
takeover · intermissionYou, at a natural breakawait BonusRound.break('intermission')The best-quality slot
takeover · intervalThe SDK, every N minutes at a calm moment, as an offerNothing (optionally safe())Most volume, cheapest per view
takeover · rewardedThe player, for a reward you defineBonusRound.rewarded({ onReward })Highest engagement and eCPM
ambientAlways on once attachedNothing (optionally placeAmbient())Viewable impressions

Turn any of these on or off per game in the dashboard, or with PATCH /api/games/:id under settings.formats. All are on by default.

Anatomy of a Bonus Round

  1. Brand card: a short transition presents the sponsor.
  2. Intro (2 s): the round's instruction appears (“Grab 14 cans!”) in a branded pocket arena with the brand's hero (mascot or giant product) and banners.
  3. Play (15 s): players run around with their own character and collect, pass through or smash branded items. Touching an item scores it.
  4. Sponsored leaderboard (about 3.5 s): scores, the brand's message and its call to action.
  5. Back to your game, exactly where the player was.

Intermission: break()

Call break() wherever your game already pauses: a round ends, a level is complete, the player dies and respawns, the match returns to the lobby. It returns a promise that resolves when the round is over, so your code reads top to bottom.

async function onLevelComplete() {
  game.pause();                                              // timers, enemies, input you don't want during the round
  const r = await window.BonusRound?.break('intermission');  // { filled, completed, reason?, score? }
  if (r?.filled) analytics.track('bonus_round', { completed: r.completed });
  game.loadNextLevel();
}

Interval

With the interval trigger on, the SDK offers a Bonus Round at most every intervalSec seconds (default 300, minimum 60), counting from the last round or offer. It waits for a calm moment, then shows a small offer card in the corner with Play and Not now. Nothing takes over unless the player chooses to play.

// Tell the SDK when an offer would be welcome or not:
BonusRound.safe(false);   // boss fight: no offers
BonusRound.safe(true);    // in a menu or a calm stretch: offers are welcome
BonusRound.safe(null);    // back to automatic: "calm" = 1.5 s without input
curl -X PATCH https://bonusround.io/api/games/gm_… \
  -H "Authorization: Bearer $BONUSROUND_API_KEY" -H "content-type: application/json" \
  -d '{ "settings": { "formats": { "takeover": { "intervalSec": 600 } } } }'

Rewarded: rewarded({ onReward })

The player opts in for something you give them: coins, a revive, a skin. Because they chose to play, rewarded rounds have the highest engagement and pay the most.

// Show the SDK's own entry button ("Bonus Round · 15 s") in the corner, once attach() has finished:
(window.bonusround = window.bonusround || []).push(async (BR) => {
  await BR.attach({ THREE, scene, camera, renderer });
  BR.rewarded({ label: 'Play for 50 coins', onReward: () => giveCoins(50) });
});

// Or start it from your own UI, e.g. on a "Watch for a revive" button:
reviveButton.onclick = () => window.BonusRound?.rewarded({ button: false, onReward: () => revivePlayer() });

Ambient props

Once attached, the SDK can place one branded prop in your world: a portal arch, a statue of the brand's hero, or a billboard. It's paired with the brand's Bonus Round, built at the scale our play agent measured in your game, and placed at an open spot it found while playing. It's billed by viewable impressions under the IAB/MRC in-game rule (half its pixels in view, at least 1.5% of the screen, at most 55° off-axis, for a continuous second), so it only counts while players can really see it.

Placement hints

You know your level better than our agent does. Suggest a spot with placeAmbient(): open ground, visible from where players spend time, not blocking a path.

BonusRound.placeAmbient({ position: [12, 0, -6], rotationY: Math.PI / 2 });   // meters, in world space, feet on the ground
BonusRound.placeAmbient(null);                                                // forget the hint, use the agent's spot

Without a hint the SDK uses the spot from the ad package, then the spot our agent picked; if there's neither, it waits for your hint. Calling placeAmbient() again moves the prop. The prop is added to your worldRoot if you passed one (so it hides during rounds and moves with the level), otherwise to the scene. Turn ambient props off per game with settings.formats.ambient.enabled = false.

Portals

A portal arch is an ambient prop players can walk into. Walking into it counts as an engagement. If you have registered a reward with rewarded({ onReward }), it also starts a rewarded Bonus Round, and finishing that round runs your onReward. Portals need native mode, because the SDK has to know where the player is (host.getPlayerPosition).