Interactive Canvas walkthrough

In this walkthrough, you’ll learn the basics of creating an Action that uses Interactive Canvas. Interactive Canvas is a framework built on the Google Assistant that allows you to create full-screen experiences on smart displays and Android phones. With Interactive Canvas, you can use the same web technologies you already know (like HTML, CSS, and JavaScript) to create visually immersive, interactive gaming experiences.

You won’t be building something end-to-end in this walkthrough— instead, you’ll set up and deploy a prebuilt Interactive Canvas game called Snowman to learn about creating an Action with Interactive Canvas. More specifically, you’ll do the following:

  • Part 1: Learn about how Interactive Canvas works
  • Part 2: Set up, deploy, and test the game
  • Part 3: Learn about the Dialogflow agent for the game
  • Part 4: Take an in-depth look at important files for the game

The Snowman game is a variation of the traditional Hangman game. The game presents you with a number of blank spaces representing an unknown word, and you guess letters that you think may be in the word. If you think you know the word, you can also guess the word you think the blank spaces represent. Each time you guess a letter that’s not in the word, a part of a snowman (the head, torso, arms, or lower body) appears on the screen. After four incorrect guesses, the snowman is fully built and you lose the game. If you successfully complete the word before building the snowman, you win the game.

Figure 1. A partially completed Snowman game.

Part 1: How Interactive Canvas works

An Action that uses Interactive Canvas works similarly to a regular conversational Action. The user still has a back-and-forth conversation with the Action to fulfill their goal; however, instead of returning responses in-line in the conversation, an Interactive Canvas Action sends a response to the user that opens the full-screen web app. The user continues to interact with the web app conversationally or through touch until the conversation is over.

Several components work together to make up a complete Interactive Canvas Action:

  • Conversational Action: An Action that uses a conversational interface to fulfill user requests, which consists of the following components:
    • Dialogflow agent: A project in Dialogflow that you customize to converse with your Action users. Your Dialogflow agent understands natural language and maps things that the user says to functions in your fulfillment code.
    • Fulfillment: Code that is deployed as a webhook that implements the conversational logic for your Action and communicates with your web app.
  • Web app: A front-end web app with customized visuals that your Action sends as a response to users during a conversation.

The conversational Action and web app communicate with each other by using the following:

  • interactiveCanvas API: A JavaScript API that you include in the web app to enable communication between the web app and your conversational Action.
  • HtmlResponse: A response type that provides the URL of the web app and allows state information to be passed to the web app. An HtmlResponse must be included with each response to keep the web app open.

The following diagram shows how Snowman works end-to-end:

  1. The user says “Let’s try the letter i.” to the Assistant device.
  2. The Actions on Google platform routes the user’s request to Dialogflow to match an intent (in our case, it matches the Guess Letter or Word intent).
  3. The fulfillment for the matched intent is run and an HtmlResponse is sent to the Smart Display.
  4. When the web app loads, it registers callbacks with the interactiveCanvas API. The data value is then passed into the registered onUpdate callback of the web app. In our example, the fulfillment sends an HtmlResponse that includes a variable with the letter “i”.
  5. The custom logic for your web app reads the data value of the HtmlResponse and makes the defined changes. In our example, if the letter “i” exists in the word, it appears in the appropriate positions. If it doesn’t exist, a part of the snowman appears.

Part 2: Set up and deploy Snowman

The following sections describe setting up, deploying, and testing the Snowman game.

Install the Firebase CLI

The Firebase CLI allows you to both deploy your Actions project to Cloud Functions and host your web app.

To install or upgrade the CLI, run the following npm command:

$ npm -g install firebase-tools

To verify that the CLI has been installed correctly, open a terminal and run the following command:

firebase --version

Make sure the version of the Firebase CLI is above 3.5.0 so that it has all the latest features required for Cloud Functions. If not, run npm install -g firebase-tools to upgrade the CLI, as shown above.

To log into Firebase, run the following command:

firebase login

When you log in to Firebase, make sure that you use the same Google account that you will use to create your Actions project.

Clone the repository

Next, you need to get the base files for the Snowman game from a GitHub repository called dialogflow-snowman-nodejs. To get these files, follow these steps:

  1. Open a terminal and change to a directory where you usually store coding projects. If you don’t have one, change to your home directory.
  2. To clone this repository, run the following command in your terminal:

    git clone https://github.com/actions-on-google/dialogflow-snowman-nodejs.git
    

