APL Commands (APL 2022.1 to 2022.2)


(This is not the most recent version of APL. Use the Other Versions option to see the documentation for the most recent version of APL)

Commands are messages that change the visual or audio presentation of the content on the screen. You send commands to the device in response to a spoken utterance with the Alexa.Presentation.APL.ExecuteCommands directive. You use event handlers in your APL document to trigger commands directly, such as the response to a button press.

For specific commands, see APL Standard Commands.

Commands and screen actions

Commands support the following types of actions on the scene:

  • Navigate within a scene
  • Change a component within an existing scene
  • Update an input control to reflect a new state
  • Change the visibility on an existing component
  • Play or pause a video clip within the existing scene
  • Speech
    • Read the audio content of a single component
    • Read the audio content from more than one components
  • Send commands to an APL extension

Command summary

The following sections describe the algorithm for running a command started by either an event handler or an externally-generated command.

Assume an array of commands to run in a series. The commands run in either normal mode or fast mode.

Normal mode evaluation

A command that runs in normal mode must have a named sequencer.

By default, a command runs on the MAIN sequencer except in the following scenarios:

  • The sequencer property of the command contains a value.
  • The command is a subcommand of a Sequential or Parallel and the Sequential or Parallel command has a different sequencer.

For each command in the array in turn, Alexa performs the following steps:

  1. Evaluate the when property of the command. If when evaluates to false, skip the command and continue to the next command in the array.

  2. Check for a value in the delay property of the command. If delay is greater than zero, pause for the specified duration in milliseconds.

  3. Check for a value in the sequencer property of the command. When sequencer has a value that's different from the current sequencer, hand this command off to the specified sequencer and continue to the next command in the array.

  4. Validate the type property of the command. When type contains an unrecognized value, skip the command and continue to the next command in the array.

    Valid type values include the following:

    • A built-in command, such as SendEvent. Run the command if all required command properties have values. Some commands return immediately. Other commands pause the sequencer until the command finishes running.
    • A user-defined command. User-defined commands inflate into an array of commands. Run those commands following the normal mode evaluation rules and continue when all the commands complete.
    • An extension command. Run the command. Some extension commands return immediately. Other commands pause the sequencer until the command finishes running.

Fast mode evaluation

A command running in fast mode doesn't have a named sequencer. For each command in the array in turn, do the following:

  1. Evaluate the when property of the command. If when evaluates to false, skip the command and continue to the next command in the array.

  2. Check for a value in the sequencer property of the command. When sequencer has a value, hand this command off to the appropriate sequencer and continue to the next command in the array. The handed-off command now runs in normal mode instead of fast mode.

  3. Validate the type property of the command. When type contains an unrecognized value, skip the command and continue to the next command in the array.

    Valid type values include the following:

    • A built-in command that supports fast mode, such as SetValue. Run the command if all required command properties contain values. For a list of commands that support fast mode, see the tables in Fast mode commands.
    • A built-in command that doesn't support fast mode, such as SendEvent. Skip the command.
    • A user-defined command. User-defined commands inflate into an array of commands. Run those commands following the fast mode evaluation rules and continue when all the commands complete.
    • An extension command that supports fast mode. Run the command.
    • An extension command that doesn't support fast mode. Skip the command.

Command evaluation

The individual properties of a command support data-binding. The data-binding context for a command includes the context in which the command is defined, extended by an event property containing information about the circumstances that triggered the command and the component that was the target of the command.

Event context

Commands evaluate in their source data-binding context.

A command sent to the device by the ExecuteCommands directive evaluates in the top-level data-binding context. The data-binding context contains the viewport and environment properties along with any named resources.

A command issued in response to an APL event (such as a screen tap) evaluates in a local data-binding context where the command is defined.

For example, consider the following TouchWrapper example.

{
  "type": "TouchWrapper",
  "bind": [
    "name": "myRandomData",
    "value": 24.3
  ],
  "onPress": {
    "type": "SendEvent",
    "arguments": [ "The value is ${myRandomData}" ]
  }
}

When the user taps the TouchWrapper, the device sends the skill a UserEvent. The arguments array in this request contains the string "The value is 24.3".

Event property

Evaluating a command extends the source data-binding context with event data. Access this event data within the event property.

The event property has two sub-properties: event.source and event.target.

  • The event.source property contains system-provided information about the component that caused the event
  • The event.target property contains system-provided information about the component that receives the event (if applicable).

