Skip to content

dotLottie State Machine Guide

Abdelrahman Ashraf edited this page Sep 4, 2025 · 4 revisions

⚠️ Experimental Feature
Interactive animations and state machines are experimental features in dotLottie. APIs may change in future versions. Use with caution in production environments.

This guide shows you how to create interactive dotLottie animations using the @lottiefiles/dotlottie-web package. Learn to build rich, interactive experiences with user inputs, animation controls, and dynamic behaviors.

What is dotLottie Interactivity?

Interactive dotLottie animations use state machines to:

  • Control animation playback based on user interactions
  • Switch between different animations dynamically
  • Create complex interactive behaviors
  • Respond to events like clicks, hovers, and custom inputs
  • Build stateful animations that remember user interactions

📖 Learn More About State Machines
For detailed information about dotLottie state machine specifications, visit the official dotLottie spec.

Quick Start

Basic Setup

import { DotLottie } from '@lottiefiles/dotlottie-web';

const canvas = document.getElementById('canvas');
const dotLottie = new DotLottie({
  canvas,
  src: 'path/to/interactive-animation.lottie', // Contains state machine
  autoplay: true,
  loop: true
});

Loading State Machines

// Wait for animation to load before starting state machine
dotLottie.addEventListener('load', () => {
  // Load a state machine by ID (defined in the .lottie file)
  dotLottie.stateMachineLoad('my-state-machine-id');
  
  // Start the state machine
  dotLottie.stateMachineStart();
  
  // Check if it's running
  console.log(dotLottie.stateMachineGetStatus()); // "Playing" or "Stopped"
});

Core API Methods

State Machine Control

// Load and start
dotLottie.stateMachineLoad('button-interaction');
dotLottie.stateMachineStart();

// Stop and check status
dotLottie.stateMachineStop();
console.log(dotLottie.stateMachineGetStatus()); // "Stopped"

// Get current state name
console.log(dotLottie.stateMachineGetCurrentState()); // "idle"

// Override current state (force transition)
dotLottie.stateMachineOverrideState('active', true); // immediate: true

Working with Inputs

State machines use inputs to store and modify values that control behavior:

// Set input values
dotLottie.stateMachineSetBooleanInput('isPressed', true);
dotLottie.stateMachineSetNumericInput('counter', 5);
dotLottie.stateMachineSetStringInput('username', 'john');

// Get input values
const isPressed = dotLottie.stateMachineGetBooleanInput('isPressed');
const counter = dotLottie.stateMachineGetNumericInput('counter'); 
const username = dotLottie.stateMachineGetStringInput('username');

// Fire events (trigger event inputs)
dotLottie.stateMachineFireEvent('button_clicked');
dotLottie.stateMachineFireEvent('animation_complete');

Automatic Pointer Event Handling

Important: You don't need to manually handle pointer events! The dotLottie-web library automatically:

  1. Detects required events from your state machine interactions
  2. Automatically adds event listeners to the canvas
  3. Handles coordinate conversion and posts events to the state machine

When you call dotLottie.stateMachineStart(), the library:

  • Calls stateMachineGetListeners() to see what events your state machine needs
  • Automatically adds the appropriate event listeners (click, pointerdown, pointerup, etc.)
  • Handles all coordinate calculations and event posting internally
// This is all you need - no manual event handling required!
dotLottie.addEventListener("load", () => {
  dotLottie.stateMachineLoadData(JSON.stringify(stateMachine));
  dotLottie.stateMachineStart(); // Events are automatically handled after this
});

Event Handling

Listen for State Machine Events

// Listen for state changes
dotLottie.addEventListener('stateMachineStateEntered', (event) => {
  console.log('Entered state:', event.data.state);
});

dotLottie.addEventListener('stateMachineStateExit', (event) => {
  console.log('Exited state:', event.data.state);
});

// Listen for custom events from the state machine
dotLottie.addEventListener('stateMachineCustomEvent', (event) => {
  console.log('Custom event:', event.data.value);
  
  // Example: Handle different custom events
  switch (event.data.value) {
    case 'button_clicked':
      showNotification('Button was clicked!');
      break;
    case 'animation_complete':
      triggerNextStep();
      break;
  }
});

// Listen for input value changes
dotLottie.addEventListener('stateMachineNumericInputValueChange', (event) => {
  console.log(`Input ${event.data.inputName} changed to ${event.data.value}`);
});

Practical Examples

Example 1: Click to Play Animation