Create AoG project and Dialogflow agent

Next, you’ll need to set up an Actions on Google project and configure a Dialogflow agent for this tutorial. To create your Actions on Google project, follow these steps:

  1. Open the Actions console with Chrome. If you’re not already signed in, sign in with the same Google account you used to log into Firebase.
  2. Click New project.
  3. Type in a name for your project (we suggest using the name Snowman). This name is for your own internal reference; later on, you can set an external name for your project.
  4. Click Create project.
  5. Click the Games & fun card.
  6. Click the Conversational card.
  7. Click Deploy in the top navigation.
  8. At the bottom of the page, check the Yes option under Do your Actions use Interactive Canvas?

  9. At the top of the page, click Save.

To set up your Dialogflow agent and associate it with your Actions on Google project, follow these steps:

  1. Click Overview in the top navigation.
  2. Click the drop-down arrow next to Build your Action. Then, click Add Action(s).
  3. Click Add your first action.
  4. Under Built-in intents, select Play game and click Get started in Dialogflow.
  5. On the page titled Create an Agent, enter a name for your agent (we suggest using Snowman).
  6. Click Create Agent.
  7. A page loads with the text Your Dialogflow agent has been created. On this page, click Go To Agent.
  8. Click the gear icon in the left navigation.

  9. Click Export and Import.

  10. Click Restore From Zip.

  11. Upload the agent.zip file from the root directory.

  12. Type “RESTORE” and click Restore.

  13. Click Done.

Deploy your fulfillment and web app

In this section, you’ll set up your Firebase project and associate it with your Actions on Google project. You’ll then deploy your local repository files using the Firebase CLI. You can deploy both the files for your web app and the file that contains your Action’s conversational logic with one terminal command.

To set up your Firebase project, follow these steps:

  1. If you skipped the Install the Firebase CLI section, runfirebase login in the terminal to log in to the same Google account that you used to create your Actions project.
  2. Navigate to the GitHub repo's root directory. Using your Action’s project ID, run the following command:

    $ firebase use <PROJECT_ID>
    
  3. Navigate to the functions/ directory.

  4. Run npm install to install dependencies.

  5. Navigate to the root directory.

  6. Run firebase deployto deploy your fulfillment code and web app to Firebase. After a few minutes, you should see “Deploy complete!”, which indicates that you’ve successfully deployed your webhook to Firebase.

  7. Go to the Dialogflow console and click on the Fulfillment tab in the left navigation.

  8. Toggle on Enable webhook fulfillment.

  9. Copy the Function URL you received in the command line after deployment and paste it into the URL field under Webhook. (The Function URL appears before "Deploy complete!".)

Test game in simulator

Now that you’ve deployed your fulfillment and web app, you can test the Snowman game in the Actions on Google simulator (as the name implies, the simulator lets you simulate a conversation between your user and your Action). To test your project in the simulator, follow these steps:

  1. Navigate to the Actions console.
  2. Click Test in the top navigation.
  3. Under Surface, choose the Smart Display icon.

  4. In the Input field in the bottom left panel, you should see a pre-populated invocation (Talk to my test app). Click into this text field and press enter to start a conversation with your agent. Your web app should render to the right of the simulator screen.

  5. After your agent responds, type a new input into the Input field and press enter to continue the conversation.

Part 3: Understand the Dialogflow agent

For Actions, you need some kind of natural language understanding solution to process what users say and return the appropriate response. One solution for this is creating a Dialogflow agent, which is a project that you customize to converse with your Action users. Your Dialogflow agent maps things that the user says to functions in your fulfillment code.

Dialogflow agents typically have a variety of intents that categorize a user’s intention for one conversation turn. For example, in the Snowman game, when the user asks for instructions, that utterance is matched to the Instructions intent. The intent then responds to the user with instructions for how to play the game. Each intent has training phrases that define what a user might say to match that intent.

The Dialogflow agent for the Snowman game consists of 12 intents that handle both the user’s input and the sendTextQuery values invoked by the web app JavaScript logic.

Some of the more important intents are the following:

  • Welcome - This intent is matched when the user invokes the Action. This intent’s fulfillment provides a welcome message and loads the web app using an HtmlResponse.
  • Fallback - This intent is invoked when none of the other intents are matched.
  • Guess Letter or Word - This intent is matched when the user provides either a letter or a word to guess the answer. This intent uses a system entity to extract the letter or word from the user input.
  • Right Guess and Wrong Guess - These intents are invoked by the web app logic with the interactiveCanvas API’s sendTextQuery() method, which programmatically invokes an intent.
  • Quit - This intent is matched when the user wants to end the game.