Not all commands have an event.target property. For example, the SendEvent command doesn't have an event.target property. All commands do have a event.source property. The details with event.source can vary and might not represent a component. For example, the source might come from an extension rather than a component.

The event property added to the data-binding context has the following form.

"event": {
  "source": {                   // Always exists
    "type": "COMPONENT_TYPE",     // The type of the component or "Document"
    "handler": "EVENT_HANDLER",   // The name of the event handler (see command notes)
    "id": "SOURCE_ID",            // The ID of the source component
    "uid": "SOURCE_UID",          // The UID of the source component
    ...                         // Additional source component properties
  },
  "target": {                   // Only exists when the command has a component target
    "type": "COMPONENT_TYPE",     // The type of the target component
    "id": "TARGET_ID",            // The ID of the target component
    "uid": "TARGET_UID",          // The UID of the target component
    ...                         // Additional target component properties
  },
  ...   // Command-specific properties
}

The event.source property has a handler property, and the event.target property doesn't.

The following table lists the standard event.source and event.target properties that different components report. For complete documentation on the properties for a given component, see the documentation for that component.

Property Type Description Reported By

bind

Map

Data-binding context

Component

checked

Boolean

Checked state

Component

color

Color

Current color

Text

currentTime

Integer

Current playback position

Video

disabled

Boolean

Disabled state

Component

duration

Integer

Duration of the current video (ms)

Video

ended

Boolean

True if the video has ended

Video

focused

Boolean

Focused state

Component

height

Number

Height in dp

Component

id

String

Component id

Component

layoutDirection

String

Current layoutDirection, LTR or RTL

Component

muted

Boolean

Current muted state of video

Video

opacity

Number

Local opacity (not cumulative)

Component

page

Integer

Current displayed page

Pager

paused

Boolean

True if the video has paused

Video

position

Number

Percentage of scrolled distance

pressed

Boolean

Pressed state

Component

text

String

Displayed text

Text

trackCount

Integer

Number of tracks

Video

trackIndex

Integer

Index of the current track

Video

type

String

Component type (for example, "Frame")

Component

uid

String

Runtime-generated unique id

Component

url

String

Source URL

width

Number

Width in dp

Component

The bind property provides access to the data-binding context of the component. The following example illustrates how you access a bound value. The component with the ID MyText is the target of the SetValue command. Therefore, the event.target.bind property contains the data bound to the MyText component and the command can use that value.

[
  {
    "type": "TouchWrapper",
    "onPress": {
      "type": "SetValue",
      "componentId": "MyText",
      "property": "text",
      "value": "The word of the day is ${event.target.bind.WordOfTheDay}"
    },
    "items": [
      {
        "type": "Text",
        "text": "Click me!"
      }
    ]
  },
  {
    "type": "Text",
    "bind": [
      {
        "name": "WordOfTheDay",
        "value": "Bear"
      }
    ],
    "id": "MyText"
  }
]

event.source

The event.source property of the event contains meta-information about the circumstances that triggered the event. The APL runtime generates event.source. You can use this information in your document. The event.source property contains all standard properties along with the following properties:

Property Type Description
handler String The name of the event handler that initiated this message. For example, Press, Checked.
value Any The value of the component that initiated this message.

The event.source.value property depends on the component. For example, for a TouchWrapper, event.source.value contains the checked state of the component. For a ScrollView, the property contains the scroll position. Refer to the individual component definitions for the specific event.source.value property provided.

For backwards-compatibility with older versions of APL, event.source property also contains a source sub-property equal to the type property. Therefore, ${event.source.source == event.source.type}. The event.source.source property is deprecated. Avoid using this property.

event.target

The event.target property provides state information about the component receiving the event. The values in the target depend on the specific component. Refer to the individual component definitions for the specific target properties you can expect.

For backwards-compatibility with older versions of APL the target property of the event also reports an source sub-property equal to the url property for the Image, VectorGraphic, and Video components. Therefore, ${event.target.source == event.target.url}. The event.target.source property is deprecated. Avoid using this property.

