AnimaScript is Rive’s language for fast scripts. Write
.as files in a Rive project and rive compiles them to
WebAssembly as you save. AnimaScript began as AssemblyScript and adds what Rive
content needs: structs, closures, SIMD, threads and real time audio. The result
runs everywhere Rive runs, on the browser’s own engine on the web and as
compiled machine code on native platforms.
struct Vec2 {
x: f32;
y: f32;
}
export function reflect(v: Vec2, n: Vec2): Vec2 {
// Structs never allocate: a Vec2 is two f32 values held
// in locals, and it returns as two results.
const d: f32 = 2.0 * (v.x * n.x + v.y * n.y);
let r = Vec2();
r.x = v.x - d * n.x;
r.y = v.y - d * n.y;
return r;
}
export function demo(): f32 {
let v = Vec2();
v.x = 3;
v.y = -1;
let n = Vec2();
n.y = 1;
return reflect(v, n).y; // the bounce: 1
}
Types are real. An i32 is a 32 bit integer and an f32
stays in a register, so nothing sits between your arithmetic and the hardware: no
boxing, no tagged values, no garbage on the hot path.
Plain data that copies like a number and never allocates. A
Vec2 is two f32 values in registers, and
body.transform.p.x compiles to one load at a fixed
offset.
Functions capture the locals around them, safe to keep in fields and return. Upstream AssemblyScript never shipped them.
closures →Move heavy work off the frame. A @job function runs on a
thread pool. A @worker lives across frames, talks through
ports and shared memory, and can meet other workers at a barrier.
Render sound sample by sample on the audio thread, fed by rings and snapshots from your script. A processor renders the same samples on every platform, so a recording makes a reliable test.
audio →Four lanes per instruction with the f32x4 and
i32x4 builtins, on by default. Keep a scalar path beside
it for browsers without SIMD, from the same source.
Published builds inline small functions and turn a
new that never escapes into plain locals, so code that
makes a temporary object per step does not pay for the
allocation.
Box2D physics, Google’s Draco mesh decoder and a physical model piano, each ported to AnimaScript and checked against the original.
Luau is deprecated. Luau scripts keep running on every Rive runtime, but new scripting features land in AnimaScript, and threads and audio processors already exist only there. Start new scripts in AnimaScript and move existing ones as you work on them. Both languages implement the same protocols with the same hooks, so a script keeps its shape when it moves.
| Luau | AnimaScript | |
|---|---|---|
| Types | gradual: values carry their type while the script runs | static: an i32 is a 32 bit integer and a struct is plain
data |
| On the web | Luau’s virtual machine, itself compiled to WebAssembly, interprets the bytecode | runs directly on the browser’s WebAssembly engine |
| Natively | Luau’s virtual machine interprets the bytecode | machine code compiled ahead of time where the platform allows it, an interpreter elsewhere |
| Threads | none | jobs, workers, shared memory and barriers |
| Audio | plays sounds | plays sounds, and processors render samples on the audio thread |
The same effects, written once in each language, in milliseconds of script time per frame as published, measured on an Apple M-series Mac:
| rive-cli sample | Luau | AnimaScript, compiled | AnimaScript, interpreted |
|---|---|---|---|
text_rain | 2.04 | 0.37 | 1.60 |
text_string | 0.30 | 0.03 | 0.26 |
text_island | 0.17 | 0.12 | 0.18 |
Where a script does its own math, compiled AnimaScript runs 5 to 10 times faster than Luau. Where it mostly calls into Rive to draw, both spend their time in the same engine code and the gap narrows. Even on an interpreter AnimaScript keeps level with Luau. On the web the gap grows, since Luau’s virtual machine runs there 1.4 to 2.1 times slower than natively, while AnimaScript runs on the browser’s engine about as fast as compiled native code. The one cost runs the other way: a module carries the parts of the standard library it uses, so the smallest scripts ship larger than their Luau bytecode.
The same WebAssembly module runs on the browser’s own engine on the web and as compiled machine code natively, with an interpreter where a platform cannot generate code. Every call runs under a time budget, so a runaway loop stops with an error instead of freezing the app.