Skip to main content

Script Types Overview

Rive provides 8 script types, each designed for a specific purpose. This page gives you the big picture — which script type to choose and when.

Runtime baseline

This overview is aligned to the June 4, 2026 compatibility baseline: public npm runtime line 2.37.8 plus source-level API snapshot runtime-v0.1.106. GPU callback guidance remains Rive Early Access editor workflow material. For versioned API-surface tracking, see Runtime Compatibility Baseline.

Need Full Access/Limitations Matrix?

For a comprehensive "what can each script type do?" reference (including callback parameters, path-geometry boundaries, and input accessibility), see Script Capability Matrix.

Coming from Part 1?

If you haven't read How Rive Scripts Work yet, start there first. It explains WHY scripts have the structure they do — the "protocol" concept, factory functions, and lifecycle callbacks.


The 8 Script Types

Script TypePrimary PurposePlacement / Binding Target
Node ScriptAnimation logic, custom renderingScene script node
Layout ScriptCustom layout behaviorLayout components
Converter ScriptData transformation for bindingsData bindings
Path Effect ScriptModify path geometryStrokes & Fills
ListenerAction ScriptCustom logic on state machine listener eventsState machine listeners
TransitionCondition ScriptCustom transition evaluation logicState machine transitions
Util ScriptShared code librariesNothing (imported)
Test ScriptUnit testing Util ScriptsNothing (run manually)
Listeners

Listener<T> is a function signature, not a script type. See Listeners for details.


Node Script

Use when: You need animation logic, custom drawing, or interactive behavior on a node.

Key features:

  • Lifecycle callbacks: init, update, advance, drawCanvas, draw
  • Pointer event handlers: pointerDown, pointerMove, pointerUp
  • Text/keyboard handlers: textEvent, keyboardEvent
  • Access to Renderer for custom drawing
  • Access to Canvas / GPUCanvas render phases for offscreen and shader workflows
  • Can have inputs from the editor

Placement: A Node script is added to the artboard as a script node. You can leave it at the artboard root or nest it under a group/shape for normal hierarchy behavior such as transform inheritance and draw order. Nesting does not pass the parent shape's NodeData or PathData into the Node callbacks.

Typical use cases:

  • Procedural animations (particles, physics)
  • Custom drawing (graphs, visualizations)
  • GPU shader passes and post-processing
  • Interactive elements (buttons, sliders)
  • Game logic (collision, scoring)

Factory return type: Node<T>

return function(): Node<MyScript>
return { init = init, advance = advance, draw = draw }
end

Learn more: Node Script Protocol → | Node Lifecycle →


Layout Script

Use when: You need programmatic control over how child elements are positioned and sized within a Layout component.

Key features:

  • All Node Script lifecycle callbacks PLUS:
  • measure() - Propose ideal size (for "Hug" fit type)
  • resize(size) - React to size changes, position children

Typical use cases:

  • Masonry grids
  • Carousels with custom spacing
  • Responsive layouts with breakpoint logic
  • Data-driven layout systems

Factory return type: Layout<T>

return function(): Layout<MyLayout>
return { init = init, measure = measure, resize = resize, draw = draw }
end

Learn more: Layout Script Protocol →


Converter Script

Use when: You need to transform data between a source and target in data bindings.

Key features:

  • convert(input) - Transform source → target
  • reverseConvert(input) - Transform target → source (for two-way binding)
  • Works with DataValue types (Number, String, Boolean, Color)

Typical use cases:

  • Format numbers (add units, decimals)
  • Temperature conversion (Celsius ↔ Fahrenheit)
  • Color transformations
  • String formatting/parsing

Factory return type: Converter<T, InputType, OutputType>

return function(): Converter<MyConverter, DataValueNumber, DataValueString>
return { convert = convert, reverseConvert = reverseConvert }
end

Learn more: Converter Script Protocol →


Path Effect Script

Use when: You need to modify the geometry of a path in real-time (stroke effects, fill distortions, warps).

Key features:

  • update(inPath, node) - Receives original PathData and host NodeReadData, returns modified PathData
  • advance(seconds) - Optional frame updates for animated effects
  • Access to PathCommand iteration

Typical use cases:

  • Wave/wobble distortion effects
  • Custom dash patterns
  • Procedural path generation
  • Animated stroke and fill effects

Factory return type: PathEffect<T>

return function(): PathEffect<MyEffect>
return { init = init, update = update, advance = advance }
end

Learn more: Path Effect Script Protocol →


ListenerAction Script

Use when: You need custom logic to run when a state machine listener event fires.

Key features:

  • init(self, context) - One-time initialization
  • performAction(self, listenerContext) - Executed when the listener action triggers (preferred)
  • perform(self, pointerEvent) - Legacy/deprecated callback in older scaffolds
  • listenerContext supports typed branching (is... / as...) for pointer, keyboard, text, focus, and listener payload kinds
  • Attached to state machine listener events

Typical use cases:

  • Custom side-effects when a state transition fires a listener
  • Triggering audio, analytics, or other actions from state machine events
  • Complex logic that can't be expressed with simple state machine outputs

