How to Build a Multimodal Alexa Skill

Build Your First Visuals with the APL Authoring Tool

Table of Contents

In this section, we are going to learn about the authoring tool and use this to create an APL LaunchRequest response document for our Cake Time skill. By the end of this section, you’ll simply have a screen that says, "Welcome to Cake Time!," with a small image of a cake. To make this screen, you’ll learn about the following:

 

  • APL image and text components
  • Styles
  • Data binding basics
  • Responsive documents

 

Let’s get started.

We suggest using Firefox or Chrome for this course. Safari is not supported in the authoring tool.

1. Hello Cake Time

A. Click here to go to the authoring tool. Save this link as a bookmark, since we will be referencing it throughout the course. Here you will see a number of templates.

 

Authoring Tool Landing page image

B. We are going to start with a Blank Document. This will give us a basic APL document without any components added to the mainTemplate.

C. In the authoring tool, make sure GUI is selected on the left side. This will give us the graphical representation of the structure of the document (under Layouts) as well as some text boxes where properties can be filled in. On the right side you will see the components you can drag and drop into place. Now drag in a text component. You’ll see something like this.

hello world image

 

D. Let’s add some text, "Hello Cake Time!". Click on the text component under the layouts section.

E. Scroll down the middle section until you find the text property of the text component. Change this message to Hello Cake Time!.

F. Above this, find the fontSize property and change this to 72dp. We want to increase the text size to account for our customers which are looking at the device from far away. Now the text is outside of the blue bounding box…​Uh oh! Because the text is bound to such a small rendering box, it no longer displays the text properly with such a large font. Let’s fix this.

G. Scroll down to the Width section and find the width property of our Text component. Delete the value in the width text box, so it will default to auto.

H. Do the same with height under the Height section. Auto is a better setting to use since it will automatically scale the size of the bounding box for the text to match the content. This will help us later if we choose to modify the font size in the future as we did above.

You should now see something like this.

Hello cake time gui

Congratulations! You have a functioning hello caketime document. You can see the document output under the APL tab in the sidebar.

For reference here is the JSON that you should have.

{
    "type": "APL",
    "version": "1.1",
    "settings": {},
    "theme": "dark",
    "import": [],
    "resources": [],
    "styles": {},
    "onMount": [],
    "graphics": {},
    "commands": {},
    "layouts": {},
    "mainTemplate": {
        "parameters": [
            "payload"
        ],
        "items": [
            {
                "type": "Container",
                "items": [
                    {
                        "type": "Text",
                        "paddingTop": "12dp",
                        "paddingBottom": "12dp",
                        "fontSize": "72dp",
                        "text": "Hello Cake Time!"
                    }
                ],
                "height": "100%",
                "width": "100%"
            }
        ]

    }
}

We created a text component and then modified some properties on it using the GUI. To see the full set of text component properties, check out the text component docs. If you looked at that page, you will see that paddingTop and paddingBottom are missing. That is because all components also inherit properties from their parents. Here is the full list of style properties that all components share.

The authoring tool also created a container for us. Containers are components that house other components in an alignment of "column" or "row". Since containers are also components, they have the basic component properties. Containers are essential for building responsive APL documents. Complex documents will use a mix of row and column containers to properly lay out components in a way which scales nicely across devices.

2. Hello Cake Time With Style

Some of the component properties can be defined using styles. Styles are a named set of properties that can be reused across components.

Not all properties can be styled on all components. Here is the full list of style-able properties for each component type.

A. Click the styles sidebar. You’ll see an empty set of parenthesis. Now, replace the empty set with this:

{
    "bigText": {
        "values": [
            {
                "fontSize": "72dp"
            }
        ]
    }
}

B. Click back into the APL tab in the sidebar and you will see your document has been updated with the styles added to the styles section.

C. Now, let’s modify the Text item to delete the fontSize property and add the following:

"style": "bigText"