Evaluation notes

  • Most commands take a componentId as a target. You can omit componentId for a command that targets itself.
  • When you use the ExecuteCommands directive to send a command to the device, you must always specify componentId.
  • When the command includes the componentId property, the command searches through all the components starting at the root of the tree hierarchy in a depth-first search manner and targets the first component with an identifier that matches the value. To guarantee that the command targets the correct component, do one of the following:
    • Assign a unique value to the target component with the id property and set componentId for the command to that value.
    • Use the system-generated uid property value provided in the visual context.
  • Command data-binding expressions evaluate when the command runs, not when the command is defined. For example, an ExecuteCommands directive can contain data-bound expressions that refer to the global data-binding context. These expressions are evaluated when the command runs on the device, not when the command is constructed in the cloud.
  • Event handlers and the ExecuteCommands directive take an array of commands to run. This array works like a Sequential command with a repeatCount of 0.

Command sequencing

The APL runtime environment runs the commands. User and environmental actions trigger the commands to run. The Parallel and Sequential commands also trigger a set of commands to run. The command sequencing rules determine the behavior when multiple commands run at the same time. These rules prevent commands from conflicting with each other. You can partially control the sequencing behavior based on how you define your commands.

Note the following terms:

  • Resource – Something used by a command that other commands can't share. For example, a command that speaks text uses the "speech" resource. A command that acts on a specific component over time (such as AnimateItem) considers that component a resource.
  • Normal mode – The standard method of running commands. Most commands run in normal mode. Normal mode commands might take time to run.
  • Fast mode – An alternative method of running commands, used when it would be inappropriate to run commands that take time to run. When a command runs in fast mode, the command ignores all delays and skips commands that take time to run. Event handlers, such as onScroll, use fast mode. These event handlers might fire multiple times per second.
  • Sequencer – A named entity that can run a single command at a time. Each normal mode command has a named sequencer. You set the sequencer property to specify the sequencer.
  • Subcommand – A command contained within another command. The Parallel and Sequential commands can each contain subcommands. A hierarchy of subcommands is also called a command tree.

Command sequencing rules

The APL runtime uses the following rules to sequence commands.

  1. Every normal mode command runs on a single sequencer. If that sequencer is already running a command, the runtime stops the running command and starts the new command.
  2. Commands running in fast mode don't use a sequencer.
  3. Any command that explicitly specifies a sequencer runs in normal mode.
  4. A subcommand of a command runs on the same sequencer as its parent unless the subcommand explicitly specifies a sequencer. When the subcommand does specify a sequencer, the runtime hands off the command to the sequencer and marks the command as complete in the parent.
  5. The default sequencer is MAIN. When a normal mode command that isn't a subcommand doesn't explicitly name a sequencer, the command runs on the MAIN sequencer. Normal mode subcommands follow rule 4.
  6. Any physical user interaction with the device, such as touching the screen or typing on a keyboard, stops any command running on the MAIN sequencer. This rule doesn't apply to voice interactions. When the user speaks the wake word to "barge in" during a command sequence, the MAIN sequencer continues and doesn't stop.
  7. When a command starts running, if the command requires a resource that's in use by a command that's already running, the runtime stops the running command.
  8. When a configuration change handler runs, any commands running in the previous configuration stop.

For example, the user touches a button the screen. This interaction starts a series of commands on the MAIN sequencer to do the following:

  1. Animate a change on the screen.
  2. Scroll down a list to a specific location.
  3. Speak an item.

The user can interrupt this series of commands at any time by touching the screen again. When the user touches the screen, the MAIN sequencer stops the currently running command.

To override the default command sequencing behavior, specify a sequencer. Use this technique to run commands in parallel. Some scenarios in which you would change the sequencing behavior:

  1. You want an event handler that runs in fast mode by default to run a normal mode command. For example, you want the onScroll event handler of a ScrollView to trigger a SendEvent command. Normally, the onScroll handler ignores normal mode commands. When you provide a named sequencer on SendEvent, the onScroll handler runs the command in normal mode, so the SendEvent runs.
  2. You want to override normal user interaction behavior and continue to run a command even when the user touches the screen. For example, you use an AnimateItem command to animate an on-screen component in a children's game where touching the screen shouldn't stop the animation.
  3. When multiple components animations are triggered to show new components appearing or old component disappearing.

Resources

Each command might use one or more system resources when running. As defined in the sequencer rules, only a single command using a given resource can run at a time. When a new command that requires that resource starts running, the old command stops.

The following is a list of resources and which commands use those resources:

The SpeakItem and SpeakList can use multiple resources.