Simple click interaction that plays an animation on user interaction:

const canvas = document.getElementById('dotLottieCanvas');
const dotLottie = new DotLottie({
  canvas,
  src: 'https://lottie.host/fba88936-b753-4751-a6ca-94db246157cf/5pGajCeC0B.json'
});

const clickMachine = {
  initial: "Idle",
  states: [
    {
      name: "Idle",
      type: "GlobalState",
      transitions: [
        {
          type: "Transition",
          toState: "PlayAnimation",
          guards: [
            {
              type: "Event",
              inputName: "onClick"
            }
          ]
        }
      ]
    },
    {
      name: "PlayAnimation",
      type: "PlaybackState",
      animation: "",
      mode: "Forward",
      autoplay: true,
      transitions: []
    }
  ],
  interactions: [
    {
      type: "Click",
      actions: [
        {
          type: "Fire",
          inputName: "onClick"
        }
      ]
    }
  ],
  inputs: [
    {
      type: "Event",
      name: "onClick"
    }
  ]
};

dotLottie.addEventListener("load", () => {
  dotLottie.stateMachineLoadData(JSON.stringify(clickMachine));
  dotLottie.stateMachineStart();
});

Example 2: Hover In/Out Animation

Create smooth hover animations with forward and reverse playback:

const canvas = document.getElementById('dotLottieCanvas');
const dotLottie = new DotLottie({
  canvas,
  src: 'https://lottie.host/b3db2fe4-76ca-4628-946f-fab26eee90b3/vzUiccejvT.lottie'
});

const hoverMachine = {
  initial: "idle",
  states: [
    {
      // Global state waiting for cursor events
      type: "GlobalState",
      name: "idle",
      transitions: [
        {
          type: "Transition",
          toState: "hoverIn",
          guards: [{ type: "Event", inputName: "pointerEnter" }]
        },
        {
          type: "Transition",
          toState: "hoverOut",
          guards: [{ type: "Event", inputName: "pointerExit" }]
        }
      ]
    },
    // Play forward on hover in
    {
      animation: "",
      type: "PlaybackState",
      name: "hoverIn",
      mode: "Forward",
      autoplay: true,
      transitions: []
    },
    // Play in reverse on hover out
    {
      animation: "",
      type: "PlaybackState",
      name: "hoverOut",
      mode: "Reverse",
      autoplay: true,
      transitions: []
    }
  ],
  interactions: [
    {
      type: "PointerEnter",
      actions: [{ type: "Fire", inputName: "pointerEnter" }]
    },
    {
      type: "PointerExit",
      actions: [{ type: "Fire", inputName: "pointerExit" }]
    }
  ],
  inputs: [
    { type: "Event", name: "pointerEnter" },
    { type: "Event", name: "pointerExit" }
  ]
};

dotLottie.addEventListener("load", () => {
  dotLottie.stateMachineLoadData(JSON.stringify(hoverMachine));
  dotLottie.stateMachineStart();
});

Example 3: Hold Button Animation

Press and hold interaction with forward/reverse animation:

const canvas = document.getElementById('dotLottieCanvas');
const dotLottie = new DotLottie({
  canvas,
  src: 'https://lottie.host/cb480e3f-1db1-4fb9-a812-592cd1aaef3a/Qda343MSSf.lottie'
});

const holdMachine = {
  initial: "Idle",
  states: [
    {
      name: "Idle",
      type: "GlobalState",
      transitions: [
        {
          type: "Transition",
          toState: "HoldForward",
          guards: [
            {
              type: "Event",
              inputName: "onHoldStart"
            }
          ]
        },
        {
          type: "Transition",
          toState: "HoldReverse",
          guards: [
            {
              type: "Event",
              inputName: "onHoldEnd"
            }
          ]
        }
      ]
    },
    {
      name: "HoldForward",
      type: "PlaybackState",
      animation: "",
      autoplay: true,
      mode: "Forward",
      transitions: []
    },
    {
      name: "HoldReverse",
      type: "PlaybackState",
      animation: "",
      autoplay: true,
      mode: "Reverse",
      transitions: []
    }
  ],
  interactions: [
    {
      type: "PointerDown",
      actions: [
        {
          type: "Fire",
          inputName: "onHoldStart"
        }
      ]
    },
    {
      type: "PointerUp",
      actions: [
        {
          type: "Fire",
          inputName: "onHoldEnd"
        }
      ]
    }
  ],
  inputs: [
    {
      type: "Event",
      name: "onHoldStart"
    },
    {
      type: "Event",
      name: "onHoldEnd"
    }
  ]
};