Factory return type: ListenerAction<T>

return function(): ListenerAction<MyAction>
return { init = init, performAction = performAction }
end

Learn more: ListenerAction Script Protocol →


TransitionCondition Script

Use when: You need custom logic to evaluate whether a state machine transition should fire.

Key features:

  • init(self, context) - One-time initialization
  • evaluate(self) - Returns true/false to allow/block the transition
  • Attached to state machine transitions

Typical use cases:

  • Complex transition conditions that depend on multiple inputs
  • Time-based or physics-based transition logic
  • Conditions that require script-level computation

Factory return type: TransitionCondition<T>

return function(): TransitionCondition<MyCondition>
return { init = init, evaluate = evaluate }
end

Learn more: TransitionCondition Script Protocol →


ScriptedInterpolator (Runtime-Backed Advanced Protocol)

Use when: You need custom interpolation curves at runtime for keyframe value blending and want script-controlled easing behavior.

Current status:

  • Runtime support is present in the audited C++ bindings tracked by the current compatibility baseline.
  • This protocol is documented in LERP for compatibility tracking and advanced usage.
  • Editor UI exposure can lag runtime support; verify availability in your current editor build before production use.

Key methods:

  • transform(self, factor) -> number (optional)
  • transformValue(self, valueFrom, valueTo, factor) -> number (optional)

Fallback behavior:

  • If methods are omitted or fail, runtime falls back to standard linear interpolation behavior.

Learn more: ScriptedInterpolator Protocol →


Util Script

Use when: You have shared code (math functions, constants, helpers) used by multiple scripts.

Creating a Util Script

In the Rive editor, select Blank Script from the script type dropdown. There is no dedicated "Util Script" option—you create one by selecting Blank Script and following the Util Script pattern described below and in the official Rive docs.

Key features:

  • No lifecycle callbacks — just a module table
  • Imported with require("UtilName")
  • Runs once when first required (cached)
  • Perfect for pure functions and constants

Typical use cases:

  • Math utilities (lerp, clamp, easing functions)
  • Color palettes and constants
  • Helper functions shared across scripts
  • Configuration objects

Return type: Module table (not a factory)

local MathUtils = {}

function MathUtils.lerp(a: number, b: number, t: number): number
return a + (b - a) * t
end

return MathUtils

Learn more: Util Script Protocol →


Event Listening (Not a Script Type)

Listener<T> is NOT a Script Type

Listener<T> is a function signature for addListener()/removeListener() methods, not a script return type.

To handle events in Rive, use these patterns:

1. ViewModel Property Listeners (in Node scripts):

function init(self: MyNode, context: Context): boolean
local vm = context:viewModel()
local score = vm:getNumber("score")
if score then
score:addListener(function()
print("Score changed to: " .. score.value)
end)
end
return true
end

2. Pointer Events (in Node scripts):

function pointerDown(self: MyNode, event: PointerEvent)
print("Clicked at: " .. event.position.x)
end

Learn more: Listeners →


Test Script

Use when: You want to write unit tests for your Util Scripts.

Key features:

  • setup(test: Tester) - Define test cases
  • test.case(name, fn) - Individual test
  • test.group(name, fn) - Group related tests
  • expect(value).is(expected) - Assertions

Typical use cases:

  • Verify math utilities work correctly
  • Test edge cases in helper functions
  • Regression testing after changes
  • Document expected behavior

Return type: Tests (function)

local MathUtils = require('MathUtils')

function setup(test: Tester)
test.case('lerp at 0.5', function(expect)
expect(MathUtils.lerp(0, 100, 0.5)).is(50)
end)
end

return function(): Tests
return setup
end

Learn more: Test Script Protocol →


Scripts and State Machines

Rive state machines decide what state an animation is in. Scripts decide how that state is visualized or extended.

How they connect:

  • ViewModels are the primary data bridge. Use ViewModel properties to drive script behavior and UI state.
  • Script Inputs (Input<T>) are best for per-instance tuning from the inspector.
  • State machine inputs are a legacy path for data flow; use them only when integrating existing files.
  • Property listeners (addListener()) respond to ViewModel property changes triggered by state machines.
  • Node scripts render visuals and can read inputs or ViewModel values to drive drawing.

Typical flow:

  1. State machine or interaction updates a ViewModel property (for example, isHovering = true)
  2. Script reads that value in update() or via addListener()
  3. Script changes visuals in draw() or updates ViewModel data

If you need to react to property changes, use property:addListener() in a Node script. If you need to render or animate based on inputs, use update() or advance() callbacks.


Quick Decision Guide


Factory Functions Recap

Remember from Part 1: every script (except Util and Test) uses a factory function. The factory creates fresh instances so each object using your script gets its own data.

Script TypeFactory Return
NodeNode<T>
LayoutLayout<T>
ConverterConverter<T, In, Out>
Path EffectPathEffect<T>
ListenerActionListenerAction<T>
TransitionConditionTransitionCondition<T>
UtilModule table (no factory)
TestTests function (no factory)

Next Steps