You will see that the properties are still observed since this is now pulling from the style you defined. You can test this by changing the fontSize property in the style block. Your APL code will now look like this:

final hello apl diagram

Let’s take this a step further and center our text using styles.

D. In the styles section, let’s add the textAlign property and set this to centered.

"textAlign": "center"

This will leave you with a style blob looking like:

{
    "bigText": {
        "values": [
            {
                "fontSize": "72dp",
                "textAlign": "center"
            }
        ]
    }
}

Even though you have not changed the actual text component, since it is using the bigText style, this is now applied to the Text component.

3. Data binding

Did you notice the Data button? This simulates the data source that can be a part of the Alexa.Presentation.APL.RenderDocument directive which is what you send from your skill backend to render the document. We’ll come back to that later, but first, let’s look at how to build our document with data sources.

To reference data in a data source, you will first need to pass a parameter into your APL document. In earlier versions of APL, the entire data source was bound to a single parameter which defaulted to the name, "payload". Now, however, you can pass in multiple parameters which are defined in your data source as long as none of the parameters are called "payload". Using a parameter named "payload" reverts to this old behavior for backwards-compatibility reasons, but it is not recommended to use the legacy naming. If you look at your current APL document, you will see the default authoring tool parameter name of "payload". We will need to change this to match our data parameters. Let’s add and use a simple data source.

A. Inside the mainTemplate.parameters array, replace the word "payload" with "text". This will leave you with:

Copied to clipboard
"mainTemplate": {
    "parameters": [
       "text"
    ]
   ...
}

Now that we have our parameter passed to the document, we can reference it. The data source is a JSON representation of key value pairs. We can nest this object however makes sense for our application. Now, let’s add another Text component which will use a data source and the style we defined. To reference the data, you will write an expression like, ${parameterName.YourDefinedObject}. Let’s modify our APL JSON.

B. Add the following inside the container's items array, underneath the existing text object:

{
    "type": "Text",
    "style": "bigText",
    "text": "${text.middle}"
},
{
    "type": "Text",
    "style": "bigText",
    "text": "${text.end}"
}

C. While we’re at it, let’s change the text data in our very first text component to ${text.start}. Wait a minute…​ Where did that go? The text disappeared because we have no data in the data source we are referencing. Let’s fix this using that Data tab.

D. After clicking the Data button, you’ll see an empty dataset {}. We'll need to add data which follows the structure we set with our parameter we named, "text". So we have a "text" object with "start", "middle", and "end" fields.

E. Add the following to the Data section of the authoring tool:

{
    "text": {
        "start": "Welcome",
        "middle": "to",
        "end": "Cake Time!"
    }
}

The data JSON object represents variable data in the document. We are going to reuse this layout later to render similarly structured text with new data. This technique will allow you to more easily localize the skill since all of the localization logic can live in the backend. In addition, we are going to leverage this functionality to reuse our APL document. You'll see the following:

welcome to cake time image

Now, we have a set of reusable styles across this APL document, and we learned about making a screen using data binding. Let’s add an image of a birthday cake.

4. Adding a birthday cake

We’ll need to add an image component and use databinding. Image components use a URL to the resource that is storing the image. However, image is a primitive component. To scale the image across all of the viewport sizes would take a lot of effort and multiple image resolutions since it does not auto scale. Instead, use the AlexaImage responsive component so we can use a single image that will scale across all device resolutions.

To use the AlexaImage component, we’ll need to add an import. Imports allow you to reference layouts, styles, and resources defined in other packages. We are going to use a standard package called alexa-layouts. The import looks like this:

{
    "name": "alexa-layouts",
    "version": "1.1.0"
}

Add the above import object to your import list in your APL document import section. Afterwards, this will look like:

{
    "type": "APL",
    "version": "1.1",
    "settings": {},
    "theme": "dark",
    "import": [
        {
            "name": "alexa-layouts",
            "version": "1.1.0"
        }
    ],
 ...<Omitted_rest_of_doc>
}