dotLottie.addEventListener("load", () => {
  dotLottie.stateMachineLoadData(JSON.stringify(holdMachine));
  dotLottie.stateMachineStart();
});

Example 4: Cursor Position Control

Animation controlled by cursor position across the screen:

const canvas = document.getElementById('dotLottieCanvas');
const dotLottie = new DotLottie({
  canvas,
  src: 'https://lottie.host/fba88936-b753-4751-a6ca-94db246157cf/5pGajCeC0B.json'
});

const cursorMachine = {
  initial: "CursorControlled",
  states: [
    {
      name: "CursorControlled",
      type: "PlaybackState",
      animation: "",
      entryActions: [
        {
          type: "SetFrame",
          value: "$cursorFrame"
        }
      ],
      transitions: [
        {
          type: "Transition",
          toState: "CursorControlled",
          guards: [
            {
              type: "Event",
              inputName: "cursorMoved"
            }
          ]
        }
      ]
    }
  ],
  interactions: [],
  inputs: [
    {
      type: "Numeric",
      name: "cursorFrame",
      value: 0
    },
    {
      type: "Event",
      name: "cursorMoved"
    }
  ]
};

dotLottie.addEventListener("load", () => {
  dotLottie.stateMachineLoadData(JSON.stringify(cursorMachine));
  dotLottie.stateMachineStart();

  const syncToCursor = (event) => {
    const pos = Math.min(event.clientX / document.body.clientWidth, 1);
    const frame = dotLottie.totalFrames * pos;

    dotLottie.stateMachineSetNumericInput("cursorFrame", frame);
    dotLottie.stateMachineFireEvent("cursorMoved");
  };

  window.addEventListener("mousemove", syncToCursor);
});

Example 5: Scroll Progress Animation

Animation synchronized with page scroll position:

const canvas = document.getElementById('dotLottieCanvas');
const dotLottie = new DotLottie({
  canvas,
  src: 'https://lottie.host/fba88936-b753-4751-a6ca-94db246157cf/5pGajCeC0B.json'
});

const scrollMachine = {
  initial: "SyncProgress",
  states: [
    {
      name: "SyncProgress",
      type: "PlaybackState",
      animation: "",
      entryActions: [
        {
          type: "SetProgress",
          value: "$scrollProgress"
        }
      ],
      transitions: [
        {
          type: "Transition",
          toState: "SyncProgress",
          guards: [
            {
              type: "Event",
              inputName: "updateScroll"
            }
          ]
        }
      ]
    }
  ],
  interactions: [],
  inputs: [
    {
      type: "Numeric",
      name: "scrollProgress",
      value: 0
    },
    {
      type: "Event",
      name: "updateScroll"
    }
  ]
};

dotLottie.addEventListener("load", () => {
  dotLottie.stateMachineLoadData(JSON.stringify(scrollMachine));
  dotLottie.stateMachineStart();

  const syncToScroll = () => {
    const scrollTop = window.scrollY;
    const scrollHeight = document.body.scrollHeight - window.innerHeight;
    const scrollProgress = Math.min(scrollTop / scrollHeight, 1) * 100;

    dotLottie.stateMachineSetNumericInput("scrollProgress", scrollProgress);
    dotLottie.stateMachineFireEvent("updateScroll");
  };

  window.addEventListener("scroll", syncToScroll);
});

Example 6: Theme Cycling Animation

Automatic theme switching with completion events:

const canvas = document.getElementById('dotLottieCanvas');
const dotLottie = new DotLottie({
  canvas,
  src: 'https://lottie.host/8e34d211-e22e-459e-bfc7-7400b283be87/6RKEZ9wCqO.lottie'
});

