start

Introduction

rasc compiles a variant of TypeScript — Rive AssemblyScript — to WebAssembly, and runs it on every Rive runtime.

rasc is a self-contained C++ compiler. There is no Node toolchain, no npm install, no bundler: one binary takes .as sources and emits a WebAssembly module, typically in 200–500 ms for a project-scale build. The language is AssemblyScript — TypeScript syntax over WebAssembly types — extended with the features Rive content actually needs: closures, value classes, allocation sinking, SIMD, and a versioned host binding surface.

fib.as
export function fib(n: i32): i32 {
    let a = 0, b = 1;
    for (let i = 0; i < n; i++) {
        let t = a + b;
        a = b;
        b = t;
    }
    return a;
}

Sources use the .as extension. If it compiles with upstream asc, rasc produces a byte-identical module by default — every rasc extension is additive, and every optimization is opt-in.

From a WebAssembly perspective

Like upstream AssemblyScript, rasc gives you WebAssembly’s type system directly: i32, i64, f32, f64, and with SIMD enabled, v128. Numeric code compiles to the instructions you would write by hand — an f32 stays in a wasm local, register-resident end to end. This is the structural reason rasc code is fast: there is no tagged value, no interpreter register file in linear memory, no boxing between your arithmetic and the hardware.

From a JavaScript perspective

You also get a familiar standard library — Array<T>, String, Map<K,V>, StaticArray<T>, typed arrays, Math — with a garbage-collected managed heap when you want it and a bump-allocator stub runtime when you don’t. rasc goes further than upstream here: functions capture enclosing locals (real closures, upstream’s oldest open request), and struct declarations give you C-struct semantics inside the TypeScript syntax you already know.

Where it runs

A compiled module is engine-neutral wasm. Rive runs it four ways, chosen per platform rather than per rewrite:

See Execution Lanes for the measured story.

Frequently asked questions

Is rasc a fork of AssemblyScript?

It is a from-scratch C++ implementation of the language, not a patched copy of the Node compiler. Compatibility is a hard rule — the test suite sweeps fixtures across runtimes and optimization levels for byte-identical default output — but the implementation owns its whole pipeline, which is what makes extensions like closures and value classes tractable.

How is this different from TypeScript?

Same syntax, different discipline. Types are real: an i32 is a 32-bit integer, not a number that hopes. There is no any, no runtime type juggling, no DOM. Code that type-checks compiles to static wasm with no interpreter underneath.

How is this different from Luau scripting in Rive?

They are two compilers targeting the same substrate — WAMR is the only runtime on native, and both languages answer the same module ABI. Luau is the drop-in tier for existing content and dynamic scripting; rasc is the C-class lane for numeric-heavy work. A .riv file can carry both, one module per language per file.

Can I bring existing C or C++ code?

Porting is the intended path, and it is pleasant: the Draco and Box2D ports mirror their C sources closely — value classes were designed so the free-function C style lands 1:1 — and both ship with differential harnesses proving bit-exact parity against the reference binaries.

Why "rasc"?

Rive AssemblyScript Compiler. The CLI is rasc, sources are .as, and the name stays lowercase — it is a tool, not a brand.