# Continue building with client or server-side fulfillment

Now that you've learned the basics, you can enhance and customize your Action
with Canvas-specific methods. You can choose to develop your Action with the
[client fulfillment model](https://developers.google.com/assistant/interactivecanvas#client-side_and_server-side_fulfillment)
or the server-side fulfillment model when you create your Actions project. For
more information about these options, see
[Enable Interactive Canvas](https://developers.google.com/assistant/interactivecanvas/prompts#enable_interactive_canvas).

If you select the client fulfillment model option, you can use the following in
your Action:

- All APIs listed under [Build with client-side fulfillment](https://developers.google.com/assistant/interactivecanvas/fulfillment#build_with_client-side_fulfillment)
- All APIs listed under [Build with server-side fulfillment](https://developers.google.com/assistant/interactivecanvas/fulfillment#build_with_server-side_fulfillment)

  > [!NOTE]
  > **Note:** For client-side fulfillment, we recommend only calling a webhook for non-Canvas features that require it, such as account linking.

- The [`onTtsMark()`](https://developers.google.com/assistant/interactivecanvas/callbacks#onttsmark) callback

If you select the server fulfillment model option, you can use the following in
your Action:

- All APIs listed under [Build with server-side fulfillment](https://developers.google.com/assistant/interactivecanvas/fulfillment#build_with_server-side_fulfillment)
- [Callbacks](https://developers.google.com/assistant/interactivecanvas/callbacks)

Some Interactive Canvas APIs are not recommended for use with a particular
fulfillment model. The following table shows the APIs enabled when you select
the client fulfillment option, and whether these APIs are are recommended or
discouraged for each model:

> [!WARNING]
> **Warning:** The APIs listed in the following table are in Developer Preview. You can test them in the simulator, but do not deploy an Action that uses any of these features to alpha, beta, or production channels. Actions deployed using these features will not function on end-user devices.

|---|---|---|
| **API name** | **Supported in server fulfillment model?** | **Supported in client fulfillment model?** |
| `sendTextQuery()` | Yes | Supported but not recommended (see [sendtextQuery()](https://developers.google.com/assistant/interactivecanvas/fulfillment#sendtextquery) for more information) |
| `outputTts()` | Yes | Yes |
| `triggerScene()` | No | Yes |
| `createIntentHandler(), expect(), clearExpectations(), prompt()` | No | Yes |
| `createNumberSlot(),createTextSlot, createConfirmationSlot, createOptionsSlot() ` | No | Yes |
| `setHomeParam(), getHomeParam(), setUserParam(), getUserParam()` | No | Yes |

The following sections explain how to implement APIs for the client and
server-side fulfillment models in your Interactive Canvas Action.

## Build with client-side fulfillment

You can implement the following Interactive Canvas APIs in your web app logic:

### `outputTts()`

<br />

> [!WARNING]
> **Warning**: This API is currently in Developer Preview. You can test this API in the simulator, but do not deploy an Action that uses this feature to alpha, beta, or production channels. Actions deployed using these features will not function on end-user devices.

<br />

This API allows you to output text-to-speech (TTS) from a device without sending
a static prompt from Actions Builder or invoking a webhook. If no server-side
logic associated with the TTS is required, you can use `outputTts()` from the
client-side to skip a trip to your server and provide a faster response to
your users.

Client-side `outputTts()`can interrupt or cancel server-side TTS. You can avoid
interrupting server-side TTS by taking these precautions:

- Avoid calling `outputTts()` at the beginning of the session; instead, use server-side TTS in your Action's first conversational turn.
- Avoid calling `outputTts()` consecutively without user action in between.

The following snippet shows how to use `outputTts()` to output TTS from the
client-side:

    interactiveCanvas.outputTts(
          '<speak>This is an example response.</speak>', true);

You can also use `outputTts()` with [`onTtsMark()`](https://developers.google.com/assistant/interactivecanvas/callbacks#onttsmark)
to place SSML markers into the text sequence. Using `onTtsMark()` syncs your web
app animation or game state at specific points of a SSML TTS string, as shown
in the following snippet:

    interactiveCanvas.outputTts(
          '<speak>Speak as <mark name="number" /> number <break time="700ms"/>' +
          '<say-as interpret-as="cardinal">12345</say-as> <break time="300ms"/> ' +
          'Speak as <mark name="digits" /> digits <break time="700ms"/>' +
          '<say-as interpret-as="characters">12345</say-as></speak>', true);

In the previous example, the two [marks](https://developers.google.com/assistant/conversational/ssml#mark)
customizing your response are sent to the web app with the TTS.

### Handle intent fulfillment on the client

<br />

> [!WARNING]
> **Warning**: This API is currently in Developer Preview. You can test this API in the simulator, but do not deploy an Action that uses this feature to alpha, beta, or production channels. Actions deployed using these features will not function on end-user devices.

<br />

In the server fulfillment model for Interactive Canvas, all intents
have to be handled by a webhook, which increases latency in your Action. Instead
of calling a webhook, you can handle fulfillment of intents within your web app.

To handle intents client-side, you can use the following APIs:

- `createIntentHandler()`: A method that allows you to define intent handlers in your web app code for custom intents defined in Actions Builder.
- `expect()`: A method that activates/registers the intent handler so that a user can match the intent.
- `clearExpectations()`: A method that clears the expectations for all currently activated intents so that the intents cannot be matched, even if a user says an utterance that matches the intent.
- `deleteHandler()`: A method that disables individual intent handlers so those intents cannot be matched.

With these APIs, you can selectively activate or disable intents for different
states of your Interactive Canvas Action. You must use `expect()` on intent
handlers to activate those intents.

#### Activate intent handlers

Activating an intent handler is a two-step process. First, you must define the
intent in Actions Builder. Next, to make the intent matchable, you need to
call `expect()` on the intent handler.

To configure and activate an intent handler on the client-side, follow
these steps:

1. Open your project in the Actions console and add a **Custom intent.**
2. Select **Yes** for **Is this a global intent?**

   > [!NOTE]
   > **Note:** Intents you create in Actions Builder that are triggered from the client-side must be configured as global intents.

3. Configure your intent and click **Save**.

4. Define the handler for the intent in your web app logic, as shown in the following snippet:

       /**
       * Define handler for intent.
       */
       const bookTableIntent = interactiveCanvas.createIntentHandler('reserveTable',
         matchedIntent => {
           console.log("Intent match handler to reserve a table was triggered!");
         });

       /**
       * Define handler for intent with an argument.
       */
       const bookTableIntent = interactiveCanvas.createIntentHandler('reserveTable',
         matchedIntent => {
           const numberOfPeople = matchedIntent.getIntentArg('numberOfPeople');
           console.log(`Intent match handler to reserve a table for ${number of people} was triggered!`);
         });

5. Call the `expect()` method to register the intent handler, as shown in the
   following snippet:

       /**
       * Define handler for intent and expect() it.
       */
       const bookTableIntent = interactiveCanvas.createIntentHandler('reserveTable',
         matchedIntent => {
           console.log("Intent match handler to reserve a table was triggered!");
         });
       var handler = interactiveCanvas.expect(bookTableIntent);

#### Disable intent handlers

After you define an intent handler, you can activate or deactivate
the intent as needed for your Action. When you call `expect()` to activate an
intent, it returns an object with a `deleteHandler()` method, which you can use to
disable the newly-created handler. The intent handler definition persists even
if the intent is not currently active, so you can re-activate the intent when
needed.

To disable an intent handler, call `deleteHandler()` on the intent handler,
as shown in the following snippet:

```
    /**
    * Define handler for intent and expect() it.
    */
    const bookTableIntent = interactiveCanvas.createIntentHandler('reserveTable',
      matchedIntent => {
        console.log("Intent match handler to reserve a table was triggered!");
      });
    var handler = interactiveCanvas.expect(bookTableIntent);
    
    // Delete the handler for `bookTableIntent`.
    handler.deleteHandler();
    
```

You can call `expect()` to re-add a disabled intent handler, as shown in the
following snippet:

        // Re-add the `bookTableIntent` handler.
        handler = interactiveCanvas.expect(bookTableIntent);

To disable intents in bulk, you can use the `clearExpectations()` method, which
disables all currently activated intents. The following snippet shows how to
clear the expectations for all intent handlers:

    interactiveCanvas.clearExpectations();

### Handle slot filling on the client

<br />

> [!WARNING]
> **Warning**: This API is currently in Developer Preview. You can test this API in the simulator, but do not deploy an Action that uses this feature to alpha, beta, or production channels. Actions deployed using these features will not function on end-user devices.

<br />

Instead of adding slot filling to a scene within Actions Builder, you can
handle slot filling directly in your web app.

To handle slot filling on the client-side, you must first create a slot using
one of the following APIs:

- `createNumberSlot(callback, hints)`: A method that allows you to define a number slot in your web app code. Used to prompt the user for a number.
- `createTextSlot(callback, hints)`: A method that allows you to define a text slot in your web app code. Used to prompt the user for a word.
- `createConfirmationSlot(callback, hints)`: A method that allows you to define a confirmation slot in your web app code. Used to prompt the user for confirmation (yes/no).
- `createOptionsSlot(options, callback, hints)`: A method that allows you to define an options slot in your web app code. Used to prompt the user to select from a list of predefined options.

When you create a slot, you can optionally define `triggerHints`, which are
keywords that improve the natural language understanding (NLU) system for your
Action. These keywords should be short words that the user might say when
filling a slot. For example, the `triggerHints` keyword for a number slot could
be `years`. When a user replies to a question about their age in the conversation
with the response *"I am thirty years old"*, your Action is more likely to
recognize that the user is filling the slot appropriately.

> [!NOTE]
> **Note:** We recommend testing your Action without keywords for `triggerHints` initially, and only adding them if the NLU needs improvement.

After creating a slot, you can prompt the user for a slot using the `prompt` API:

- `prompt(tts, slot)`: A method that will output TTS to the user, prompting them for an expected slot to be filled.

Calling `prompt()` returns a promise with the status and value of the
filled slot.

#### Create number slot

A number slot allows you to prompt a user for a number during the
conversation. For more information about slot filling, see the
[Slot filling](https://developers.google.com/assistant/conversational/scenes#slot_filling) section of
the Actions Builder documentation.

To prompt the user to fill a number slot on the client-side, follow these steps:

1. Call the `createNumberSlot()` method to create a number slot in your web
   app logic:

       /**
        * Create number slot.
        */
       const triggerHints = { associatedWords: ['guess number', 'number'] };
       const slot = interactiveCanvas.createNumberSlot(
         number => {
           console.log(`Number guessed: ${number}.`);
         }, triggerHints);

2. Call the `prompt()` method to prompt the user for the slot, and handle the
   slot value from the returned promise, as shown in the following snippet:

       const promptPromise = interactiveCanvas.prompt(
         { text: 'What number am I thinking of between 1 and 10?' }, slot);

       promptPromise.then(
         answer => {
           if (answer.status == interactiveCanvas.AnswerStatus.ANSWERED) {
             // answer === {value: 5, status: ANSWERED}
             // Do something with answer.value
           } else {
             console.error('Promise returned unsuccessful status ' + answer.status);
           }
         });

#### Create text slot

A text slot allows you to prompt a user for a word during the
conversation. For more information about slot filling, see the
[Slot filling](https://developers.google.com/assistant/conversational/scenes#slot_filling) section of
the Actions Builder documentation.

To prompt the user to fill a text slot on the client-side, follow these steps:

1. Call the `createTextSlot()` method to create a text slot in your web app logic:

       /**
        * Create text slot.
        */
       const triggerHints = { associatedWords: ['favorite color', 'color'] };
       const slot = interactiveCanvas.createTextSlot(
         text => {
           console.log(`Favorite color: ${text}.`);
         }, triggerHints);

2. Call the `prompt()` method to prompt the user for the slot, and handle the
   slot value from the returned promise, as shown in the following snippet:

       const promptPromise = interactiveCanvas.prompt(
         { text: 'What is your favorite color?' }, slot);

       promptPromise.then(
         answer => {
           if (answer.status == interactiveCanvas.AnswerStatus.ANSWERED) {
             // answer === {value: "red", status: ANSWERED}
             // Do something with answer.value
           } else {
             console.error('Promise returned unsuccessful status ' + answer.status);
           }
         });

#### Create confirmation slot

A confirmation slot allows you to prompt a user for confirmation (the user can
respond "Yes" or "No" to fill the slot). For more information about slot filling, see the
[Slot filling](https://developers.google.com/assistant/conversational/scenes#slot_filling) section of
the Actions Builder documentation.

To prompt the user to fill a confirmation slot on the client-side, follow
these steps:

1. Call the `createConfirmationSlot()` method to create a confirmation slot in
   your web app logic:

       /**
        * Create confirmation slot (boolean).
        */
       const triggerHints = { associatedWords: ['user confirmation', 'confirmation'] };
       const slot = interactiveCanvas.createConfirmationSlot(
         yesOrNo => {
           console.log(`Confirmation: ${yesOrNo}`);
         }, triggerHints);

2. Call the `prompt()` method to prompt the user for the slot, and handle the
   slot value from the returned promise, as shown in the following snippet:

       const promptPromise = interactiveCanvas.prompt(
         { text: 'Do you agree to the Terms of Service?' }, slot);

       promptPromise.then(
         answer => {
           if (answer.status == interactiveCanvas.AnswerStatus.ANSWERED) {
             // answer === {value: true, status: ANSWERED}
             // Do something with answer.value
           } else {
             console.error('Promise returned unsuccessful status ' + answer.status);
           }
         });

#### Create options slot

An options slot allows you to prompt the user to select from a list of
predefined options. For more information about slot filling, see the
[Slot filling](https://developers.google.com/assistant/conversational/scenes#slot_filling) section of
the Actions Builder documentation.

To prompt the user to fill an options slot on the client-side, follow these steps:

1. Call the `createOptionsSlot()` method to create an options slot in your
   web app logic:

       /**
        * Create options slot (list selection).
        */
       const triggerHints = { associatedWords: ['select fruit', 'choose fruit'] };
       // Define selectable options
       const options = [{
         key: 'apple',
         synonyms: ['apple', 'large apple', 'gala apple'],
       }, {
         key: 'banana',
         synonyms: ['banana', 'green banana', 'plantain'],
       }];
       const slot = interactiveCanvas.createOptionsSlot(
         options,
         selectedOption => {
           console.log(`You have selected ${selectedOption} as your fruit.`);
         }, triggerHints);

2. Call the `prompt()` method to prompt the user for the slot, and handle the
   slot value from the returned promise, as shown in the following snippet:

       const promptPromise = interactiveCanvas.prompt(
         { text: 'Would you like a banana or an apple?' }, slot);

       promptPromise.then(
         answer => {
           if (answer.status == interactiveCanvas.AnswerStatus.ANSWERED) {
             // answer === {value: 'apple', status: ANSWERED}
             // Do something with answer.value
           } else {
             console.error('Promise returned unsuccessful status ' + answer.status);
           }
         });

### `triggerScene()`

<br />

> [!WARNING]
> **Warning**: This API is currently in Developer Preview. You can test this API in the simulator, but do not deploy an Action that uses this feature to alpha, beta, or production channels. Actions deployed using these features will not function on end-user devices.

<br />

> [!NOTE]
> **Note:** Although `triggerScene()` is a client-side API, it can be used to invoke a conversational flow that may require a webhook.

The `triggerScene()` API allows you to transition to another scene in your
Interactive Canvas Action from your client-side fulfillment. With
`triggerScene()`, you can also switch from client-side fulfillment to server-side
fulfillment when the user needs to access a
[system scene](https://developers.google.com/assistant/conversational/scenes#system_scenes) in Actions Builder
that requires a webhook. For example, you can call `triggerScene()`when a user
needs to link their account or receive notifications; then, you can return from
that scene to client-side fulfillment with a `Canvas` prompt.

The following snippet shows how to implement `triggerScene()` in your Action:

    interactiveCanvas.triggerScene('SceneName').then((status) => {
      console.log("sent the request to trigger scene.");
    }).catch(e => {
      console.log("Failed to trigger a scene.");
    })

### Home and user storage on the client

<br />

> [!WARNING]
> **Warning**: This API is currently in Developer Preview. You can test this API in the simulator, but do not deploy an Action that uses this feature to alpha, beta, or production channels. Actions deployed using these features will not function on end-user devices.

<br />

Instead of using a webhook to get and set home and user storage values, you can
call client-side APIs to handle home and user storage in your web app. Your web
app can then use these stored values across multiple sessions (for example, in
prompts and conditions), and can access values for a specific household or
user when needed. Using these APIs can reduce latency in your Interactive Canvas
Action because you no longer need to call a webhook to get and set
storage values.

Home and user storage in the web app follows the same general principles as
storage in the webhook. For more information about home and user storage, see
the documentation for [Home storage](https://developers.google.com/assistant/conversational/storage-home)
and [User storage](https://developers.google.com/assistant/conversational/storage-user).

#### Client-side home storage

Home storage allows you to store values for household users based on the
[home graph](https://developers.google.com/assistant/smarthome/concepts/homegraph), and is shared across all
sessions in the household. For example, if a user plays an Interactive Canvas
game in a household, the score of the game can be stored in home storage, and
other household members can continue playing the game with the stored score.

To enable your Action to support home storage, follow these steps:

1. In the Actions console, navigate to **Deploy \> Directory information \>
   Additional Information**.
2. Check the **Yes** box for **Do your Actions use home storage?**

   ![](https://developers.google.com/static/assistant/interactivecanvas/images/home-storage-option.png)

To write a value to home storage in your web app, call the `setHomeParam()`
method, as shown in the following snippet:

    interactiveCanvas.setHomeParam('familySize',  10).then(
          result => {
            console.log('Set home param success');
          },
          fail => {
            console.error(err);
          });

To read a value from home storage in your web app, call the `getHomeParam()`
method, as shown in the following snippet:

    interactiveCanvas.getHomeParam('familySize').then(
          value => {
            console.log(JSON.stringify(result));
          },
          err => {
            console.error(err);
          }
      );

To clear all existing home storage, call the `resetHomeParam()` method, as shown
in the following snippet:

    interactiveCanvas.resetHomeParam();

#### Client-side user storage

User storage allows you to store parameter values for a specific, verified user
across multiple sessions. For example, if a user is playing a game, the score of
the game can be stored for that user. In a subsequent gameplay session, the user
can continue playing the game with the same score.

To write a value to user storage in your web app, call the `setUserParam()`
method, as shown in the following snippet:

    interactiveCanvas.setUserParam('color',  'blue').then(
          result => {
            console.log('Set user param success');
          },
          err => {
            console.error(err);
          });

To read a value from user storage in your web app, call the `getUserParam()`
method, as shown in the following snippet:

    interactiveCanvas.getUserParam('color').then(
          value => {
            console.log(JSON.stringify(result));
          },
          err => {
            console.error(err);
          }
      );

To clear all existing user storage, call the `resetUserParam()` method, as shown
in the following snippet:

    interactiveCanvas.resetUserParam();

### `setCanvasState()`

The `setCanvasState()` method allows you to send state data from your Interactive
Canvas web app to your fulfillment, and notifies Assistant that the web app has
updated its state. The web app sends its updated state as a JSON object.

Calling `setCanvasState()` does not invoke an intent. After
invoking `setCanvasState()`, if `sendTextQuery()` is invoked or the user query
matches an intent in the conversation, the data that was set with `setCanvasState()`
in the previous conversational turn is then available in subsequent turns of
conversation.

In the following snippet, the web app uses `setCanvasState()` to set Canvas state
data:

### JavaScript

```javascript
this.action.canvas.setCanvasState({ score: 150 })
    
```

#### Reference Canvas state from webhook

You can reference stored Canvas state values in your fulfillment code. To reference the
value, use `conv.context.canvas.state.KEY` syntax, where
`KEY` is the key given when the Canvas state value was set.

For example, if you previously stored a high score value for a game in Canvas
state as the parameter `score`, reference that value using
`conv.context.canvas.state.score` to access that value in fulfillment:

### Node.js

```javascript
app.handle('webhook-name', conv => {
    console.log(conv.context.canvas.state.score);
})
    
```

#### Reference Canvas state within prompts

You can reference stored Canvas state values in a prompt. To reference the
value, use `$canvas.state.KEY` syntax, where
`KEY` is the key given when the Canvas state value was set.

For example, if you previously stored a high score value for a game in Canvas
state as the parameter `score`, reference that value using `$canvas.state.score`
to access that value in a prompt:

### JSON

```
{
  "candidates": [{
    "first_simple": {
      "variants": [{
        "speech": "Your high score is $canvas.state.score."
      }]
    }
  }]
}
    
```

#### Reference Canvas state within conditions

You can also reference stored Canvas state values in conditions. To
reference the value, use the `canvas.state.KEY`
syntax, where `KEY` is the key given when the Canvas
state value was set.

For example, if you previously stored a high score value for a game in Canvas
state as the parameter `score` and want to compare it with the value `999` in a
condition, you can reference the stored value in your condition using
`canvas.state.score`. Your condition expression looks like the following:

### Condition syntax

```
canvas.state.score >= 999
    
```

### `sendTextQuery()`

> [!NOTE]
> **Note:** Using `sendTextQuery()` when client fulfillment is enabled is not recommended because it requires a round trip to the server. Use client-side APIs for the intended functionality instead.

The `sendTextQuery()` method sends text queries to the Conversational Action to
programmatically match an intent. This sample uses `sendTextQuery()` to restart
the triangle-spinning game when the user clicks a button. When the user clicks
the "Restart game" button, `sendTextQuery()` sends a text query that
matches the `Restart game` intent and returns a promise. This promise results in
`SUCCESS` if the intent is triggered and `BLOCKED` if it is not. The following
snippet matches the intent and handles the success and failure cases of the
promise:

### JavaScript

```javascript
...
/**
* Handle game restarts
*/
async handleRestartGame() {
    console.log(`Request in flight`);
    this.button.texture = this.button.textureButtonDisabled;
    this.sprite.spin = false;
    const res = await this.action.canvas.sendTextQuery('Restart game');
    if (res.toUpperCase() !== 'SUCCESS') {
        console.log(`Request in flight: ${res}`);
        return;
    }
    console.log(`Request in flight: ${res}`);
    this.button.texture = this.button.textureButtonDisabled;
    this.sprite.spin = false;
}
...
    
```

If the promise results in `SUCCESS`, the `Restart game` webhook handler sends a
`Canvas` response to your web app:

### JavaScript

```javascript
...
app.handle('restart', conv => {
  conv.add(new Canvas({
    data: {
      command: 'RESTART_GAME'
    }
  }));
});
...
    
```

This `Canvas` response triggers the `onUpdate()` callback, which executes the
code in the `RESTART_GAME` code snippet below:

### JavaScript

```javascript
...
RESTART_GAME: (data) => {
    this.scene.button.texture = this.scene.button.textureButton;
    this.scene.sprite.spin = true;
    this.scene.sprite.tint = 0x00FF00; // green
    this.scene.sprite.rotation = 0;
},
...
    
```

## Build with server-side fulfillment

You can implement the following Interactive Canvas APIs in your webhook:

### Enable full-screen mode

By default, Interactive Canvas web apps include a [header](https://developers.google.com/assistant/interactivecanvas/design#header_or_toast_message)
at the top of the screen with the name of your Action. You can use `enableFullScreen`
to remove the header and replace it with a temporary toast that appears in the
loading screen, which allows your user to have a full-screen experience
while interacting with your Action. The toast message shows the Action's display
name, the developer's name, and instructions for exiting the Action, and the toast
color changes depending on what the user selects as a theme on their device.
![](https://developers.google.com/static/assistant/interactivecanvas/images/toast-message.png) **Figure 1.** A toast message on the loading screen for an Action.

> [!NOTE]
> **Note:** To set the developer name, go to your project in the [Actions console](https://console.actions.google.com) and navigate to **Deploy \> Directory information \> Contact details \> Developer name**.

If a user interacts with your Action frequently, the toast message temporarily
stops appearing on the loading screen. If the user does not engage with your
Action for a while, the toast message reappears when they launch the Action.

You can enable full-screen mode in your webhook or in a static prompt in
Actions Builder.

To enable full-screen mode in your webhook, follow this step:

1. Set the `enableFullScreen` field to `true` in the first `canvas` response
   returned by the webhook in a session. The following snippet is an example
   implementation using the Node.js client library:

        const { conversation, Canvas } = require('@assistant/conversation');
        const functions = require('firebase-functions');

        const app = conversation();

        app.handle('invocation_fullscreen', conv => {
          conv.add(new Canvas(
            {
              url: 'https://example-url.com',
              enableFullScreen: true
            }));
        });

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

To enable full-screen mode in a static prompt in Actions Builder, follow these steps:

1. Open your project in the [Actions console](https://console.actions.google.com).
2. Click **Develop** in the navigation bar and open the prompt that includes the first `canvas` response.
3. Set `enable_full_screen` to `true`, as shown in the following snippet:

        {
         "candidates": [
           {
             "canvas": {
               "url": "https://example-url.com",
               "enable_full_screen": true
             }
           }
         ]
       }

### `continueTtsDuringTouch`

By default, when a user taps the screen while using an Interactive Canvas
Action, the TTS stops playing. You can enable the TTS to continue playing
when users touch the screen with `continueTtsDuringTouch`. This behavior cannot
be toggled on and off in the same session.

> [!NOTE]
> **Note:** When a user taps on any on-screen element that triggers a new text query, such as buttons that invoke `sendTextQuery()`, the TTS stops playing, even if `continueTtsDuringTouch` is set to `true`.

You can implement this behavior in your webhook or in a static prompt
in Actions Builder.

To enable TTS to continue after the user taps the screen in your webhook, follow
this step:

- Set the `continueTtsDuringTouch` field to `true` in the first `canvas` response
  returned by the webhook in a session. The following snippet is an example
  implementation using the Node.js client library:

      const { conversation, Canvas } = require('@assisant/conversation');
      const functions = require('firebase-functions');

      const app = conversation();

      app.handle('intent-name', conv => {
        conv.add(new Canvas(
          {
            url: 'https://example-url.com',
            continueTtsDuringTouch: true
          }));
      });

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

To enable TTS to continue after the user taps the screen in a static prompt in
Actions Builder, follow these steps:

1. Open your project in the [Actions console](https://console.actions.google.com).
2. Click **Develop** in the navigation bar and open the prompt the includes the first `canvas` response.
3. Set `continue_tts_during_touch` to `true`, as shown in the following snippet:

         {
          "candidates": [
            {
              "canvas": {
                "url": "https://example-url.com",
                "continue_tts_during_touch": true
              }
            }
          ]
        }