Alexa layouts is an important package for creating responsive layouts. The AlexaImage component has many parameters, most of which are optional.

B. Add the following image block inside of a new container underneath the last text component. This new block should be nested within the existing Container, so be sure to put it in the same "items" array as your text components.

{
    "type": "AlexaImage",
    "alignSelf": "center",
    "imageSource": "${assets.cake}",
    "imageRoundedCorner": false,
    "imageScale": "best-fill",
    "imageHeight":"40vh",
    "imageAspectRatio": "square",
    "imageBlurredBackground": false
}

Let’s break this down:

  • For the fields we are using in the AlexaImage, imageSource is important since it specifies the URL where the image is hosted.
  • We want to give it the standard landscape aspect ratio since we’ll want to maintain our image resolution.
  • When the image scales, it will use the best-fit strategy.
  • To control the size, we are using the imageHeight property and set it to 40% of the viewport height.

To learn more about each of these, check out the parameters in the tech doc. If you look at the tech docs, you’ll notice no reference to alignSelf. This property exists and works because the component is a child component of a container. AlignSelf will override the container alignment for that child, only. There are some other properties that are added since this is a child of a container, too. This relies on a new "assets.cake" object to be added to the data section. The new data section will look like:

{
    "text": {
        "start": "Welcome",
        "middle": "to",
        "end": "Cake Time!"
    },
    "assets": {
        "cake":"https://github.com/alexa/skill-sample-nodejs-first-apl-skill/blob/master/modules/assets/alexaCake_960x960.png?raw=true"
    }
}

C. Go to the Data tab and update your data with the new "assets" object.

D. The last step is to add our new mainTemplate parameter, "assets". Go back to the APL tab and add this to the mainTemplate.parameters list, leaving you with:

Copied to clipboard
"mainTemplate": {
    "parameters": [
       "text",
       "assets"
    ]
   ...
}

Then, you’ll see:

authoring tool image

How does it look? Delicious!? This is starting to look more like a birthday-themed skill. Let’s make this work for the other viewport profiles, too.

5. Making a responsive document

Below the simulator screen that we have been viewing our changes in, you’ll see some Echo devices with screens. We have been using the "Medium Hub" device (which is the Echo Show screen parameters) for now, but there are many other supported devices. Now, let’s try out our document on other screens.

A. Click the various symbols on the top and take note of any issues you find.

The simulator device types

  • Small Hub [Round] (480x480)
  • Small Hub [Landscape] (960x480)
  • Medium Hub (1024x600)
  • Large Hub (1280x800)
  • Extra Large TV (1920x1080)
  • Add Custom Device (any x any)

The last option gives you the ability to create whichever screen resolution you want to simulate the device rendering.

WARNING: spoiler below

broken hello spot image
Figure 1. Well, that doesn’t look quite right…​

B. Our wording is cut off on the Small Hub (Round) device screen. Let’s fix this using the when property. This property allows for boolean evaluation. If true, it will show a component and its children, but if false, it will not. In addition to when, we will be using Resources from the alexa-layouts import. Resources are simply named constants which are referenced with @<Resource_name>. This time, we will use the alexa-layouts package’s definitions of constants representing the above device types and viewport profiles. It allows you to create statements with predefined viewport-specific constants such as:

${@viewportProfile == @hubLandscapeLarge}

rather than

${viewport.width == "1280dp"}

There is no difference between these statements for an Echo Show 2 device request. But, let’s consider there is a new device with a 1300dp wide screen. Should we add another statement to this conditional? What about for a third device in a similar class? By using the Amazon defined resources, we will have better scaling APL documents without even knowing all the possible screen size permutations. This is because @hubLandscapeLarge represents screens between 1280 and 1920 wide, so it encompasses more devices of that class. Even though it is in the same class of device, since the screen does not match exactly the width we are checking, it will not render anything.

