Command Palette

Search for a command to run...

State Machine Specification

Complete technical specification for the dotLottie state machine file format

State Machine Specification

Version

Current specification version: 1.0

Overview

A state machine is a JSON file embedded in a .lottie archive that makes an animation interactive. Rather than playing a fixed sequence, an animation with a state machine can respond to user input — clicks, pointer events, custom signals — and drive different playback states in response.

The state machine logic is self-contained inside the .lottie file. Behaviour is portable across platforms without rewriting per-platform interaction code.

Architecture

A state machine is built from four cooperating parts:

PartDescription
InputsNamed typed variables (numeric, string, boolean) and named event signals. Guards read inputs; actions write them.
InteractionsDeclarations that bind user gestures or animation lifecycle events to actions. Interactions fire actions, which update inputs.
StatesNamed playback configurations. The machine is always in exactly one state at a time.
TransitionsRules attached to a state that move to another state when guards pass. Guards check input values.

The typical flow: an interaction fires → executes actions that update inputs → the state machine re-evaluates transitions (which check guards on inputs) → a matching transition fires → the machine moves to the new state.

File Location

State machine files are stored in the s/ directory and registered in manifest.json:

{
  "version": "2",
  "animations": [{ "id": "button" }],
  "stateMachines": [
    { "id": "button-states", "name": "Button States" }
  ],
  "initial": { "stateMachine": "button-states" }
}

The id must match the filename (without .json). For example, "id": "button-states" references s/button-states.json.

Schema

Top-Level Structure

A state machine file is a JSON object with the following top-level fields:

FieldRequiredTypeDescription
initialYesStringName of the state to enter when the state machine starts.
statesYesState[]List of state definitions. Must contain at least one.
interactionsNoInteraction[]User or lifecycle events that trigger actions.
inputsNoInput[]Typed variables available to guards and actions.

States

A state defines the animation playback configuration while the machine is in that state.

PlaybackState

Controls the playback of a specific animation.

FieldRequiredTypeDefaultDescription
nameYesStringUnique identifier for this state, referenced in transitions and interactions.
typeYes"PlaybackState"Declares this as a playback-controlling state.
animationYesStringID of the animation to play, corresponding to a file in a/.
autoplayNoBooleanWhen true, animation starts playing immediately on entry.
loopNoBooleanWhen true, animation loops continuously.
loopCountNoInteger (≥ 1)Total loop iterations when loop is true. Omit for infinite. Ignored when loop is false.
modeNo"Forward" | "Reverse" | "Bounce" | "ReverseBounce"Playback direction. Bounce plays forward then reverse; ReverseBounce plays reverse then forward.
speedNoNumber1.0Playback speed multiplier. 2.0 is twice as fast; 0.5 is half speed.
segmentNoStringName of a marker or frame range to play within the animation. Missing markers fall back to the full animation range.
backgroundColorNoNumberBackground color as a hexadecimal number (e.g., 0xFFFFFF for white).
finalNoBooleanfalseWhen true, this state cannot transition to other states, ending the machine.
entryActionsNoAction[][]Actions executed when entering this state.
exitActionsNoAction[][]Actions executed when leaving this state.
transitionsYesTransition[]Rules defining when and how to leave this state.
{
  "name": "idle",
  "type": "PlaybackState",
  "animation": "button",
  "autoplay": true,
  "loop": true,
  "transitions": [
    {
      "type": "Transition",
      "toState": "active",
      "guards": [
        { "type": "Boolean", "inputName": "isActive", "conditionType": "Equal", "compareTo": true }
      ]
    }
  ]
}

GlobalState

A special state whose transitions are checked first, regardless of the current state. Use it to define global behaviors — like a universal reset or escape route — without duplicating transitions across every other state.

FieldRequiredTypeDescription
nameYesStringUnique identifier for this global state.
typeYes"GlobalState"Declares this as a global override state.
entryActionsNoAction[]Actions executed when entering this state.
exitActionsNoAction[]Actions executed when leaving this state.
transitionsYesTransition[]Transitions evaluated before all regular state transitions.

Transitions

Transitions define when and how to move from one state to another.

Evaluation order:

  1. GlobalState transitions are checked first

  2. Then the current state's transitions, in declaration order

  3. The first transition whose guards all pass fires

  4. Guardless transitions act as fallbacks, checked after guarded ones

Transition

An immediate state change.

FieldRequiredTypeDescription
typeYes"Transition"Declares an immediate state change.
toStateYesStringName of the target state.
guardsNoGuard[]Conditions that must all pass for this transition to fire (AND logic).
{
  "type": "Transition",
  "toState": "hover",
  "guards": [
    { "type": "Boolean", "inputName": "isHovered", "conditionType": "Equal", "compareTo": true }
  ]
}

Tweened Transition

A gradual state change with an animation over a specified duration.

FieldRequiredTypeDescription
typeYes"Tweened"Declares an animated transition.
toStateYesStringName of the target state.
guardsNoGuard[]Conditions that must all pass (AND logic).
durationYesNumberDuration of the transition animation in seconds.
easingYes[x1, y1, x2, y2]Cubic Bézier curve control points defining the easing function.

