rasc · rive assemblyscript

TypeScript syntax. C-class speed. Every Rive runtime.

rasc is Rive’s AssemblyScript compiler — a single self-contained C++ binary that compiles .as sources to WebAssembly in a few hundred milliseconds. It adds the things upstream never did — closures, value classes, allocation sinking — and runs the result everywhere Rive runs: the browser engine on web, a fast interpreter for instant iteration, ahead-of-time machine code for shipping.

Get started Read the docs
bounce.as
struct Vec2 {
    x: f32;
    y: f32;
}

export function reflect(v: Vec2, n: Vec2): Vec2 {
    // Value classes never allocate: a Vec2 is two f32 leaves
    // living in wasm locals, returned via multi-value.
    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
}
language

The AssemblyScript you know, plus the parts it was missing.

rasc stays additive: sources that compile with upstream asc produce byte-identical modules. New syntax only ever adds capability.

Value classes

Plain-old-data classes that flatten to wasm locals and stack values. Copy semantics, zero allocation, one-load field access when embedded — body.transform.p.x compiles to a single load at a constant offset.

value classes →

=> Closures

Functions capture enclosing locals — the feature upstream AssemblyScript famously never shipped. Captures snapshot by value into GC-traced context objects, safe to escape through fields and returns.

concepts →

(a, b) Multi-value returns

Value classes return as native wasm multi-value tuples — every size, no hidden out-pointers. -O2 enables the feature in wasm-opt only when a signature actually carries several results.

multi-value →

-O Allocation sinking

Non-escaping new scalarizes into locals automatically. Naive object-per-frame code runs 12× faster on the interp tier with no hand-optimization.

compiler →

v128 SIMD & relaxed SIMD

Opt-in wasm SIMD with f32x4/i32x4 builtins and relaxed fused multiply-add. One source, two artifacts — output stays byte-identical when the feature is off.

flags →

rive_*_v1 Host bindings

Versioned import namespaces are the protocol. One IDL generates the C header, WAMR natives, JS stubs, and the AS projection — and unused bindings tree-shake away per module.

host bindings →
performance

The fastest lane in every cell it occupies.

Measured on real ported workloads — Draco mesh decode, Box2D solving, the Fields app — against native Luau and the shipping Luau web lane.

~8.7×
faster than native Luau on Draco decode, compiled ahead of time with wamrc
1.38×
from full native C with SIMD, on the Box2D pyramid benchmark
~300 ms
edit-to-running: rasc compiles project-scale sources in 200–500 ms, instantiates in ~1 ms
23.5 KB
gzipped Draco decoder module — versus 286 KB plus JS glue for the reference build
runtime

One module, four ways to run it.

The same wasm module runs on the browser engine, the WAMR fast interpreter, and wamrc AOT — pick per platform, not per rewrite. The editor iterates on interp instantly and swaps in optimized AOT bits in the background.

Execution lanes