const themeMachine = {
  initial: "Main",
  inputs: [{ type: "Numeric", name: "themeIndex", value: 0 }],
  states: [
    {
      name: "Main",
      type: "PlaybackState",
      animation: "",
      autoplay: true,
      transitions: [
        {
          type: "Transition",
          toState: "ApplyWater",
          guards: [
            {
              type: "Numeric",
              inputName: "themeIndex",
              conditionType: "Equal",
              compareTo: 1
            }
          ]
        }
      ]
    },
    {
      name: "ApplyWater",
      type: "PlaybackState",
      animation: "",
      autoplay: true,
      entryActions: [{ type: "SetTheme", value: "Water" }],
      transitions: [
        {
          type: "Transition",
          toState: "ApplyAir",
          guards: [
            {
              type: "Numeric",
              inputName: "themeIndex",
              conditionType: "Equal",
              compareTo: 2
            }
          ]
        }
      ]
    },
    {
      name: "ApplyAir",
      type: "PlaybackState",
      animation: "",
      autoplay: true,
      entryActions: [{ type: "SetTheme", value: "air" }],
      transitions: [
        {
          type: "Transition",
          toState: "ApplyEarth",
          guards: [
            {
              type: "Numeric",
              inputName: "themeIndex",
              conditionType: "Equal",
              compareTo: 3
            }
          ]
        }
      ]
    },
    {
      name: "ApplyEarth",
      type: "PlaybackState",
      animation: "",
      autoplay: true,
      entryActions: [{ type: "SetTheme", value: "earth" }],
      transitions: [
        {
          type: "Transition",
          toState: "Main",
          guards: [
            {
              type: "Numeric",
              inputName: "themeIndex",
              conditionType: "Equal",
              compareTo: 0
            }
          ]
        }
      ]
    }
  ],
  interactions: [
    {
      type: "OnComplete",
      stateName: "Main",
      actions: [{ type: "SetNumeric", inputName: "themeIndex", value: 1 }]
    },
    {
      type: "OnComplete",
      stateName: "ApplyWater",
      actions: [{ type: "SetNumeric", inputName: "themeIndex", value: 2 }]
    },
    {
      type: "OnComplete",
      stateName: "ApplyAir",
      actions: [{ type: "SetNumeric", inputName: "themeIndex", value: 3 }]
    },
    {
      type: "OnComplete",
      stateName: "ApplyEarth",
      actions: [{ type: "SetNumeric", inputName: "themeIndex", value: 0 }]
    }
  ]
};

dotLottie.addEventListener("load", () => {
  dotLottie.stateMachineLoadData(JSON.stringify(themeMachine));
  dotLottie.stateMachineStart();
});

Example 7: Interactive Emoji Rating

Smooth transitions between different animation segments using tweened transitions:

const canvas = document.getElementById('dotLottieCanvas');
const dotLottie = new DotLottie({
  canvas,
  src: 'https://lottie.host/1f21c068-194f-45cc-9003-f587ad559ba2/opL6U2S624.lottie'
});

const ratingMachine = {
  initial: "laughing",
  states: [
    {
      name: "global",
      type: "GlobalState",
      animation: "",
      transitions: [
        {
          type: "Tweened",
          toState: "angry",
          duration: 0.5,
          easing: [0.76, 0, 0.24, 1],
          guards: [
            {
              type: "Numeric",
              conditionType: "Equal",
              inputName: "rating",
              compareTo: 1
            }
          ]
        },
        {
          type: "Tweened",
          toState: "sad",
          duration: 0.5,
          easing: [0.76, 0, 0.24, 1],
          guards: [
            {
              type: "Numeric",
              conditionType: "Equal",
              inputName: "rating",
              compareTo: 2
            }
          ]
        },
        {
          type: "Tweened",
          toState: "mourn",
          duration: 0.5,
          easing: [0.76, 0, 0.24, 1],
          guards: [
            {
              type: "Numeric",
              conditionType: "Equal",
              inputName: "rating",
              compareTo: 3
            }
          ]
        },
        {
          type: "Tweened",
          toState: "wink",
          duration: 0.5,
          easing: [0.76, 0, 0.24, 1],
          guards: [
            {
              type: "Numeric",
              conditionType: "Equal",
              inputName: "rating",
              compareTo: 4
            }
          ]
        },
        {
          type: "Tweened",
          toState: "laughing",
          duration: 0.5,
          easing: [0.76, 0, 0.24, 1],
          guards: [
            {
              type: "Numeric",
              conditionType: "Equal",
              inputName: "rating",
              compareTo: 5
            }
          ]
        }
      ]
    },
    {
      type: "PlaybackState",
      name: "angry",
      animation: "",
      autoplay: true,
      loop: true,
      segment: "angry",
      transitions: []
    },
    {
      type: "PlaybackState",
      name: "sad",
      animation: "",
      autoplay: true,
      loop: true,
      segment: "sad",
      transitions: []
    },
    {
      type: "PlaybackState",
      name: "mourn",
      animation: "",
      autoplay: true,
      loop: true,
      segment: "mourn",
      transitions: []
    },
    {
      type: "PlaybackState",
      name: "wink",
      animation: "",
      autoplay: true,
      loop: true,
      segment: "wink",
      transitions: []
    },
    {
      type: "PlaybackState",
      name: "laughing",
      animation: "",
      autoplay: true,
      loop: true,
      segment: "laughing",
      transitions: []
    }
  ],
  inputs: [
    {
      type: "Numeric",
      name: "rating",
      value: 5 // Start with laughing
    }
  ],
  interactions: [
    {
      type: "PointerDown",
      layerName: "angry",
      actions: [
        {
          type: "SetNumeric",
          inputName: "rating",
          value: 1
        }
      ]
    },
    {
      type: "PointerDown",
      layerName: "sad",
      actions: [
        {
          type: "SetNumeric",
          inputName: "rating",
          value: 2
        }
      ]
    },
    {
      type: "PointerDown",
      layerName: "mourn",
      actions: [
        {
          type: "SetNumeric",
          inputName: "rating",
          value: 3
        }
      ]
    },
    {
      type: "PointerDown",
      layerName: "wink",
      actions: [
        {
          type: "SetNumeric",
          inputName: "rating",
          value: 4
        }
      ]
    },
    {
      type: "PointerDown",
      layerName: "laughing",
      actions: [
        {
          type: "SetNumeric",
          inputName: "rating",
          value: 5
        }
      ]
    }
  ]
};

