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.

[Download app sample code](https://drive.google.com/drive/folders/1OqaSsmcwtD6D5V4k-JzcMKaVAqqwS9vJ)

## 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.

1. Open the terminal.

2. Go to the directory that contains your index.html file, then run:

       python -m http.server 8000

   > [!NOTE]
   > **Note:** http.server is only available in Python 3.x.

3. In a web browser, go to `localhost:8000`

You can also use any other web server, such as the
[Apache HTTP Server](https://httpd.apache.org/).

## 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](https://support.google.com/adsense/answer/9955214#adsense_code_parameter_descriptions).
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](https://developers.google.com/ad-placement/docs/test). 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`](https://developers.google.com/admob/android/browser/webview/api-for-ads)
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`](https://developer.android.com/reference/android/webkit/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.