Skip to content

State machine inputs & events

How to drive a Lupian animation from your site (inputs) and respond to it (events).

Use this when

  • The animation has interactive states (hover, click, progress, scroll, etc.).
  • The animation needs to tell your code something happened ("submit button clicked", "sequence finished").
  • handoff.md lists inputs or events.

What you need

  • An initialised Rive instance (from any recipe).
  • The state machine name from handoff.md.
  • The input/event names from handoff.md (case-sensitive).

Concepts

TermDirectionExample
InputYour code → animationisHovering = true to start a hover animation
EventAnimation → your code"buttonClicked" fires when the user clicks the visual button

Inputs come in three types: boolean (value: true/false), number (value: 0–100, etc.), trigger (call .fire() once).

Reading inputs in Vanilla Web

js
const r = new Rive({
  src: "/animations/hero.riv",
  canvas,
  autoplay: true,
  stateMachines: "State Machine 1",
  onLoad: () => {
    r.resizeDrawingSurfaceToCanvas();

    const inputs = r.stateMachineInputs("State Machine 1");

    const isHover  = inputs.find(i => i.name === "isHovering");  // bool
    const progress = inputs.find(i => i.name === "progress");    // number
    const onClick  = inputs.find(i => i.name === "onClick");     // trigger

    canvas.addEventListener("mouseenter", () => { isHover.value = true; });
    canvas.addEventListener("mouseleave", () => { isHover.value = false; });
    canvas.addEventListener("click",      () => { onClick.fire(); });

    // Drive a number from scroll, for example
    window.addEventListener("scroll", () => {
      progress.value = Math.min(100, window.scrollY / 5);
    });
  },
});

Read inputs after onLoad

stateMachineInputs(...) returns an empty array until the file has loaded. Always read inside onLoad or after a "load" event.

Reading inputs in React

tsx
import {
  useRive,
  useStateMachineInput,
} from "@rive-app/react-webgl2";

export function Button() {
  const { rive, RiveComponent } = useRive({
    src: "/animations/button.riv",
    stateMachines: "Button SM",
    autoplay: true,
  });

  const isHover = useStateMachineInput(rive, "Button SM", "isHovering");
  const onClick = useStateMachineInput(rive, "Button SM", "onClick");

  return (
    <RiveComponent
      onMouseEnter={() => isHover && (isHover.value = true)}
      onMouseLeave={() => isHover && (isHover.value = false)}
      onClick={() => onClick?.fire()}
    />
  );
}

Listening to events

Events are custom messages the animation fires (set up by Lupian in the Rive editor).

Vanilla Web:

js
new Rive({
  src: "/animations/form.riv",
  canvas,
  autoplay: true,
  stateMachines: "State Machine 1",
  onRiveEventReceived: (event) => {
    if (event.data.name === "submitClicked") {
      yourForm.submit();
    }
  },
});

React:

tsx
const { RiveComponent } = useRive({
  src: "/animations/form.riv",
  stateMachines: "State Machine 1",
  autoplay: true,
  onRiveEventReceived: (event) => {
    if (event.data.name === "submitClicked") {
      // ...
    }
  },
});

Event payloads can carry properties (numbers, strings, URLs) — event.data includes them. See rive.app/docs for the full event payload reference.

Common mistakes

  • Reading inputs before onLoad. Returns an empty array. Wrap in onLoad.
  • Wrong input name. Case-sensitive. Copy from handoff.md exactly.
  • Setting a boolean to a string (isHover.value = "true"). Use real true / false.
  • Calling .fire() on a non-trigger input (or .value = ... on a trigger). The types are fixed by Lupian in the editor; handoff.md lists each one.
  • Forgetting that React inputs may be null on first render. Guard with input && (...) or input?.fire().
  • Setting a number input outside its declared range. It clamps silently — the animation just looks stuck.

If stuck, send this to Lupian

  • The state machine name + input/event names you are using.
  • A short clip of the wrong behaviour.
  • handoff.md so we can confirm what is wired up.
  • See Ask Lupian for help.

Developer support for Lupian-delivered Rive animations