The Ad Placement API is designed to support AdSense and AdMob developers using interstitial and rewarded ads in HTML5 games on the web or within apps. This example demonstrates how to integrate the Ad Placement API into a game and use it to place an interstitial ad.
Prerequisites
Before you begin, you will need the following:
- Create two empty files in the same directory:
- index.html
- game.js
- Install Python locally, or use a web server to test your implementation
App sample code
AdMob publishers can download sample app code to better understand how the API can be integrated into an app game.
1. Start a development server
Because the Ads Placement API loads dependencies via the same protocol as the page which it is loaded on, you need to use a web server to test your app. You can use Python's built-in server to create a local development environment.
Open the terminal.
Go to the directory that contains your index.html file, then run:
python -m http.server 8000
In a web browser, go to
localhost:8000
You can also use any other web server, such as the Apache HTTP Server.
2. Create an HTML5 game
Modify index.html
to create an HTML5 canvas element and a button to trigger
gameplay. Then add the necessary script tag to load the game.js
file.
index.html
<!doctype html>
<html lang="en">
<head>
<title>Ad Placement API HTML5 demo</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<canvas id="gameContainer" height="300px" width="300px"></canvas>
<br>
<button id="playButton">Play</button>
<button style="display:none" id="headsButton">Heads</button>
<button style="display:none" id="tailsButton">Tails</button>
<script src="game.js"></script>
</body>
</html>
Modify game.js to play a coin flip game when the "Play" button is clicked.
game.js
// Create a coin flip game
class Game {
constructor() {
// Define variables
this.score = 0;
this.choice = '';
this.canvas = document.getElementById('gameContainer').getContext('2d');
this.canvas.font = '24px Arial';
this.playButton = document.getElementById('playButton');
this.headsButton = document.getElementById('headsButton');
this.tailsButton = document.getElementById('tailsButton');
// On click listeners for the game's buttons.
this.playButton.addEventListener('click', () => {
this.erase();
this.play();
});
this.headsButton.addEventListener('click', () => {
this.choice = 'Heads';
this.flipCoin();
});
this.tailsButton.addEventListener('click', () => {
this.choice = 'Tails';
this.flipCoin();
});
this.erase();
}
// Start the game
play() {
this.score = 0;
this.canvas.fillText('Score: ' + this.score, 8, 26);
this.canvas.fillText('Heads or Tails?', 66, 150);
this.playButton.style.display = 'none';
this.headsButton.style.display = 'inline-block';
this.tailsButton.style.display = 'inline-block';
}
// Flip the coin
flipCoin() {
this.headsButton.disabled = true;
this.tailsButton.disabled = true;
this.erase();
this.canvas.fillText('Score: ' + this.score, 8, 26);
this.canvas.fillText('Flipping coin . . .', 60, 150);
setTimeout(() => { this.coinLanded() }, 2000);
}
// Logic for when the coin lands
coinLanded() {
this.headsButton.disabled = false;
this.tailsButton.disabled = false;
let sideUp;
if(Math.random() < 0.5) {
sideUp = 'Heads';
} else {
sideUp = 'Tails';
}
if (sideUp === this.choice ) {
this.win(sideUp);
} else {
this.lose(sideUp);
}
}
// Guess the flip correctly
win(sideUp) {
this.erase();
this.score += 1;
this.canvas.fillText('Score: ' + this.score, 8, 26);
this.canvas.fillText('It was ' + sideUp + '!', 66, 150);
this.canvas.fillText('Guess again', 70, 200);
}
// Guess the flip incorrectly
lose(sideUp) {
this.erase();
this.canvas.fillText('Sorry, it was ' + sideUp, 50, 100);
this.canvas.fillText('Your score was ' + this.score, 50, 150);
this.canvas.fillText('Want to play again?', 45, 200);
this.playButton.style.display = 'inline-block';
this.headsButton.style.display = 'none';
this.tailsButton.style.display = 'none';
}
// Erase the canvas
erase() {
this.canvas.fillStyle = '#ADD8E6';
this.canvas.fillRect(0, 0, 300, 300);
this.canvas.fillStyle = '#000000';
}
}
const game = new Game();
After completing this step, when you open index.html
in your browser (via your
development server) you should be able to see the game canvas and "Play" button.
If you click Play, the coin flip game should start.
3. Import the Ad Placement API
Next, add the Ad Placement API to your game by inserting a script tag in
index.html
, before the tag for game.js
.
The script tag can take a number of parameters. We will use the following parameters to specify the AdSense property code and to enable testing mode:
data-ad-client=<AdSense property code>
Your AdSense property code. This is always required even for games that will run within apps.data-adbreak-test="on"
Enables testing mode. Remove this for games once they are being served to players.
Setup the AdSense Code and turn on testing mode
The Ad Placement API functionality is included in the AdSense code. To turn it
on, you must first add the AdSense code and include a small script snippet that
initializes its two key functions: adBreak()
and adConfig()
.
index.html (web)
[...]
<canvas id="gameContainer" height="300px" width="300px"></canvas>
<br>
<button id="playButton">Play</button>
<button style="display:none" id="headsButton">Heads</button>
<button style="display:none" id="tailsButton">Tails</button>
<script async
data-adbreak-test="on"
src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-123456789"
crossorigin="anonymous">
</script>
<script>
window.adsbygoogle = window.adsbygoogle || [];
const adBreak = adConfig = function(o) {adsbygoogle.push(o);}
</script>
<script src="game.js"></script>
</body>
</html>
Embedding your game (optional)
If you want to embed a game in other pages inside an iFrame, and the
adsbygoogle
tag is in the game's HTML page, make sure to add
allow='autoplay'
to the iframe element. This is a best practice, and is
necessary for certain ads to be eligible for your game.
<head>
<!-- The adsbygoogle tag is not here -->
</head>
<body>
<iframe src="https://www.my-game.com" title="My game" allow="autoplay">
<!-- The game is loaded here and contains the adsbygoogle tag -->
</iframe>
</body>
Support mobile apps
An H5 game can run in a regular web browser, a WebView
, or a Chrome
Custom Tab within an app. The Ad Placement API can detect which environment your
game is running in and direct the ad requests appropriately. If your game is
running within a regular web browser the ad requests are treated like normal
AdSense requests. If the Ad Placement API detects an in-app environment then it
communicates with the Google Mobile Ads SDK, if it's present, to request and
show AdMob ads.
This capability is supported on Android apps that have been linked with the
Google Mobile Ads SDK. In order to enable it you need to
register the WebView
that will show your game with the Google Mobile Ads SDK and then configure AdMob
ad units and pass them as additional parameters to the AdSense tag. When your
game is run within a suitable app, the Ad Placement API will use these ad units
to show ads.
To enable mobile support, you must specify the following additional tag parameters:
data-admob-interstitial-slot=<AdMob slot ID>
An AdMob interstitial ad unit ID that you’ve configured previously.data-admob-rewarded-slot=<AdMob slot ID>
An AdMob rewarded ad unit ID.
Your AdSense property code should always be passed with the
data-ad-client
parameter and at least one of data-admob-interstitial-slot
or
data-admob-rewarded-slot
must be specified. Both parameters should be
specified if your game uses both formats.
Optionally, you can also specify data-admob-ads-only=on
tag parameter to
indicate that your game should show ads only from AdMob and not fallback to
AdSense in the cases where the game is being played in an environment which
doesn't support AdMob requests (e.g. non-app environments or apps without
the Google Mobile Ads SDK configured).
Important: When you design your game to be embedded within an app and you own the app, or are entering into a revenue share agreement with the owner of the app, then the only way to do this in a high performing and policy compliant way is to use this AdMob support.
First, register the WebView
that will show your game with the Google Mobile
Ads SDK:
MainActivity.java (app)
Default WebView
settings are not optimized for ads. Use the
WebSettings
APIs to configure your WebView
for:
- JavaScript
- Access to local storage
Automatic video play
Java
import android.webkit.CookieManager;
import android.webkit.WebView;
public class MainActivity extends AppCompatActivity {
private WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webView = findViewById(R.id.webview);
// Let the web view accept third-party cookies.
CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);
// Let the web view use JavaScript.
webView.getSettings().setJavaScriptEnabled(true);
// Let the web view access local storage.
webView.getSettings().setDomStorageEnabled(true);
// Let HTML videos play automatically.
webView.getSettings().setMediaPlaybackRequiresUserGesture(false);
// Set the H5AdsWebViewClient.
h5AdsWebViewClient = new H5AdsWebViewClient(this, webView);
webView.setWebViewClient(h5AdsWebViewClient);
h5AdsWebViewClient.setDelegateWebViewClient(pubWebViewClient);
// Register the web view.
MobileAds.registerWebView(webView);
}
}
Kotlin
import android.webkit.CookieManager
import android.webkit.WebView
class MainActivity : AppCompatActivity() {
lateinit var webView: WebView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
webView = findViewById(R.id.webview)
// Let the web view accept third-party cookies.
CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true)
// Let the web view use JavaScript.
webView.settings.javaScriptEnabled = true
// Let the web view access local storage.
webView.settings.domStorageEnabled = true
// Let HTML videos play automatically.
webView.settings.mediaPlaybackRequiresUserGesture = false
// Set the H5AdsWebViewClient.
val h5AdsWebViewClient = H5AdsWebViewClient(this, webView)
webView.webViewClient = h5AdsWebViewClient
h5AdsWebViewClient.delegateWebViewClient = pubWebViewClient
// Register the web view.
MobileAds.registerWebView(webView)
}
}
Next, pass AdMob ad units (one for interstitials and one for rewarded ads) as follows:
index.html (app)
[...]
<canvas id="gameContainer" height="300px" width="300px"></canvas>
<br>
<button id="playButton">Play</button>
<button style="display:none" id="headsButton">Heads</button>
<button style="display:none" id="tailsButton">Tails</button>
<script async
data-admob-interstitial-slot="ca-app-pub-0987654321/1234567890"
data-admob-rewarded-slot="ca-app-pub-0987654321/0987654321"
src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-123456789"
crossorigin="anonymous">
</script>
<script>
window.adsbygoogle = window.adsbygoogle || [];
const adBreak = adConfig = function(o) {adsbygoogle.push(o);}
</script>
<script src="game.js"></script>
</body>
</html>
4. Make a call to adConfig()
The adConfig()
call communicates the game's current configuration to the Ad
Placement API. The API then can use this information to filter the types of ads
it requests so they are suitable for the game (such as video ads that require
sound, if sound is enabled).
A call should be made to adConfig()
any time this configuration changes, like
when a user mutes or unmutes the game. Make a call to adConfig()
in the game's
constructor, then add a button to mute and unmute the game that makes an
additional adConfig()
call.
game.js
class Game {
constructor() {
// Define variables
this.score = 0;
this.choice = '';
this.muted = false;
this.canvas = document.getElementById('gameContainer').getContext('2d');
this.canvas.font = '24px Arial';
this.playButton = document.getElementById('playButton');
this.headsButton = document.getElementById('headsButton');
this.tailsButton = document.getElementById('tailsButton');
this.muteButton = document.getElementById('muteButton');
adConfig({
sound: 'on',
});
// On click listeners for the game's buttons.
this.playButton.addEventListener('click', () => {
this.erase();
this.play();
});
this.headsButton.addEventListener('click', () => {
this.choice = 'Heads';
this.flipCoin();
});
this.tailsButton.addEventListener('click', () => {
this.choice = 'Tails';
this.flipCoin();
});
this.muteButton.addEventListener('click', () => {
var soundString = this.muted ? 'on' : 'off';
this.muteButton.innerHTML = this.muted ? 'Mute sound' : 'Un-mute sound';
this.muted = !this.muted;
adConfig({
sound: soundString,
});
});
this.erase();
[...]
Now, add the mute button to your HTML file.
index.html
[...]
<canvas id="gameContainer" height="300px" width="300px"></canvas>
<br>
<button id="playButton">Play</button>
<button style="display:none" id="headsButton">Heads</button>
<button style="display:none" id="tailsButton">Tails</button>
<button id="muteButton">Mute sound</button>
<script async
data-adbreak-test="on"
src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-123456789"
crossorigin="anonymous">
</script>
[...]
5. Make a call to adBreak()
when the game ends
The adBreak()
call defines an ad placement and takes the object called
placement config that specifies everything required to show an ad at this point
in your game. Supporting different types of ads will require you to initialize
different subsets of the placement config.
The adBreak()
call defines a placement where an ad could show, or in other
words, an opportunity to show an ad. Whether an ad actually shows will depend on
a number of things:
- The type of ad placement that you declared.
- If there have been suitable user interactions prior to this ad placement.
- Whether a suitable ad exists for the current player, that:
- Is relevant to them.
- Is consistent with their data privacy and consent settings.
- The number of ads the user has seen recently.
- The control settings you have configured for this game as either:
- Hints in the tag.
- On AdSense (Note: the controls available in AdSense will evolve over time)
Add code for an interstitial ad to be shown when the game restarts: make a call
to adBreak()
inside the play()
function, which runs only after the game has
been played through once.
adBreak()
must be called as part of a user action, such as clicking the "Play"
button, otherwise the API won't be able to request and display ads.
Create functions to be called before and after the ad break, which you will then
use in the adBreak()
placement config. It is important to note that the
beforeAd
and afterAd
functions will only be called if a suitable ad is
found.
game.js
class Game {
constructor() {
// Define variables
this.score = 0;
this.choice = '';
this.muted = false;
this.shouldShowAdOnPlay = false;
[...]
// Start the game
play() {
if (this.shouldShowAdOnPlay) {
this.shouldShowAdOnPlay = false;
adBreak({
type: 'next', // ad shows at start of next level
name: 'restart-game',
beforeAd: () => { this.disableButtons(); }, // You may also want to mute the game's sound.
afterAd: () => { this.enableButtons(); }, // resume the game flow.
});
}
this.score = 0;
this.canvas.fillText('Score: ' + this.score, 8, 26);
this.canvas.fillText('Heads or Tails?', 66, 150);
this.playButton.style.display = 'none'
this.headsButton.style.display = 'inline-block'
this.tailsButton.style.display = 'inline-block'
}
[...]
// Guess the flip incorrectly
lose(sideUp) {
this.erase()
this.canvas.fillText('Sorry, it was ' + sideUp, 50, 100);
this.canvas.fillText('Your score was ' + this.score, 50, 150);
this.canvas.fillText('Want to play again?', 45, 200);
this.playButton.style.display = 'inline-block'
this.headsButton.style.display = 'none'
this.tailsButton.style.display = 'none'
this.shouldShowAdOnPlay = true;
}
[...]
// Erase the canvas
erase() {
this.canvas.fillStyle = '#ADD8E6';
this.canvas.fillRect(0, 0, 300, 300);
this.canvas.fillStyle = '#000000';
}
enableButtons() {
this.playButton.disabled = false;
this.headsButton.disabled = false;
this.tailsButton.disabled = false;
}
disableButtons() {
this.playButton.disabled = true;
this.headsButton.disabled = true;
this.tailsButton.disabled = true;
}
}
const game = new Game();
6. Make a call to adBreak()
for a rewarded ad
Add code for a rewarded ad to be shown when the game ends but the user would
like to revive their existing score instead of starting afresh. Make a call to
adBreak()
inside the lose()
function, checking if a rewarded ad is
available. If it is, show the user a prompt asking if they want the reward (i.e.
revival in this case) and when they agree to watch the ad, call the
corresponding showAdFn()
. You can configure what to do if the user watches /
skips the rewarded ads using the adViewed
and adDismissed
callbacks.
A fresh adBreak()
should be called for every opportunity to show a rewarded
ad. This ensures the ad is refreshed if the previous ad expired or wasn't
available.
showAdFn()
must be called as part of a direct user action to watch an ad,
otherwise the ad may not display.
Create functions to be called before and after the ad break, which you will then
use in the adBreak()
placement config. It is important to note that the
beforeReward
, adViewed
, adDismissed
, beforeAd
and afterAd
functions
will only be called if a suitable ad is found.
game.js
class Game {
constructor() {
// Define variables
this.score = 0;
this.choice = '';
this.muted = false;
this.shouldShowAdOnPlay = false;
this.showRewardedAdFn = null;
this.canvas = document.getElementById('gameContainer').getContext('2d');
this.canvas.font = '24px Arial';
this.playButton = document.getElementById('playButton');
this.headsButton = document.getElementById('headsButton');
this.tailsButton = document.getElementById('tailsButton');
this.muteButton = document.getElementById('muteButton');
this.continueButton = document.getElementById('continueButton');
adConfig({
sound: 'on',
});
// On click listeners for the game's buttons.
this.playButton.addEventListener('click', () => {
this.erase();
this.play();
});
this.headsButton.addEventListener('click', () => {
this.choice = 'Heads';
this.flipCoin();
});
this.tailsButton.addEventListener('click', () => {
this.choice = 'Tails';
this.flipCoin();
});
this.muteButton.addEventListener('click', () => {
var soundString = this.muted ? 'on' : 'off';
this.muteButton.innerHTML = this.muted ? 'Mute sound' : 'Un-mute sound';
this.muted = !this.muted;
adConfig({
sound: soundString,
});
});
this.continueButton.addEventListener('click', () => {
if (this.showRewardedAdFn) {
this.showRewardedAdFn();
}
});
this.erase();
}
// Start the game
play() {
if (this.shouldShowAdOnPlay) {
this.shouldShowAdOnPlay = false;
adBreak({
type: 'next', // ad shows at start of next level
name: 'restart-game',
beforeAd: () => { this.disableButtons(); }, // You may also want to mute the game's sound.
afterAd: () => { this.enableButtons(); }, // resume the game flow.
});
}
this.score = 0;
this.canvas.fillText('Score: ' + this.score, 8, 26);
this.canvas.fillText('Heads or Tails?', 66, 150);
this.playButton.style.display = 'none';
this.continueButton.style.display = 'none';
this.headsButton.style.display = 'inline-block';
this.tailsButton.style.display = 'inline-block';
}
[...]
// Guess the flip incorrectly
lose(sideUp) {
this.erase()
this.canvas.fillText('Sorry, it was ' + sideUp, 50, 100);
this.canvas.fillText('Your score was ' + this.score, 50, 150);
this.canvas.fillText('Want to play again?', 45, 200);
this.playButton.style.display = 'inline-block'
this.headsButton.style.display = 'none'
this.tailsButton.style.display = 'none'
this.shouldShowAdOnPlay = true;
adBreak({
type: 'reward', // rewarded ad
name: 'reward-continue',
beforeReward: (showAdFn) => {
this.showRewardedAdFn = () => { showAdFn(); };
// Rewarded ad available - prompt user for a rewarded ad
this.continueButton.style.display = 'inline-block';
},
beforeAd: () => { this.disableButtons(); }, // You may also want to mute the game's sound.
adDismissed: () => {
this.continueButton.style.display = 'none'; // Hide the reward button and continue lose flow.
},
adViewed: () => { this.continueGame(); }, // Reward granted - continue game at current score.
afterAd: () => { this.enableButtons(); }, // Resume the game flow.
});
}
// Continue gameplay at current score
continueGame() {
this.erase();
this.canvas.fillText('Score: ' + this.score, 8, 26);
this.canvas.fillText('Heads or Tails?', 66, 150);
this.playButton.style.display = 'none';
this.continueButton.style.display = 'none';
this.headsButton.style.display = 'inline-block';
this.tailsButton.style.display = 'inline-block';
}
[...]
}
const game = new Game();
Now, add the continue button to your HTML file.
index.html
[...]
<canvas id="gameContainer" height="300px" width="300px"></canvas>
<br>
<button id="playButton">Play</button>
<button style="display:none" id="headsButton">Heads</button>
<button style="display:none" id="tailsButton">Tails</button>
<button style="display:none" id="continueButton">Watch Ad to continue?</button>
<button id="muteButton">Mute sound</button>
<script async
data-adbreak-test="on"
src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-123456789"
crossorigin="anonymous">
</script>
[...]
The coin flip app is now creating ad placements for ads to be displayed.
Your own app may have additional, appropriate places for ads other than when the
game ends. Calling adBreak()
in those places should be similar to this
example.
Turn off testing for production apps
Before releasing your app, it is important to remove or comment out the line
data-adbreak-test="on"
in index.html
, as this code turns on test settings in
production.