Skip to main content

Data & Input

APIs for script inputs, properties, runtime context, and data binding.


Input

An input is a value on your script that appears as a control in the Rive editor's inspector panel. When someone changes that control, your script receives the new value automatically.

Think of inputs like knobs and sliders on a mixing board — they let you (or someone using your file) adjust behavior without editing code.

How Inputs Work

  1. You declare an input in your script's type definition
  2. You provide a default value in the factory return
  3. The input appears in the editor as a control (slider, checkbox, color picker, etc.)
  4. When the value changes, Rive calls your script's update() function
  5. You read the value with self.inputName — no .value needed
export type MyNode = {
speed: Input<number>, -- declares an input called "speed"
}

function update(self: MyNode)
-- This runs whenever speed changes in the editor
print("Speed is now: " .. self.speed)
end

return function(): Node<MyNode>
return {
init = init,
update = update,
draw = draw,
speed = 50, -- default value: 50
}
end
Inputs vs Properties

Inputs (Input<T>) are read with self.speed — no .value. Properties (Property<T>) from ViewModels are read with prop.value. They look similar but work differently. If you're using self.something and it's an input, just use it directly.


Input<number>

A numeric value. Appears as a number field in the editor.

When to use: Anything that's a number — speed, size, count, opacity, angle, duration.

Factory default: Any number (e.g. 42, 0.5, 100).

How to read: self.speed returns the number directly.

export type Particle = {
speed: Input<number>,
size: Input<number>,
opacity: Input<number>,
}

function advance(self: Particle, seconds: number): boolean
-- Use the numbers directly
local distance = self.speed * seconds
local radius = self.size * 0.5
return true
end

return function(): Node<Particle>
return {
init = init,
advance = advance,
draw = draw,
speed = 100, -- pixels per second
size = 20, -- diameter in pixels
opacity = 255, -- fully opaque
}
end

Input<boolean>

A true/false toggle. Appears as a checkbox in the editor.

When to use: On/off switches — show/hide something, enable/disable a feature, flip a mode.

Factory default: true or false.

How to read: self.isVisible returns true or false.

export type Overlay = {
isVisible: Input<boolean>,
showShadow: Input<boolean>,
}

function draw(self: Overlay, renderer: Renderer)
if not self.isVisible then return end

renderer:drawPath(self.bgPath, self.bgPaint)

if self.showShadow then
renderer:drawPath(self.shadowPath, self.shadowPaint)
end
end

return function(): Node<Overlay>
return {
init = init,
draw = draw,
isVisible = true,
showShadow = false,
}
end

Input<string>

A text value. Appears as a text field in the editor.

When to use: Labels, names, mode selectors, anything that's text.

Factory default: Any string in quotes (e.g. "hello", "idle", "").

How to read: self.label returns the string directly.

export type Badge = {
label: Input<string>,
mode: Input<string>,
}

function update(self: Badge)
if self.mode == "warning" then
self.paint.color = Color.rgb(255, 200, 0)
elseif self.mode == "error" then
self.paint.color = Color.rgb(255, 60, 60)
else
self.paint.color = Color.rgb(100, 200, 100)
end
end

return function(): Node<Badge>
return {
init = init,
update = update,
draw = draw,
label = "Status",
mode = "normal",
}
end

Input<Color>

A color value. Appears as a color picker in the editor.

When to use: Tints, fills, backgrounds — any color that should be adjustable.

Factory default: A Color value like Color.rgb(255, 0, 0) or Color.rgba(100, 150, 200, 128).

How to read: self.tint returns a Color value you can assign to a paint.

export type ColoredShape = {
fillColor: Input<Color>,
strokeColor: Input<Color>,
}

function update(self: ColoredShape)
-- When either color changes, update the paints
self.fillPaint.color = self.fillColor
self.strokePaint.color = self.strokeColor
end

return function(): Node<ColoredShape>
return {
init = init,
update = update,
draw = draw,
fillColor = Color.rgb(80, 160, 255),
strokeColor = Color.rgb(40, 80, 180),
fillPaint = late(),
strokePaint = late(),
}
end