dotLottie.addEventListener("load", () => {
  dotLottie.stateMachineLoadData(JSON.stringify(ratingMachine));
  dotLottie.stateMachineStart();
});

Example 8: Audio Reactive Animation

Animation that responds to microphone input:

const canvas = document.getElementById('dotLottieCanvas');
const dotLottie = new DotLottie({
  canvas,
  src: 'https://lottie.host/fba88936-b753-4751-a6ca-94db246157cf/5pGajCeC0B.json'
});

const audioMachine = {
  initial: "VolumeControlled",
  inputs: [
    { type: "Numeric", name: "volumeLevel", value: 0 },
    { type: "Event", name: "volumeChanged" }
  ],
  states: [
    {
      name: "VolumeControlled",
      type: "PlaybackState",
      animation: "",
      entryActions: [
        {
          type: "SetProgress",
          value: "$volumeLevel"
        }
      ],
      transitions: [
        {
          type: "Transition",
          toState: "VolumeControlled",
          guards: [{ type: "Event", inputName: "volumeChanged" }]
        }
      ]
    }
  ],
  interactions: []
};

const audioSensitivity = 0.4; // Adjust sensitivity

dotLottie.addEventListener("load", async () => {
  dotLottie.stateMachineLoadData(JSON.stringify(audioMachine));
  dotLottie.stateMachineStart();

  try {
    const audioCtx = new AudioContext();
    const analyser = new AnalyserNode(audioCtx, {
      fftSize: 64,
      maxDecibels: -25,
      minDecibels: -60,
      smoothingTimeConstant: 0.6
    });

    const data = new Uint8Array(analyser.frequencyBinCount);
    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
    const source = audioCtx.createMediaStreamSource(stream);
    source.connect(analyser);

    function updateFromMic() {
      analyser.getByteFrequencyData(data);
      let volumeRaw = data[0]; // 0–255 from mic
      let volume = volumeRaw * audioSensitivity;
      volume = Math.max(0, Math.min(100, volume)); // Clamp to 0–100

      dotLottie.stateMachineSetNumericInput("volumeLevel", volume);
      dotLottie.stateMachineFireEvent("volumeChanged");

      requestAnimationFrame(updateFromMic);
    }

    await audioCtx.resume();
    requestAnimationFrame(updateFromMic);
  } catch (error) {
    console.error('Microphone access denied:', error);
  }
});

Example 9: Device Orientation Control (Mobile)

Animation controlled by device tilt (mobile devices):

const canvas = document.getElementById('dotLottieCanvas');
const enableBtn = document.getElementById('enableOrientation');

const dotLottie = new DotLottie({
  canvas,
  src: 'https://lottie.host/fba88936-b753-4751-a6ca-94db246157cf/5pGajCeC0B.json'
});