During a tween, input changes that would trigger further transitions are ignored. Entry actions on the target state run only after the tween completes.

{
  "type": "Tweened",
  "toState": "expanded",
  "duration": 0.3,
  "easing": [0.4, 0, 0.2, 1]
}

Guards

Guards are conditions evaluated against input values. All guards on a transition must pass for it to fire.

Numeric Guard

Compares a numeric input against a value.

FieldRequiredTypeDescription
typeYes"Numeric"
inputNameYesStringName of a numeric input to evaluate.
conditionTypeYes"Equal" | "NotEqual" | "GreaterThan" | "GreaterThanOrEqual" | "LessThan" | "LessThanOrEqual"Comparison to perform.
compareToYesNumber | StringValue to compare against. Use a $-prefixed string to reference another numeric input (e.g., "$maxCount").
{ "type": "Numeric", "inputName": "rating", "conditionType": "GreaterThan", "compareTo": 3 }

String Guard

Compares a string input against a value.

FieldRequiredTypeDescription
typeYes"String"
inputNameYesStringName of a string input to evaluate.
conditionTypeYes"Equal" | "NotEqual"Comparison to perform.
compareToYesStringValue to compare against. Use a $-prefixed string to reference another string input.
{ "type": "String", "inputName": "theme", "conditionType": "Equal", "compareTo": "dark" }

Boolean Guard

Compares a boolean input against a value.

FieldRequiredTypeDescription
typeYes"Boolean"
inputNameYesStringName of a boolean input to evaluate.
conditionTypeYes"Equal" | "NotEqual"Comparison to perform.
compareToYesBoolean | StringValue to compare against. Use a $-prefixed string to reference another boolean input.
{ "type": "Boolean", "inputName": "isEnabled", "conditionType": "Equal", "compareTo": true }

Event Guard

Passes when a named event input has been fired. Events are edge-triggered — consumed after a successful evaluation, not re-used.

FieldRequiredTypeDescription
typeYes"Event"
inputNameYesStringName of the event input to monitor.
{ "type": "Event", "inputName": "onSubmit" }

Actions

Actions perform operations when triggered by interactions or state entry/exit. Actions that modify inputs may cause transitions to re-evaluate.

Input Modification Actions

TypeDescriptionFields
SetNumericSets a numeric input to a value.inputName (String), value (Number)
SetStringSets a string input to a value.inputName (String), value (String)
SetBooleanSets a boolean input to a value.inputName (String), value (Boolean)
ToggleFlips a boolean input.inputName (String)
IncrementAdds to a numeric input.inputName (String), value (Number or $-prefixed input name, default 1)
DecrementSubtracts from a numeric input.inputName (String), value (Number or $-prefixed input name, default 1)
FireFires a named event input, triggering any guards that watch it.inputName (String)
ResetRestores an input to its initial value.inputName (String)
{ "type": "Increment", "inputName": "rating", "value": 1 }
{ "type": "Toggle", "inputName": "isActive" }
{ "type": "Fire", "inputName": "onSubmit" }

Playback Actions

TypeDescriptionFields
SetFrameJumps the animation to a specific frame.value (Number, or $-prefixed numeric input name)
SetProgressSets the animation progress (0–1).value (Number, or $-prefixed numeric input name)

SetFrame and SetProgress operate on the currently active animation.

{ "type": "SetProgress", "value": 0.5 }

Styling and Navigation Actions

TypeDescriptionFields
SetThemeApplies a dotLottie theme by ID.value (String, or $-prefixed string input name)
OpenUrlOpens a URL in a browser window.url (String, or $-prefixed string input name), target ("_blank", "_self", "_parent", "_top")
FireCustomEventEmits a custom event string to the host application.value (String)
{ "type": "SetTheme", "value": "dark-theme" }
{ "type": "OpenUrl", "url": "https://example.com", "target": "_blank" }

Interactions

Interactions bind user gestures or animation lifecycle events to a list of actions. They are declared at the top level and fire independently of the current state.

Pointer Interactions

All pointer interactions accept an optional layerName to restrict triggering to a specific named layer in the animation. Layer name matching is exact and case-sensitive.

TypeDescription
PointerEnterFires when a pointer enters the animation (or layer) bounds.
PointerExitFires when a pointer exits the animation (or layer) bounds.
PointerDownFires when a pointer is pressed within the bounds.
PointerUpFires when a pointer is released within the bounds.
PointerMoveFires when a pointer moves over the bounds.
ClickFires on a complete click or tap gesture.
FieldRequiredTypeDescription
typeYesStringOne of the interaction types above.
layerNameNoStringRestrict to a specific named layer.
actionsYesAction[]Actions to execute when this interaction fires.
{
  "type": "Click",
  "actions": [
    { "type": "Toggle", "inputName": "isActive" }
  ]
}
{
  "type": "PointerEnter",
  "layerName": "button-layer",
  "actions": [
    { "type": "SetBoolean", "inputName": "isHovered", "value": true }
  ]
}