The fulfillment logic for each of the intents is in the functions/index.js file. When an intent that uses fulfillment is added to the Dialogflow agent, the index.js file has to be updated with a new intent handler.

Part 4: Understand the code

In this section, you’ll look in-depth at several important files for the Snowman game. These files include the following:

  • index.html
  • main.js
  • index.js
  • assistant.js

index.html

The index.html file defines how your UI looks and loads in several scripts, including the main.js and assistant.js files discussed previously. The entirety of index.html is shown below:

<!doctype html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <title>Snowman with Interactive Canvas</title>
  <script type="text/javascript" src="//cdn.jsdelivr.net/npm/phaser@3.18.1/dist/phaser.min.js"></script>

  <!-- Disable favicon requests -->
  <link rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;,">

  <!-- Load Assistant Interactive Canvas API -->
  <script type="text/javascript"   src="https://www.gstatic.com/assistant/interactivecanvas/api/interactive_canvas.min.js"></script>

  <!-- Load Howler Audio Library -->
  <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/howler/2.1.2/howler.min.js"></script>

  <script type="text/javascript" src="./dictionary.js"></script>
  <script type="text/javascript" src="./classes.js"></script>
  <script type="text/javascript" src="./assistant.js"></script>
  <script type="text/javascript" src="./main.js"></script>

  <style type="text/css">
    body {
      margin: 0;
    }
  </style>
</head>

<body>
</body>
</html>

  • <script type="text/javascript" src="https://www.gstatic.com/assistant/interactivecanvas/api/interactive_canvas.min.js"></script>

    This script loads in the interactiveCanvas library, which allows you to use the Interactive Canvas API.

main.js

The main.js file contains HTML5 game code built with Phaser 3, a JavaScript game development library. While this section won’t look at the Phaser 3 code in depth, it’ll go over a few important lines in this file that pertain to the Assistant:

// Set assistant at game level.
this.assistant = new Assistant(this);
// Call setCallbacks to register assistant callbacks.
this.assistant.setCallbacks();
  • this.assistant = new Assistant (this);

    This line creates a new instance of the Assistant class to be used within the game.

  • this.assistant.setCallbacks(this);

    This line calls the function setCallbacks(), which is defined in the file assistant.js. setCallbacks() registers the interactiveCanvas callbacks, which provide a way for you to respond to information or requests from the conversational Action.

index.js

The index.js file contains the conversational logic for your Action, which is defined in the form of custom Dialogflow intents that send responses back to the user in conversation. Each intent must include an instance of the HtmlResponse class, which defines how the web app renders. The basic flow from end-to-end for intents that include an HtmlResponse is the following:

  1. The user says something to the Action.
  2. Dialogflow matches the user’s utterance to the correct intent.
  3. The fulfillment executes the intent handler and a) returns a spoken response to the user and b) returns a response with a state to the web app.
  4. If there is a change in state, the onUpdate() callback executes and triggers the corresponding function. (For instance, if data contains an attribute command: 'GUESS', guess() is executed.)

The following snippet contains a subset of the code in index.js:

'use strict';

const functions = require('firebase-functions');
const {dialogflow, HtmlResponse} = require('actions-on-google');

const firebaseConfig = JSON.parse(process.env.FIREBASE_CONFIG);
const app = dialogflow({debug: true});

const INSTRUCTIONS = `After 4 incorrect guesses, a snowman is built and the ` +
  `game is over. If you know the word, you can say, for instance, ` +
  `"The word is penguin." You can try another word, or ask for help.`;

const PLAY_AGAIN_INSTRUCTIONS = `You can play another or quit?`;

const WELCOME_RESPONSES = [`Hey, you're back to Snowman! ` +
  `Try guessing a letter in the word, or guess the entire word ` +
  `if you think you know what it is.`, `Welcome back to Snowman! ` +
  `Try guessing a letter in the word, or guess the entire word if ` +
  `you're feeling confident!`, `I'm glad you're back to play! ` +
  `Try guessing a letter in the word or guessing the word.`, `Hey there, ` +
  `you made it! Let's play Snowman. Try guessing a letter in the word or ` +
  `guessing the word.`];