Normal mode

The APL runtime runs a command in normal mode in the following scenarios:

  • When the document initially loads (Document onMount and Component onMount).
  • The user touches or selects a TouchWrapper (onPress).
  • The user swipes on a Pager and changes the displayed page (onPageChanged)
  • A Video component ends playback of the video or changes the track (onEnd, onPause, onPlay, onTrackUpdate).
  • A new set of commands arrives from an external source such as the ExecuteCommands directive or an extension event handler.
  • The user presses or releases a key on the keyboard (handleKeyDown and handleKeyUp).

By default all normal mode commands use the MAIN sequencer.

In the following example the onPress event handler has an array of commands. This array is treated as a sequential array. Therefore, SpeakItem runs first. After the speech finishes, the Scroll command runs. When Scroll finishes, the SendEvent command runs and sends a UserEvent to your skill. All of these commands run on the MAIN sequencer.

{
  "type": "TouchWrapper",
  "items": {
    "type": "Text",
    "id": "myText",
    "speech": "VALUE TO SPEAK"
  },
  "onPress": [
    {
      "type": "SpeakItem",
      "componentId": "myText"
    },
    {
      "type": "Scroll",
      "componentId": "myScrollRegion",
      "distance": 2
    },
    {
      "type": "SendEvent",
      "arguments": [
        "The button was pushed and spoken have I"
      ]
    }
  ]
}

In the previous example, the component running the Scroll command generates scrolling events which might invoke commands defined in the component's onScroll event handler. These commands run in fast mode and therefore don't affect the currently running sequence of commands.

If the user taps the TouchWrapper multiple times, the command sequence ends and restarts with each tap. Although you could set the onPress handler to also disable the TouchWrapper, this doesn't solve the problem. Any user tap on the screen stops the commands on the MAIN sequencer.

Instead, to guarantee that the sequence of commands completes, assign a different sequencer to the commands. The following example both disables the TouchWrapper so that the user can't restart the sequence until it finishes and uses a different sequencer so that touching the screen doesn't cancel the commands.

{
  "type": "TouchWrapper",
  "items": {
    "type": "Text",
    "id": "myText",
    "speech": "VALUE TO SPEAK"
  },
  "onPress": [
    {
      "type": "Sequential",
      "sequencer": "MySequencer",
      "commands": [
        {
          "type": "SetValue",
          "property": "disabled",
          "value": true
        },
        {
          "type": "SpeakItem",
          "componentId": "myText"
        },
        {
          "type": "Scroll",
          "componentId": "myScrollRegion",
          "distance": 2
        },
        {
          "type": "SendEvent",
          "arguments": [
            "The button was pushed and spoken have I"
          ]
        }
      ],
      "finally": {
        "type": "SetValue",
        "property": "disabled",
        "value": false
      }
    }
  ]
}

Because the custom "MySequencer" isn't the MAIN sequencer, another tap on the screen doesn't cancel the commands. Note that this example runs the final SetValue command in the finally block. If the Sequential stops for some reason, the commands specified in finally still run to re-enable the TouchWrapper.

Note that the previous example works for a series of commands because all the commands are subcommands of the Sequential. According to the sequencer rules, subcommands run on the same sequencer as the parent unless they specify their own.

In contrast, the following array of commands assigned to onPress doesn't work. When the user taps this TouchWrapper, nothing happens:

{
  "type": "TouchWrapper",
  "items": {
    "type": "Text",
    "id": "myText",
    "speech": "VALUE TO SPEAK"
  },
  "onPress": [
    {
      "type": "SetValue",
      "property": "disabled",
      "value": true,
      "sequencer": "BadIdea"
    },
    {
      "type": "SpeakItem",
      "componentId": "myText",
      "sequencer": "BadIdea"
    },
    {
      "type": "Scroll",
      "componentId": "myScrollRegion",
      "distance": 2,
      "sequencer": "BadIdea"
    },
    {
      "type": "SendEvent",
      "arguments": [
        "The button was pushed and spoken have I"
      ],
      "sequencer": "BadIdea"
    },
    {
      "type": "SetValue",
      "property": "disabled",
      "value": false,
      "sequencer": "BadIdea"
    }
  ]
}