Input<Data.X>

A reference to a ViewModel instance — a named collection of properties defined in the Rive editor. This is how you connect your script to data that lives on the artboard.

When to use: When your script needs to read or write properties that are bound to visual elements — positions, scores, colors, toggles that drive the animation.

Factory default: late() — because the editor assigns the ViewModel instance at runtime. Your script doesn't know which instance it will receive until the file loads.

How to read: self.character gives you the ViewModel instance. To access its properties, use .propertyName.value:

export type Controller = {
character: Input<Data.Character>,
time: number,
}

function advance(self: Controller, seconds: number): boolean
self.time += seconds

-- Read a property
local currentX = self.character.posX.value

-- Write a property (updates all bound visual elements)
self.character.posX.value = math.sin(self.time) * 100
self.character.posY.value = math.cos(self.time) * 50

return true
end

return function(): Node<Controller>
return {
init = init,
advance = advance,
draw = draw,
character = late(), -- assigned by the editor
time = 0,
}
end

Editor setup:

  1. Create a ViewModel in the Rive editor (e.g. Character with posX and posY number properties)
  2. Attach the script to a node
  3. In the script's input panel, assign the ViewModel instance to character
Why .value?

self.character is the ViewModel instance. self.character.posX is a Property<number>. Properties hold their value inside .value — that's how Rive tracks changes and updates bindings. So you always read/write with .value at the end.


Input<Artboard>

A reference to a nested artboard (component). Use this to create instances of reusable components at runtime — particles, list items, repeated UI elements.

When to use: When your script needs to stamp out copies of a component, each with its own data.

Factory default: late() — the editor assigns the artboard reference.

How to read: self.template gives you the artboard. Call :instance() to create a copy, optionally with a ViewModel:

export type Spawner = {
template: Input<Artboard>,
instances: { Artboard },
}

function init(self: Spawner, context: Context): boolean
self.instances = {}

-- Create 5 instances of the template
for i = 1, 5 do
local vm = Data.ItemVm.new()
local idx = vm:getNumber("index")
if idx then idx.value = i end

local inst = self.template:instance(vm)
inst.frameOrigin = false
table.insert(self.instances, inst)
end

return true
end

return function(): Node<Spawner>
return {
init = init,
advance = advance,
draw = draw,
template = late(), -- assigned by the editor
instances = {},
}
end

Editor setup:

  1. Create a component artboard (the thing you want to repeat)
  2. Attach the script to a node
  3. In the script's input panel, assign the component to template

Input<Trigger>

A one-shot signal — like pressing a button. Unlike all other input types, a trigger doesn't hold a value. It fires once and is done.

When to use: Reset buttons, "play" signals, "spawn now" commands — any action that should happen once when activated.

Factory default: function() end — a function that does nothing. This is different from other inputs: triggers must be functions because Rive calls the function directly when the trigger fires. Using late() or nil causes the error "expected trigger X to be a function".

How it works: Rive calls your function the moment the trigger fires. You can override this function in init() to do something useful:

export type Resettable = {
resetTrigger: Input<Trigger>,
pendingReset: boolean,
score: number,
}

function init(self: Resettable, context: Context): boolean
-- Replace the do-nothing placeholder with a real function
self.resetTrigger = function()
self.pendingReset = true
end
return true
end

function advance(self: Resettable, seconds: number): boolean
if self.pendingReset then
self.pendingReset = false
self.score = 0 -- reset the score
end
return true
end

return function(): Node<Resettable>
return {
init = init,
advance = advance,
draw = draw,
resetTrigger = function() end, -- placeholder
pendingReset = false,
score = 0,
}
end

Execution order on a trigger frame: trigger fn → advance()update().

Editor setup:

  1. Add a Trigger property to your ViewModel
  2. In the script's input panel, bind resetTrigger to the ViewModel trigger
  3. Fire it from a state machine, listener, or another script

See Trigger Inputs for a deeper walkthrough of the flag pattern and why it's useful.