const RIGHT_RESPONSES = ['Right on! Good guess.', 'Splendid!',
  'Wonderful! Keep going!', 'Easy peasy lemon squeezy!', 'Easy as pie!'];

const WRONG_RESPONSES = [`Whoops, that letter isn't in the word. Try again!`,
  'Try again!', 'You can do this!', 'Incorrect. Keep on trying!'];

const REVEAL_WORD_RESPONSES = [`Better luck next time!`,
  `Don't give up, keep on trying!`];

const WIN_RESPONSES = ['Congratulations and BRAVO!',
  'You did it! So proud of you!',
  'Well done!', 'I'm happy for you!',
  'This is awesome! You're awesome! Way to go!'];
/**
 * Pick a random item from an array. This is to make
 * responses more conversational.
 *
 * @param  {array} array representing a list of elements.
 * @return  {string} item from an array.
 */
const randomArrayItem = (array) => {
  return array[Math.floor(Math.random() * array.length)];
};

app.intent('Welcome', (conv) => {
  if (conv.user.last.seen) {
    conv.ask(randomArrayItem(WELCOME_RESPONSES));
  } else {
    conv.ask(`Welcome to Snowman! Try to figure out the word by ` +
      `guessing letters that you think are in the word. ${INSTRUCTIONS}`);
  }
  conv.ask(new HtmlResponse({
    url: `https://${firebaseConfig.projectId}.firebaseapp.com`,
  }));
});

app.intent('Fallback', (conv) => {
  conv.ask(`I don't understand. Try guessing a letter!`);
  conv.ask(new HtmlResponse());
});

/**
 * Guess a letter or word from Snowman.
 *
 * @param  {conv} standard Actions on Google conversation object.
 * @param  {string} letterOrWord from A-Z.
 */
app.intent('Guess Letter or Word', (conv, {letterOrWord}) => {
  conv.ask(`Let's see if ${letterOrWord} is there...`);
  conv.ask(new HtmlResponse({
    data: {
      command: 'GUESS',
      letterOrWord,
    },
  }));
});


/**
 * Trigger to re-play the game again at anytime.
 *
 * @param  {conv} standard Actions on Google conversation object.
 */
app.intent('Play Again', (conv) => {
  conv.ask(`Okay, here's another game!`);
  conv.ask(new HtmlResponse({
    data: {
      command: 'PLAY_AGAIN',
    },
  }));
});

/**
 * Send a random right response back to Google Assistant.
 *
 * @param  {conv} standard Actions on Google conversation object.
 */
app.intent('Right Guess', (conv, {letterOrWord}) => {
  conv.ask(`${letterOrWord} is right. ${randomArrayItem(RIGHT_RESPONSES)}`);
  conv.ask(new HtmlResponse());
});

/**
 * Send a random wrong response back to Google Assistant.
 *
 * @param  {conv} standard Actions on Google conversation object.
 */
app.intent('Wrong Guess', (conv, {letterOrWord}) => {
  conv.ask(`${letterOrWord} is wrong. ${randomArrayItem(WRONG_RESPONSES)}`);
  conv.ask(new HtmlResponse());
});


/**
 * Reveal the word when player loses the game.
 *
 * @param {conv} standard Actions on Google conversation object.
 * @param {word} set by the client.
 */
app.intent('Game Over Reveal Word', (conv, {word}) => {
  conv.ask(`Sorry, you lost. The word is ${word}`);
  conv.ask(new HtmlResponse());
});

/**
 * Reveal the word when player wins the game.
 *
 * @param {conv} standard Actions on Google conversation object.
 */
app.intent('Game Won', (conv, {word}) => {
  conv.ask(`${word} word is right! ${randomArrayItem(WIN_RESPONSES)}`);
  conv.ask(new HtmlResponse());
});

exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);

  • app.intent('Welcome', ...);

    The Welcome intent is matched when the user invokes the Snowman Action. It provides a brief introduction to the Snowman game and describes how it works. If the user has invoked the Action before, the intent briefly welcomes the user back. If the user has not invoked the Action before, the intent responds with a more in-depth introduction to the game.

    This intent also creates an instance of the HtmlResponse class that contains the URL for the web app that renders the game. When this intent is matched, the specified URL opens and callbacks are registered using the interactiveCanvas API. You only need to include the URL for your web app in the opening intent of your Action.

  • app.intent('Fallback', ...);

    The Fallback intent is matched when the user says something that doesn’t match any custom Dialogflow intents. When this intent is matched, it responds to the user that it doesn’t understand and asks them to guess a letter. Because this intent doesn’t need to update the web app’s state, the HtmlResponse does not have a data attribute.

  • app.intent('Guess Letter or Word', ...);

    The Guess Letter or Word intent is matched whenever the user either guesses a letter or guesses the complete word in the game. It sends to the user the response "Let's see if ${letterOrWord} is there…", with ${letterOrWord} corresponding to the letter or word the user guessed.

    This intent creates an instance of HtmlResponse, which maps to other functions (onUpdate() and guess()) that update the state of the web app based on the user’s guess. For example, if the user guesses “t” and the word is “tuna,” the web app updates to show “t” as the first letter of the word.

  • app.intent('Play Again', ...);

    This intent is matched when the user asks to play again. It sends the user the response "Okay, here's another game!" and starts another game by mapping the data attribute in HtmlResponse to its corresponding function.

  • app.intent('Right Guess', ...); app.intent('Wrong Guess', ...);

    The Right Guess intent is matched when the user guesses a letter in the word, and the Wrong Guess intent is matched when the user guesses a letter that isn’t in the word. The Right Guess intent responds to the user that the letter or word they guessed is correct along with a random response from the RIGHT_RESPONSES array, while the Wrong Guess intent responds to the user that the letter or word they guessed is incorrect along with a random response from the WRONG_RESPONSES array.

    Unlike the other intents we’ve discussed, these two intents can only be matched after guess() is called, and they are triggered from the client side using sendTextQuery() calls. sendTextQuery() is a method that sends text queries to the conversational Action. sendTextQuery() can trigger intents in the same way that a user’s input normally triggers intents.

  • app.intent('Game Over Reveal Word', ...);

    The Game Over Reveal Word intent is matched when the user loses the game. It responds to the user with "Sorry, you lost. The word is ${word}." with ${word} corresponding to the game word, along with a random response from the REVEAL_WORD_RESPONSES array. Because this intent does not need to update the web app’s state, the HtmlResponse does not have a data attribute.

    This intent is triggered from a sendTextQuery() call instead of from a user’s input.

  • app.intent('Game Won', ...);

    The Game Won intent is matched when the user wins the game. It responds to the user with "${word} word is right!", along with a random response from the WIN_RESPONSES array. Because this intent does not need to update the web app’s state, the HtmlResponse does not have a data attribute.

    This intent is triggered from a sendTextQuery() call instead of from a user’s input.