const orientationMachine = {
  initial: "OrientationControlled",
  inputs: [
    {
      type: "Numeric",
      name: "tiltProgress",
      value: 0
    },
    {
      type: "Event",
      name: "orientationChanged"
    }
  ],
  states: [
    {
      name: "OrientationControlled",
      type: "PlaybackState",
      animation: "",
      entryActions: [
        {
          type: "SetProgress",
          value: "$tiltProgress"
        }
      ],
      transitions: [
        {
          type: "Transition",
          toState: "OrientationControlled",
          guards: [{ type: "Event", inputName: "orientationChanged" }]
        }
      ]
    }
  ],
  interactions: []
};

function handleOrientation(event) {
  const gamma = Math.max(-90, Math.min(90, event.gamma || 0));
  const percent = ((gamma + 90) / 180) * 100;

  dotLottie.stateMachineSetNumericInput("tiltProgress", percent);
  dotLottie.stateMachineFireEvent("orientationChanged");
}

dotLottie.addEventListener("load", async () => {
  dotLottie.stateMachineLoadData(JSON.stringify(orientationMachine));
  dotLottie.stateMachineStart();

  if (
    typeof DeviceOrientationEvent !== "undefined" &&
    typeof DeviceOrientationEvent.requestPermission === "function"
  ) {
    // iOS 13+ Safari requires user interaction
    enableBtn.style.display = "block";
  } else if ("DeviceOrientationEvent" in window) {
    // Orientation is supported without permission
    window.addEventListener("deviceorientation", handleOrientation);
  }
});

enableBtn.addEventListener("click", async () => {
  const granted = await DeviceOrientationEvent.requestPermission();
  if (granted === "granted") {
    window.addEventListener("deviceorientation", handleOrientation);
    enableBtn.style.display = "none";
  } else {
    alert("Motion access was denied.");
  }
});

Framework Integration

React Integration

import { DotLottieReact } from '@lottiefiles/dotlottie-react';
import { useRef, useState } from 'react';

function InteractiveAnimation() {
  const dotLottieRef = useRef(null);
  const [currentState, setCurrentState] = useState('idle');
  const [counter, setCounter] = useState(0);

  const handleLoad = () => {
    const dotLottie = dotLottieRef.current;
    if (!dotLottie) return;

    // Load and start state machine after animation loads
    dotLottie.stateMachineLoad('interactive');
    dotLottie.stateMachineStart();

    // Listen for state changes
    const handleStateChange = (event) => {
      setCurrentState(event.data.state);
    };

    const handleCounterChange = (event) => {
      if (event.data.inputName === 'counter') {
        setCounter(event.data.value);
      }
    };

    dotLottie.addEventListener('stateMachineStateEntered', handleStateChange);
    dotLottie.addEventListener('stateMachineNumericInputValueChange', handleCounterChange);
  };

  const handleIncrement = () => {
    const dotLottie = dotLottieRef.current;
    if (dotLottie) {
      const currentValue = dotLottie.stateMachineGetNumericInput('counter') || 0;
      dotLottie.stateMachineSetNumericInput('counter', currentValue + 1);
      dotLottie.stateMachineFireEvent('counter_changed');
    }
  };

  const handleReset = () => {
    const dotLottie = dotLottieRef.current;
    if (dotLottie) {
      dotLottie.stateMachineSetNumericInput('counter', 0);
      dotLottie.stateMachineFireEvent('reset');
    }
  };

  return (
    <div>
      <DotLottieReact
        ref={dotLottieRef}
        src="interactive-counter.lottie"
        loop
        autoplay
        onLoad={handleLoad}
        style={{ width: 400, height: 400 }}
      />
      
      <div>
        <p>Current State: {currentState}</p>
        <p>Counter: {counter}</p>
        <button onClick={handleIncrement}>Increment</button>
        <button onClick={handleReset}>Reset</button>
      </div>
    </div>
  );
}

Vue Integration

<template>
  <div>
    <DotLottieVue
      ref="dotLottieRef"
      :src="animationSrc"
      :loop="true"
      :autoplay="true"
      @load="onLoad"
      style="width: 400px; height: 400px"
    />
    
    <div>
      <p>Current State: {{ currentState }}</p>
      <button @click="toggleState">Toggle</button>
      <button @click="setCustomInput">Set Custom Value</button>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import { DotLottieVue } from '@lottiefiles/dotlottie-vue';

const dotLottieRef = ref(null);
const animationSrc = ref('interactive-toggle.lottie');
const currentState = ref('idle');