C. Since our document looks good on all devices except for the round small hub device, let’s add in a new set of components for that one. Click on the Small Round Hub icon.

D. Since a false evaluation will lead to no children components displaying, let’s add the following statement at the top of our first container.

"when":"${@viewportProfile != @hubRoundSmall}"

E. You should see a black screen! Check it out on the rectangular screens and your components will render. Since we omitted the @hubRoundSmall class from this container and its children, we will need to make a new container which will render when we are on a @hubRoundSmall device.

F. Now under that first container, duplicate the container and child Text components and add it to the items list of the mainTemplate. You’ll want to add the inverse of the statement above to this block:

"when":"${@viewportProfile == @hubRoundSmall}"

G. Now, we’ll fix the display. This can be achieved just by adding some padding to the top of the first text component.

"paddingTop": "75dp",

H. Next, remove all of the other padding values in that those text boxes.

I. Then, remove the cake image. Now your display should look properly on each of the device types. Check your work across the different classes to make sure it looks right to you.

J. We will use this JSON in the next section.. Save your APL document as launchDocument.json.

As an aside, there are a number of different ways we could have fixed this document for the small round hub profile. We could just keep the image and drop the text, or move the image to the background of the small round hub. In terms of structure, we could keep everything in one container and conditionally add the padding and hide the image to provide the same experience. The benefit to this technical approach is that we will not get newly added components by default in the future. Which also means as we iterate and change the rectangular hubs, we will not be modifying the structure of our small round hub screens. Since the screen is fundamentally different from others especially in our design, we forked it. Feel free to take a different approach for other skills if it suits your designs better!

The final APL Document JSON for reference:

Copied to clipboard
{
   "type": "APL",
   "version": "1.1",
   "settings": {},
   "theme": "dark",
   "import": [
       {
           "name": "alexa-layouts",
           "version": "1.1.0"
       }
   ],
   "resources": [],
   "styles": {
       "bigText": {
           "values": [
               {
                   "fontSize": "72dp",
                   "textAlign": "center"
               }
           ]
       }
   },
   "onMount": [],
   "graphics": {},
   "commands": {},
   "layouts": {},
   "mainTemplate": {
       "parameters": [
           "text",
           "assets"
       ],
       "items": [
           {
               "type": "Container",
               "when":"${@viewportProfile != @hubRoundSmall}",
               "items": [
                   {
                       "type": "Text",
                       "style": "bigText",
                       "paddingTop": "12dp",
                       "paddingBottom": "12dp",
                       "text": "${text.start}"
                   },
                   {
                       "type": "Text",
                       "style": "bigText",
                       "paddingTop": "12dp",
                       "paddingBottom": "12dp",
                       "text": "${text.middle}"
                   },
                   {
                       "type": "Text",
                       "style": "bigText",
                       "paddingTop": "12dp",
                       "paddingBottom": "12dp",
                       "text": "${text.end}"
                   },
                   {
                       "type": "AlexaImage",
                       "alignSelf": "center",
                       "imageSource": "${assets.cake}",
                       "imageRoundedCorner": false,
                       "imageScale": "best-fill",
                       "imageHeight":"40vh",
                       "imageAspectRatio": "square",
                       "imageBlurredBackground": false
                   }
               ],
               "height": "100%",
               "width": "100%"
           },
           {
               "type": "Container",
               "when":"${@viewportProfile == @hubRoundSmall}",
               "items": [
                   {
                       "type": "Text",
                       "style": "bigText",
                       "paddingTop": "75dp",
                       "text": "${text.start}"
                   },
                   {
                       "type": "Text",
                       "style": "bigText",
                       "text": "${text.middle}"
                   },
                   {
                       "type": "Text",
                       "style": "bigText",
                       "text": "${text.end}"
                   }
               ],
               "height": "100%",
               "width": "100%"
           }
       ]
   }
}

Let’s put this document to use in the next section.

Complete code in Github