How to add ads to a three.js game
Monetize your three.js game with one line of code. For full rounds, attach it to your renderer and mark your natural breaks. At each break, a brand pays for a 15-second playable Bonus Round inside your game, and then your players are straight back in. You keep 70% of net ad revenue.
Run npx bonusround init in your project. It finds your three.js setup and renderer, shows you a diff for the install and the attach() line, and leaves a TODO(bonusround) where a break probably goes. You place break() yourself. Using an AI coding agent? Tell it “Integrate Bonus Round” (more).
The code
-
Add one line of code
Paste this line into the
<head>of the page that runs your game. It's async and small, and loads the rest of the SDK only when it's needed.<script async src="https://bonusround.io/v1/br.js" data-pub="pub_XXXXXXXX"></script>Using a bundler?
npm i bonusroundworks too (Install → npm and bundlers). -
Optional: attach it after your renderer, scene and camera exist
(window.bonusround = window.bonusround || []).push((BR) => BR.attach({ THREE, scene, camera, renderer }));This is the safe form of
BonusRound.attach({ THREE, scene, camera, renderer }): it runs as soon as the async script has loaded. The SDK uses theTHREEyou pass, so it never loads a second copy of three.js. -
Optional: show a full round at a natural break
async function onRoundEnd() { pauseGameplay(); // stop timers, enemies, scoring await window.BonusRound?.break('intermission'); // plays a Bonus Round, or resolves at once if there's no ad startNextRound(); }Good breaks: round over, level complete, death and respawn, back to the lobby. The
?.means an ad blocker can never break your game.
That's the whole integration. Your game starts in test mode, so the next break() plays a free test round right away. Test plays are never billed or paid.
React Three Fiber
R3F gives you the scene, camera and renderer from useThree(). Add this inside your <Canvas>, plus the line from step 1 in index.html:
import { useEffect } from 'react';
import { useThree } from '@react-three/fiber';
import * as THREE from 'three';
export function BonusRoundAttach() {
const { scene, camera, gl } = useThree();
useEffect(() => {
(window.bonusround = window.bonusround || []).push((BR) => BR.attach({ THREE, scene, camera, renderer: gl }));
}, [scene, camera, gl]);
return null;
}
Call await window.BonusRound?.break('intermission') from your game state when a round ends. More stacks (Vite, webpack, import maps, Next.js): Install.
What players see
A 5-second heads-up, then a 15-second branded round in your game: players collect the brand's items, see a sponsored leaderboard, and go back to your game. Every round is labelled “Ad”. With a host adapter they keep their own character, controls and physics. You can also turn on opt-in rewarded rounds and an ambient branded prop in your world.
Ways to put ads in a three.js game
| Option | Where it works | What the player sees |
|---|---|---|
| Web game portal SDKs (Poki, CrazyGames, GameDistribution) | Only when your game is published on that portal | The portal's video ads between levels |
| Google's H5 Games Ads | Your own site, with an approved AdSense account | Interstitial and rewarded ads over the game |
| Banner or video ads around the canvas | Your own site | Ads outside the game, or a video over it |
| Bonus Round | Your own site, itch.io and other embeds; it switches itself off on portals that don't allow third-party ads | A 15-second playable branded round inside your three.js scene |
What you earn
You keep 70% of net ad revenue: what brands spend in your game, minus any disclosed agency commission. Each earning clears 30 days after you earn it (net-30), and you can cash out through Stripe once you have $50. See Payouts and Pricing.