const onLoad = () => {
  const dotLottie = dotLottieRef.value?.getDotLottieInstance();
  if (!dotLottie) return;

  // Load state machine
  dotLottie.stateMachineLoad('toggle');
  dotLottie.stateMachineStart();

  // Listen for events
  dotLottie.addEventListener('stateMachineStateEntered', (event) => {
    currentState.value = event.data.state;
  });
};

const toggleState = () => {
  const dotLottie = dotLottieRef.value?.getDotLottieInstance();
  if (dotLottie) {
    dotLottie.stateMachineFireEvent('toggle');
  }
};

const setCustomInput = () => {
  const dotLottie = dotLottieRef.value?.getDotLottieInstance();
  if (dotLottie) {
    dotLottie.stateMachineSetStringInput('user_input', 'Hello World');
    dotLottie.stateMachineFireEvent('input_changed');
  }
};
</script>

Advanced Techniques

Real-time Input Synchronization

Synchronize external data with your animations:

class AnimationController {
  constructor(canvasId, src) {
    this.canvas = document.getElementById(canvasId);
    this.dotLottie = new DotLottie({
      canvas: this.canvas,
      src: src,
      autoplay: true
    });
    
    this.setupStateMachine();
    this.setupEventListeners();
  }

  async setupStateMachine() {
    // Wait for animation to load
    await new Promise(resolve => {
      this.dotLottie.addEventListener('load', resolve, { once: true });
    });

    this.dotLottie.stateMachineLoad('data-sync');
    this.dotLottie.stateMachineStart();
  }

  setupEventListeners() {
    // Listen for all input changes
    this.dotLottie.addEventListener('stateMachineNumericInputValueChange', (event) => {
      console.log(`Numeric input ${event.data.inputName}: ${event.data.value}`);
    });

    this.dotLottie.addEventListener('stateMachineCustomEvent', (event) => {
      this.handleCustomEvent(event.data.value);
    });
  }

  // Sync external API data
  async syncWithAPI() {
    try {
      const response = await fetch('/api/dashboard-data');
      const data = await response.json();

      // Update multiple inputs
      this.dotLottie.stateMachineSetNumericInput('cpu_usage', data.cpu / 100);
      this.dotLottie.stateMachineSetNumericInput('memory_usage', data.memory / 100);
      this.dotLottie.stateMachineSetStringInput('status', data.status);
      
      // Trigger update
      this.dotLottie.stateMachineFireEvent('data_updated');
    } catch (error) {
      console.error('Failed to sync data:', error);
    }
  }

  // Handle real-time WebSocket updates
  connectWebSocket() {
    const ws = new WebSocket('wss://api.example.com/live-data');
    
    ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      
      // Update animation in real-time
      this.dotLottie.stateMachineSetNumericInput('live_value', data.value);
      this.dotLottie.stateMachineFireEvent('live_update');
    };
  }

  handleCustomEvent(eventValue) {
    switch (eventValue) {
      case 'alert_triggered':
        this.showNotification('Alert!');
        break;
      case 'threshold_exceeded':
        this.highlightDashboard();
        break;
      default:
        console.log('Unknown custom event:', eventValue);
    }
  }
}

// Usage
const controller = new AnimationController('dashboard-canvas', 'dashboard.lottie');
controller.connectWebSocket();
setInterval(() => controller.syncWithAPI(), 5000);

State Machine Debugging

Useful debugging techniques:

class StateMachineDebugger {
  constructor(dotLottie) {
    this.dotLottie = dotLottie;
    this.stateHistory = [];
    this.inputHistory = [];
    
    this.enableDebugLogging();
  }

  enableDebugLogging() {
    // Track all state changes
    this.dotLottie.addEventListener('stateMachineStateEntered', (event) => {
      const entry = {
        timestamp: Date.now(),
        type: 'state_entered',
        state: event.data.state,
        currentInputs: this.getAllInputValues()
      };
      
      this.stateHistory.push(entry);
      console.log('🔄 State entered:', entry);
    });

    this.dotLottie.addEventListener('stateMachineStateExit', (event) => {
      console.log('🚪 State exited:', event.data.state);
    });

    // Track input changes
    ['Numeric', 'String', 'Boolean'].forEach(type => {
      this.dotLottie.addEventListener(`stateMachine${type}InputValueChange`, (event) => {
        const entry = {
          timestamp: Date.now(),
          type: `${type.toLowerCase()}_input_change`,
          inputName: event.data.inputName,
          value: event.data.value
        };
        
        this.inputHistory.push(entry);
        console.log(`📝 ${type} input changed:`, entry);
      });
    });

    // Track custom events
    this.dotLottie.addEventListener('stateMachineCustomEvent', (event) => {
      console.log('🎯 Custom event:', event.data.value);
    });

    // Track errors
    this.dotLottie.addEventListener('stateMachineError', (event) => {
      console.error('❌ State machine error:', event.data.error);
    });
  }