Triggers can also be accessed via vm:getTrigger("name") with addListener(callback) — see PropertyTrigger.


Change Handling

When any input changes, Rive calls your update() function. This is where you react to new values — rebuild geometry, update colors, recalculate layout.

function update(self: MyNode)
-- Called whenever any input changes
-- Rebuild whatever depends on the inputs
rebuildShape(self)
end

update() does not tell you which input changed. If you need to know, store the previous value and compare:

function update(self: MyNode)
if self.size ~= self.lastSize then
rebuildPath(self)
self.lastSize = self.size
end
end

See Also: Property, ViewModel


Property

Mutable property with change notification.

Attributes

property.value

The property value. Type: T (read/write)

Methods

property:addListener(callback)

Registers a change callback. Can be called with or without a bound object.

property:addListener(callback: () -> ())
property:addListener(obj: any, callback: (any) -> ())

property:removeListener(callback)

Removes a callback.

property:removeListener(callback: () -> ())

See Also: Input, ViewModel


Context

Runtime context passed to init() in all script protocols.

Methods

context:viewModel()

Gets the main Artboard's ViewModel.

context:viewModel(): ViewModel?

context:rootViewModel()

Gets the root ViewModel of the artboard hierarchy.

context:rootViewModel(): ViewModel?

context:dataContext()

Gets the DataContext for hierarchy traversal.

context:dataContext(): DataContext?

context:image(name)

Gets an image asset by name.

context:image(name: string): Image?

context:blob(name)

Gets a blob (binary data) asset by name.

context:blob(name: string): Blob?

context:audio(name)

Gets an audio source asset by name.

context:audio(name: string): AudioSource?

context:markNeedsUpdate()

Requests an update on the next frame.

context:markNeedsUpdate()

See Also: ViewModel, DataContext, Image, Blob, AudioSource


ViewModel

Connects editor elements to data and code. Provides access to bound properties by type.

Attributes

viewModel.name

The name of the ViewModel. Type: string (read-only)

Methods

viewModel:getNumber(name)

Gets a numeric property by name.

viewModel:getNumber(name: string): Property<number>?

viewModel:getString(name)

Gets a string property by name.

viewModel:getString(name: string): Property<string>?

viewModel:getBoolean(name)

Gets a boolean property by name.

viewModel:getBoolean(name: string): Property<boolean>?

viewModel:getColor(name)

Gets a color property by name.

viewModel:getColor(name: string): Property<Color>?

viewModel:getTrigger(name)

Gets a trigger property by name.

viewModel:getTrigger(name: string): PropertyTrigger?

viewModel:getList(name)

Gets a list property by name.

viewModel:getList(name: string): PropertyList?

viewModel:getViewModel(name)

Gets a nested ViewModel by name. Returns a PropertyViewModel wrapper (access the ViewModel via .value).

viewModel:getViewModel(name: string): PropertyViewModel?

viewModel:getEnum(name)

Gets an enum property by name.

viewModel:getEnum(name: string): PropertyEnum?

viewModel:instance()

Creates an independent instance of the ViewModel.

viewModel:instance(): ViewModel

Example

function init(self: MyScript, context: Context): boolean
local vm = context:viewModel()

-- Get typed properties
local score = vm:getNumber("score")
local playerName = vm:getString("playerName")
local isActive = vm:getBoolean("isActive")

-- Read values
if score then
print(`Score: {score.value}`)
end

-- Listen for changes
if playerName then
playerName:addListener(function()
print(`Name changed to: {playerName.value}`)
end)
end

return true
end

See Also: Property, Context, PropertyTrigger


DataContext

Provides hierarchy traversal for data binding contexts. Obtained via context:dataContext().

Methods

dataContext:parent()

Gets the parent DataContext, or nil if at the root.

dataContext:parent(): DataContext?

dataContext:viewModel()

Gets the ViewModel associated with this context level.

dataContext:viewModel(): ViewModel?

See Also: Context, ViewModel

Next Steps