Overview
In this tutorial, we will go over a Hello World
example using Alexa Presentation Language (APL) to display static text on different Echo devices with displays, such as Echo Show, Echo Dot and Fire TV Cube. We will be using the APL Authoring Tool to assist us in generating an APL document for a skill to render visuals on displays when present.
Here is what we will go over in the tutorial:
- Creating an initial Hello World APL document
- Rendering the APL document to a display
- Conditionally display visuals depending on the lack or presence of a display
What is APL?
Alexa skills now support multimodal display capabilities that can enhance your voice experience. Visual complements to your skills could be built through the Alexa Presentation Language (APL). The developer console provides an Authoring Tool that let's you add components and update values to generate an APL document which stores layout information in JSON format. The APL document could also be created through raw JSON as well.
Behind the Scenes (Optional)
When getting started with APL, it is helpful to know that the underlying layout mechanism is based on a subset of CSS Flexbox rendered by the Yoga layout engine. Flexbox provides a grid-like layout for elements on a page, so think rows and columns. It's simple, yet flexible.
Creating an APL Document with the Authoring Tool
Below is a document that will display static content, in our case Hello World. We'll get into a more useful example in future guides.
a) Go to the Alexa Developer Console
b) Create a new skill
c) Click on Interfaces on the left menu
d) Toggle the switch for Alexa Presentation Language
e) Click on Displays on the left side menu below Interfaces
f) Select Start from scratch option
g) Click on mainTemplate
on the lower left. This is the initial root element that needs to exist and cannot be deleted.
payload
is how you will reference datasources that are passed in. We'll see this later in the tutorial.
Below is the current JSON representation:
{
"type": "APL",
"version": "1.0",
"import": [],
"resources": [],
"styles": {},
"layouts": {},
"mainTemplate": {
"parameters": [
"payload"
],
"items": []
}
}
e) Add a Container
by clicking the +
Below is the current JSON representation:
{
"type": "APL",
"version": "1.0",
"import": [],
"resources": [],
"styles": {},
"layouts": {},
"mainTemplate": {
"parameters": [
"payload"
],
"items": [
{
"type": "Container"
}
]
}
}
f) Add a Text
element
- Click on the
Container
in the Layout section and click + similarly to the previous step - Next to the Layout section you will find the properties that can be set on the
Text
element. In this case, we want to settext
to Hello World. You will notice that Hello World now shows up in the display simulator.
Below is the current JSON representation:
{
"type": "APL",
"version": "1.0",
"import": [],
"resources": [],
"styles": {},
"layouts": {},
"mainTemplate": {
"parameters": [
"payload"
],
"items": [
{
"type": "Container",
"items": [
{
"type": "Text",
"text": "Hello World"
}
]
}
]
}
}
Render the APL Document to Displays
a) Click the toggle switch in the lower panel (depending on your configuration) to change the layout interface from graphical to JSON. There you could find the APL Document JSON.
b) Copy the APL Document JSON. The document should be as follows:
{
"type": "APL",
"version": "1.0",
"theme": "dark",
"import": [],
"resources": [],
"styles": {},
"layouts": {},
"mainTemplate": {
"parameters": [
"payload"
],
"items": [
{
"type": "Container",
"item": [
{
"type": "Text",
"text": "Hello World"
}
]
}
]
}
}
c) Go to where index.js
is located and in the same folder create a file named launch.json .
d) Paste the copied JSON generated from the APL Authoring Tool in launch.json.
e) Open index.js
. Then in the LaunchIntentHandler
we will add the directive to render our Hello World APL document.
Import the launch.json
and in the handle
function, let's add a directive for rendering the APL document by updating the return value to the following:
const launch = require('launch.json') // import the launch APL document
const LaunchRequestHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
},
handle(handlerInput) {
const speechText = "Hello World!";
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
.addDirective({ // Add APL RenderDocument directive
type: 'Alexa.Presentation.APL.RenderDocument',
version: '1.0',
document: launch, // This references launch.json
datasources: {}
})
}
}
Passing Datasources to the APL Document
APL documents also support data sources, so let's refactor our code to pass in the text value instead of a hard-coded value. To do so we will need to update our Lambda code and APL document.
Updating the Node.js code
To update our Node.js code we need create a speechText
object that is passed to datasources
in the following this format:
datasources: {
speechTextObject: {
type: "Text",
value: speechText
}
}
Then update the handle
function to the following:
handle(handlerInput) {
const speechText = "Hello World!";
// add directive to render APL document
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
.addDirective({
type: 'Alexa.Presentation.APL.RenderDocument',
version: '1.0',
document: launch, // This references launch.json
datasources: { // This is where datasources could be added
speechTextObject: { // Pass speechTextObject as a datasource
type: "Text",
value: speechText
}
}
})
}
Updating the APL Document
To access the speechText
that was passed into datasources
from Node.js we need to reference payload
from
"mainTemplate": {
"parameters": [
"payload"
]
}
Note: This variable name could be named something else and does not have to bepayload
, but it would have to be listed underparameters
.
Let's update the hard-coded Hello World string with the speechText
value we passed in from Node.js by updating the Container
item
with the following:
{
"type": "Container",
"item": {
"type": "Text",
"text": "${payload.speechTextObject.value}"
}
}
The"${}"
is how you do variable interpolation in APL, so in our case"${payload.speechTextObject.value}"
is Hello World.
{
"type": "Text",
"value": "${speechTextObject.text}"
}
Putting everything together, the new APL document should look like this:
{
"type": "APL",
"version": "1.0",
"theme": "dark",
"import": [],
"resources": [],
"styles": {},
"layouts": {},
"mainTemplate": {
"parameters": [
"payload"
],
"items": [
{
"type": "Container",
"item": ["${payload.speechTextObject}"]
}
]
}
}
Verifying the Device Has a Display
Not all Alexa supported devices have a display, so we need to check if the device has a display and conditionally add an APL Render Document Skill Directive.
We will add the supportsAPL
function to our backend right after our const Alexa = require('ask-sdk-core');
:
const supportsAPL = function (handlerInput) {
const supportedInterfaces = handlerInput.requestEnvelope.context.System.device.supportedInterfaces;
const aplInterface = supportedInterfaces['Alexa.Presentation.APL'];
return aplInterface !== null && aplInterface !== undefined;
}
Then we are going to refactor the code in the handle
function inside handleHelloWorldIntent
to the following:
handle(handlerInput) {
const speechText = "Hello World!";
// create a new response with the speech
var response = handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
// Check if the device supports APL before adding the directive to render the APL document
if(supportsAPL(handlerInput)) {
return response.addDirective({
type: 'Alexa.Presentation.APL.RenderDocument',
version: '1.0',
document: launch,
datasources: {
speechTextObject: {
type: "Text",
value: speechText
}
}
})
}
return response
}
Summary
Now we have a skill that integrates with multimodal displays using the Alexa Presentation Language (APL). This is a simple Hello World example that went through the basics mechanics of integrating APL with skills.
In this guide, we went through:
- How to build APL documents with the Authoring Tool or raw JSON
- Adding an APL document to our skill to display visuals
- Passing data sources to the APL documents
What's next?
Currently it only renders Hello World, but there are more things that could be added to create voice rich interactive skills.
Resources
- Alexa Presentation Language (APL)
- Alexa Design Guide
- Visual Experiences
- APL Render Document Skill Directive
- Nodejs Hello World Skill
For more tips on skill building follow me on Twitter @itspanw.