  getAllInputValues() {
    // This would need to be implemented based on your specific inputs
    return {
      // Example of reading all inputs you know about
      counter: this.dotLottie.stateMachineGetNumericInput('counter'),
      isActive: this.dotLottie.stateMachineGetBooleanInput('isActive'),
      status: this.dotLottie.stateMachineGetStringInput('status')
    };
  }

  printDebugInfo() {
    console.log('🐛 Debug Info:');
    console.log('Current State:', this.dotLottie.stateMachineGetCurrentState());
    console.log('Status:', this.dotLottie.stateMachineGetStatus());
    console.log('Current Inputs:', this.getAllInputValues());
    console.log('State History:', this.stateHistory.slice(-10)); // Last 10 states
    console.log('Input History:', this.inputHistory.slice(-10)); // Last 10 changes
  }

  exportDebugData() {
    return {
      currentState: this.dotLottie.stateMachineGetCurrentState(),
      currentInputs: this.getAllInputValues(),
      stateHistory: this.stateHistory,
      inputHistory: this.inputHistory
    };
  }
}

// Usage
const debugger = new StateMachineDebugger(dotLottie);

// Print debug info to console
document.addEventListener('keydown', (event) => {
  if (event.key === 'D' && event.ctrlKey) {
    debugger.printDebugInfo();
  }
});

API Reference

State Machine Control

// Loading and lifecycle
dotLottie.stateMachineLoad(stateMachineId: string): boolean
dotLottie.stateMachineStart(): boolean
dotLottie.stateMachineStop(): boolean

// Status and information
dotLottie.stateMachineGetStatus(): string
dotLottie.stateMachineGetCurrentState(): string
dotLottie.stateMachineGetActiveId(): string

// State control
dotLottie.stateMachineOverrideState(state: string, immediate: boolean): boolean

Input Management

// Set input values
dotLottie.stateMachineSetBooleanInput(name: string, value: boolean): boolean
dotLottie.stateMachineSetNumericInput(name: string, value: number): boolean
dotLottie.stateMachineSetStringInput(name: string, value: string): boolean

// Get input values
dotLottie.stateMachineGetBooleanInput(name: string): boolean | undefined
dotLottie.stateMachineGetNumericInput(name: string): number | undefined
dotLottie.stateMachineGetStringInput(name: string): string | undefined

// Fire events
dotLottie.stateMachineFireEvent(name: string): void

Event Listeners

// State machine events
dotLottie.addEventListener('stateMachineStateEntered', callback)
dotLottie.addEventListener('stateMachineStateExit', callback)
dotLottie.addEventListener('stateMachineTransition', callback)

// Input change events
dotLottie.addEventListener('stateMachineNumericInputValueChange', callback)
dotLottie.addEventListener('stateMachineStringInputValueChange', callback)
dotLottie.addEventListener('stateMachineBooleanInputValueChange', callback)

// Custom and error events
dotLottie.addEventListener('stateMachineCustomEvent', callback)
dotLottie.addEventListener('stateMachineError', callback)

Building .lottie Files with Embedded State Machines

The examples in this guide load state machines externally for demonstration purposes. In production, you can embed state machines directly into .lottie files using the @dotlottie/dotlottie-js SDK:

import { DotLottie } from '@dotlottie/dotlottie-js';

async function createDotLottieWithStateMachine() {
  try {
    const dotlottie = new DotLottie();

    // Add your animation
    dotlottie.addAnimation({
      id: 'my-animation',
      url: 'https://assets.lottiefiles.com/packages/lf20_VeqS4d.json',
      name: 'Interactive Animation'
    });

    // Add your state machine
    dotlottie.addStateMachine({
      id: 'my-state-machine',
      data: clickMachine // Use your state machine JSON here
    });

    // Build the .lottie file
    await dotlottie.build();

    // Export to ArrayBuffer
    const arrayBuffer = await dotlottie.toArrayBuffer();
    
    return arrayBuffer;
  } catch (error) {
    console.error('Error creating .lottie file:', error);
  }
}

// Usage
const buffer = await createDotLottieWithStateMachine();
Clone this wiki locally