Animation Lifecycle Interactions

TypeDescription
OnCompleteFires when the specified state's animation finishes its final playback (not fired for infinite loops).
OnLoopCompleteFires after each loop iteration of the specified state's animation completes.
FieldRequiredTypeDescription
typeYes"OnComplete" | "OnLoopComplete"
stateNameYesStringName of the state to monitor.
actionsYesAction[]Actions to execute when triggered.
{
  "type": "OnComplete",
  "stateName": "loading",
  "actions": [
    { "type": "Fire", "inputName": "onLoadComplete" }
  ]
}

Inputs

Inputs are typed variables declared at the top level. Guards read them; actions write them.

Numeric Input

FieldRequiredTypeDescription
typeYes"Numeric"
nameYesStringUnique identifier, referenced in guards and actions.
valueYesNumberInitial value.
{ "type": "Numeric", "name": "rating", "value": 0 }

String Input

FieldRequiredTypeDescription
typeYes"String"
nameYesStringUnique identifier.
valueYesStringInitial value.
{ "type": "String", "name": "theme", "value": "light" }

Boolean Input

FieldRequiredTypeDescription
typeYes"Boolean"
nameYesStringUnique identifier.
valueYesBooleanInitial value.
{ "type": "Boolean", "name": "isActive", "value": false }

Event Input

Represents a named signal with no persistent value. Fired by Fire actions; consumed by Event guards after evaluation.

FieldRequiredTypeDescription
typeYes"Event"
nameYesStringUnique identifier.
{ "type": "Event", "name": "onSubmit" }

Validation Rules

  1. initial must reference a state name that exists in the states array

  2. states must contain at least one entry

  3. Each state name must be unique within the state machine

  4. Each transition toState must reference an existing state name

  5. Each input name must be unique within the state machine

  6. Guards and actions reference inputs by name — mismatched names are invalid

  7. States with final: true should have no outgoing transitions


Complete Examples

Example 1: Toggle Button

A button that switches between two states on click, using a boolean input and a GlobalState to handle the toggle from any state.

{
  "initial": "idle",
  "states": [
    {
      "name": "idle",
      "type": "PlaybackState",
      "animation": "button",
      "autoplay": true,
      "loop": true,
      "transitions": [
        {
          "type": "Transition",
          "toState": "active",
          "guards": [
            { "type": "Boolean", "inputName": "isActive", "conditionType": "Equal", "compareTo": true }
          ]
        }
      ]
    },
    {
      "name": "active",
      "type": "PlaybackState",
      "animation": "button",
      "autoplay": true,
      "loop": false,
      "entryActions": [
        { "type": "SetTheme", "value": "active-theme" }
      ],
      "transitions": [
        {
          "type": "Transition",
          "toState": "idle",
          "guards": [
            { "type": "Boolean", "inputName": "isActive", "conditionType": "Equal", "compareTo": false }
          ]
        }
      ]
    }
  ],
  "interactions": [
    {
      "type": "Click",
      "actions": [
        { "type": "Toggle", "inputName": "isActive" }
      ]
    }
  ],
  "inputs": [
    { "type": "Boolean", "name": "isActive", "value": false }
  ]
}

Example 2: Star Rating

A 5-star rating selector. Pointer move updates a numeric input; the state machine plays the corresponding animation segment. Demonstrates numeric guards, SetNumeric actions, and layer-scoped pointer interactions.

{
  "initial": "rating",
  "states": [
    {
      "name": "rating",
      "type": "PlaybackState",
      "animation": "stars",
      "autoplay": false,
      "transitions": [
        {
          "type": "Transition",
          "toState": "rating",
          "guards": [
            { "type": "Numeric", "inputName": "frame", "conditionType": "GreaterThanOrEqual", "compareTo": 0 }
          ]
        }
      ]
    }
  ],
  "interactions": [
    {
      "type": "PointerEnter",
      "layerName": "star-1",
      "actions": [{ "type": "SetNumeric", "inputName": "frame", "value": 20 }]
    },
    {
      "type": "PointerEnter",
      "layerName": "star-2",
      "actions": [{ "type": "SetNumeric", "inputName": "frame", "value": 40 }]
    },
    {
      "type": "PointerEnter",
      "layerName": "star-3",
      "actions": [{ "type": "SetNumeric", "inputName": "frame", "value": 60 }]
    },
    {
      "type": "PointerEnter",
      "layerName": "star-4",
      "actions": [{ "type": "SetNumeric", "inputName": "frame", "value": 80 }]
    },
    {
      "type": "PointerEnter",
      "layerName": "star-5",
      "actions": [{ "type": "SetNumeric", "inputName": "frame", "value": 100 }]
    },
    {
      "type": "Click",
      "actions": [{ "type": "Fire", "inputName": "onRatingSelected" }]
    }
  ],
  "inputs": [
    { "type": "Numeric", "name": "frame", "value": 0 },
    { "type": "Event", "name": "onRatingSelected" }
  ]
}

References

Last updated: April 10, 2026 at 9:12 AMEdit this page