assistant.js

The assistant.js file registers the callbacks provided by the interactiveCanvas API. These callbacks enable your web app to respond to information or requests from the conversational Action. This file also defines the guess() function, which is called when the user guesses a letter in the word or guesses the word itself. The contents of this file are below:

'use strict';

/**
 * This class is used as a wrapper for Google Canvas Assistant class along
 * with its callbacks.
 */
class Assistant {
  /**
   * @param  {phaser} game which serves as a container of all visual
   * and audio elements.
   */
  constructor(game) {
    this.canvas = window.interactiveCanvas;
    this.game = game;
    const that = this;
    this.commands = {
      GUESS: function(data) {
        that.guess(data.letterOrWord.toUpperCase());
      },
      PLAY_AGAIN: function() {
        that.game.startSnowman();
      },
      TOGGLE_CAPTIONS: function() {
        that.game.toggleCaptions(that.game.wordText, that.game.commandText,
            that.game.letterText, that.game.statusText);
      },
      DEFAULT: function() {
        // do nothing, when no command is found
      },
    };
  }

  /**
   * Register all callbacks used by Google Assistant
   * executed during game creation time.
   *
   */
  setCallbacks() {
    const that = this;
    // declare assistant canvas callbacks
    const callbacks = {
      onUpdate(data) {
        that.commands[data.command ? data.command.toUpperCase() :
          'DEFAULT'](data);
      },
    };
    // called by the Interactive Canvas web app once web app has loaded to
    // register callbacks
    this.canvas.ready(callbacks);
  }