This example fails because the onPress command runs on the MAIN sequencer by default. Specifying a different sequencer causes the handler to hand off the command to the other sequencer and then run the next command in the array. When a sequencer receives a new command, it stops any existing command and replaces the existing command with the new one.

The sequence of events for the failed example would be the following.

  1. The onPress handler hands off SetValue to the BadIdea sequencer.
  2. The onPress handler moves on to SpeakItem and immediately hands off SpeakItem to BadIdea.
  3. The BadIdea sequencer cancels SetValue to start SpeakItem.
  4. The onPress handler hands off SendEvent to BadIdea.
  5. The BadIdea sequencer cancels SpeakItem to start SendEvent.
  6. The onPress handler hands off SetValue to BadIdea.
  7. The BadIdea sequencer cancels SendEvent to start SetValue.
  8. The SetValue command does successfully run, but has no effect because the TouchWrapper is already enabled.

You can also use a sequencer to toggle a command between running and not running. In the following example, the first TouchWrapper runs an AnimateItem command to animate moving the ball. The second TouchWrapper forces the animation to stop by sending a new command to the same sequencer to cancel the AnimateItem command.

{
  "type": "Container",
  "direction": "row",
  "items": [
    {
      "type": "Frame",
      "id": "Ball",
      "width": 100,
      "height": 100,
      "borderRadius": 50,
      "backgroundColor": "red"
    },
    {
      "type": "TouchWrapper",
      "items": {
        "type": "Text",
        "text": "Start Moving"
      },
      "onPress": [
        {
          "type": "AnimateItem",
          "easing": "ease-in-out",
          "duration": 1000,
          "repeatCount": 10000000,
          "repeateMode": "reverse",
          "componentId": "Ball",
          "sequencer": "BallSequencer",
          "value": {
            "property": "transform",
            "from": {
              "translateY": 0
            },
            "to": {
              "translateY": 300
            }
          }
        }
      ]
    },
    {
      "type": "TouchWrapper",
      "items": {
        "type": "Text",
        "text": "Stop Moving"
      },
      "onPress": [
        {
          "type": "Idle",
          "sequencer": "BallSequencer"
        }
      ]
    }
  ]
}

The onPress handler for the second TouchWrapper runs an Idle command on the BallSequencer to stop AnimateItem. The Idle command doesn't do anything, but it does stop any command currently running on the sequencer. Any other command sent to BallSequencer would have the same result.

Fast mode

Some event handlers can trigger many times per second. For example, the onScroll handler on a ScrollView runs every time the scroll position moves. At the same time, some commands might take a measurable amount of time to run. For example, scrolling a list on the screen or speaking text takes measurable time to run.

To avoid issues with long-running commands running from frequently-fired event handlers, those handlers run their commands in fast mode.

Any set of commands triggered from an event handler that runs at frame rate uses fast mode. All other command sequences run in normal mode. In fast mode, the handler ignores all delay settings in the commands and skips commands that take measurable time to run. The event handlers have the following behavior:

Event Handler Behavior
Actionable onBlur Fast mode
Actionable onFocus Fast mode
Actionable handleKeyDown Normal mode
Actionable handleKeyUP Normal mode
Component onCursorEnter Fast mode
Component onCursorExit Fast mode
Component onMount Normal mode
Document onMount Normal mode
Document onConfigChange Fast mode
Document onDisplayStateChange Fast mode
Pager onPageChanged Normal or fast mode
ScrollView onScroll Fast mode
Sequence onScroll Fast mode
Touchable onDown Fast mode
Touchable onMove Fast mode
Touchable onUp Fast mode
Touchable onPress Normal mode
Video onEnd Normal or fast mode
Video onPause Normal or fast mode
Video onPlay Normal or fast mode
Video onTimeUpdate Fast mode
Video onTrackUpdate Normal or fast mode

A one-time event that occurs due to user interaction, such as tapping a TouchWrapper, always runs in normal mode. Events such as scrolling run in fast mode. Events that can run in either mode can be triggered by a normal action (such as a video track ending) or by a command that was ultimately triggered by fast mode (such as a video pause from a scroll event). External commands and extension commands run in normal mode.

Each command documents its behavior in fast mode. The following table summarizes fast-mode behavior:

Command Fast mode behavior
AnimateItem Jumps to end state.
AutoPage Ignored
ClearFocus Runs
ControlMedia Ignored for command="play", run otherwise.
Finish Runs
Idle Ignored
OpenUrl Ignored
Parallel Runs
PlayMedia Ignored
Scroll Ignored
ScrollToComponent Ignored
ScrollToIndex Ignored
Select Runs
SendEvent Ignored
Sequential Runs
SetFocus Runs
SetPage Ignored
SetValue Runs
SpeakItem Ignored
SpeakList Ignored

When a command that normally runs in fast mode has a a value in the sequencer property, the command runs in normal mode on the specified sequencer instead. Use this feature to run normal mode commands from fast mode event handlers.

The following example checks the position of a scroll view whenever it moves and sends an event when the scroll position is at the top. It applies a rate limit by recording the time when it sends then event, so at least a second passes between events. Without the sequencer property, the SendEvent never runs.

{
  "type": "ScrollView",
  "bind": [
    {
      "name": "LastEventSentTime",
      "value": 0
    }
  ],
  "onScroll": [
    {
      "type": "Sequential",
      "when": "${event.source.value == 0 && utcTime > LastEventSentTime + 1000}",
      "commands": [
        {
          "type": "SetValue",
          "property": "LastEventSentTime",
          "value": "${utcTime}"
        },
        {
          "type": "SendEvent",
          "arguments": [
            "top of scroll view reached"
          ],
          "sequencer": "ScrollSender"
        }
      ]
    }
  ]
}

Command Trees

A command tree is the complete set of commands that run. Command trees occur because commands can nest within each other or because a command might invoke a new event handler. The following primitive commands have nested commands:

  • Parallel
  • Sequential
  • OpenURL

User-defined commands also support nested commands.

Primitive commands can also invoke event handlers. For example, the Scroll, ScrollToComponent, and SpeakList commands can all invoke the onScroll event handler.

Command trees can run to completion or stop before completion. A source starts running a command tree. The command tree then runs to completion unless it stops early. When a command tree stops, APL stops running all of the tree's commands immediately.

The following example illustrates a command tree for an ExecuteCommands directive that includes the Scroll, SpeakItem, and PlayVideo commands.

ExecuteCommand
  + Scroll (distance=-10000)           // Scroll to top
    + onScroll                         // Invokes multiple times as the view scrolls to the top
      + SetValue (name="opacity", value=event.source.value * 10) // Change opacity
  + SpeakItem (id)                     // Scroll item into view and run karaoke
    + onScroll                         // Invokes multiple times as the view scrolls
      + SetValue (name="opacity"....)
  + PlayVideo (synchronously)
    + onStart                          // Invokes once
    + onTrackUpdate                    // Invokes each time a new track is displayed
      + SetValue (name="progress"...)  // Update a progress bar display
    + onStop                           // Invokes once

This series of commands scrolls to the top of the screen, speaks one of the items, and then plays a video. If the user taps on the screen during playback, any running speech, scrolling, or video playback stops.

An individual command inside of a series of commands might specify a different sequencer. When command is reached, it's handed off to the appropriate sequencer and then the next command in the sequence runs immediately. Commands that don't have an explicit sequencer property run on the current sequencer. Commands only run on the next sequencer after command delay has been processed. For example, consider the following series of commands running on the main sequencer:

+ Sequential
  + AnimateItem A (delay=100, duration=1000)
  + AnimateItem B (delay=200, sequencer="other", duration=2000)
  + Parallel (delay=200)
    + AnimateItem C (duration=1000)
    + AnimateItem D (sequencer="other", duration=2000)
  + AnimateItem E (delay=100, duration=1000)

The following table summarizes the time line actions caused by this command tree.

Time Action
0 Sequential command starts
100 AnimateItem A starts
1100 AnimateItem A finishes
1300 AnimateItem B starts on sequencer other
1500 Parallel command starts
  AnimateItem C starts
  AnimateItem D starts on sequencer other. AnimateItem B stops.
2500 AnimateItem C finishes
  Parallel command finishes
2600 AnimateItem E starts
3500 AnimateItem D finishes on sequencer other
3600 AnimateItem E finishes
  Sequential command finishes

When a command tree stops, APL makes a number of assumptions to get the device to a consistent state:

  • Scrolling stops
  • Page turns are canceled and return to either the original page or the next page (whichever is closer)
  • Speaking immediately stops
  • Scene changes and structural changes to the layout "jump" to their final position.

Was this page helpful?

Last updated: Nov 28, 2023