  /**
   * Check for every user guess.
   *
   * Orchestrate instructions to change visual element and trigger sounds.
   * Trigger other instructions to Actions on Google to trigger intents
   * that present options to users.
   * @param {string} letterOrWord guess to be checked against the actual word.
   */
  guess(letterOrWord) {
    const foundLetter = this.game.wordPlaceholder.isInWord(letterOrWord);
    const rightWord = this.game.wordPlaceholder.word.text;
    const displayWinOrLoseScreen = () => {
      this.game.finishGame(this.game.wordPlaceholder.userWins());
      this.canvas.sendTextQuery('Play again or quit?');
    };

    this.game.setCaptions.bind(this.game)('Guess letter', letterOrWord,
        rightWord,
      foundLetter ? 'Correct' : 'Incorrect');
    if (!this.game.wordPlaceholder.isGameOver()) {
      if (foundLetter) { // trigger right guess random response
        this.game.correctSound.play('up');
        this.canvas.sendTextQuery(`Right Guess ${letterOrWord}`);
      } else { // trigger wrong guess intent from Actions on Google
        this.game.wrongSound.play();
        this.canvas.sendTextQuery(`Wrong Guess ${letterOrWord}`);
      }
    } else { // when game is over, present different options, and present
      // you win or lose image
      setTimeout(displayWinOrLoseScreen, 8000);
      if (this.game.wordPlaceholder.userWins()) {
        this.game.winSound.play();
        this.canvas.sendTextQuery(`${rightWord.toUpperCase()} word is right`);
      } else {
        this.game.loseSound.play();
        this.canvas.sendTextQuery(`The word to guess is ${rightWord.toUpperCase()}`);
        // Reveal the word in placeholder
        this.game.wordPlaceholder.isInWord(rightWord.toUpperCase());
      }
    }
  }
}
  • setCallbacks()

    This function defines and registers interactiveCanvas callbacks, which provide a way for your web app to respond to HTML responses provided by the Dialogflow intent fulfillment. setCallbacks() is only called when the Snowman game starts.

    onUpdate()

    A callback contained within the callbacks object that executes when an HtmlResponse is sent from the conversational Action. onUpdate() takes the parameter data, which is an object containing the updated data sent from fulfillment. onUpdate() matches the data attribute from an HtmlResponse to its corresponding function that updates the web app.

    If data.command doesn’t exist, the DEFAULT function executes (this function does nothing).

  • this.commands = { … }

    This object contains the functions that update the web app. These functions correspond to the intentsGuess Letter or Word, Play Again, and Toggle Captions. onUpdate() triggers these functions by matching data.command in each intent’s HtmlResponse to its corresponding function.

  • guess(letterOrWord)

    This function is called from onUpdate() when the user guesses a letter or word. If the letter is in the word, sendTextQuery() is called with the Right Guess intent’s training phrase as a parameter, which triggers the intent. If the letter is not in the word, sendTextQuery() is called with the Wrong Guess intent’s training phrase as a parameter.

    const displayWinOrLoseScreen = () => {

    This function is called when the user wins or loses the game. If the user wins or loses the game, the Action presents different options and images, and sendTextQuery() is called with the Play Again Instructions intent’s training phrase as a parameter, which triggers the intent.

    if (this.game.wordPlaceholder.userWins()) {

    After setTimeout() is called, if the user has won, the game plays a triumphant sound and sendTextQuery() triggers the Game Won intent by passing in the intent’s training phrase as a parameter.

    If the user has lost, a sad sound plays and sendTextQuery() triggers the Game Over Reveal Word intent by passing in the intent’s training phrase as a parameter. This intent reveals the word that the user couldn’t guess during the game.

Extra game features (optional)

This section covers the intents that map to game features this walkthrough hasn’t discussed. These features involve the following intents from the index.js file:

  • app.intent('Toggle Captions', ...);

    The Toggle Captions intent makes captions appear on the upper left hand corner of the web app. These captions include the word the user is trying to guess.

    This intent includes an HtmlResponse instance, and the instance’s data attribute, command: 'TOGGLE_CAPTIONS', maps to the corresponding function toggleCaptions() that carries out the action.

  • app.intent('Instructions', ...);

  • app.intent('Play Again Instructions', ...);

    The Instructions intent tells the user how to play the Snowman game, while the Play Again Instructions intent gives the user a shortened version of the instructions.

    The HtmlResponse in each of these intents does not contain a data attribute because the web app does not need to be updated.

Conclusion

Congratulations on finishing the Interactive Canvas walkthrough! You’ve now learned the skills necessary to build your own customized Interactive Canvas gaming experience. If you want to learn more about building an Interactive Canvas Action, check out the following resources: