Build Settings
What rive.yaml and the rive command change
about how your scripts compile.
Debug and optimized builds
The live window, rive <dir>, builds your scripts for debugging.
Every line carries a probe the debugger can stop on, every loop carries a fuel
check so a runaway script stops at its time budget, and every throw keeps its
message. That makes tight loops run several times slower than they ship, and the
log says so the first time.
The other builds are optimized. They inline small functions, turn a
new that never escapes its function into plain locals, and leave the
debugger’s probes out:
| Command | What it builds |
|---|---|
rive <dir> |
the debug build, in a window that rebuilds on every save |
rive <dir> --optimize |
an optimized build that keeps throw messages; use it for timing and long tests |
rive <dir> --bench |
the scripts a publish ships, timed frame by frame |
rive <dir> --publish, rive build |
the scripts a published file ships: optimized, without throw messages |
Jobs, workers and audio processors are always optimized, even in the live window, since nothing can step into them with the debugger. See Threads.
Natively, rive runs your module as machine code compiled ahead of
time whenever it can, and on an interpreter otherwise. The log names the one each
job, worker and processor module runs on, such as
processors module tier: interp. Headless runs, such as
--test or --render-audio, take --aot to ask
for compiled code too.
See Where Scripts Run.
rive.yaml
These keys shape how AnimaScript builds. rive docs project/rive-yaml
has every key.
| Key | Values | What it does |
|---|---|---|
scripting | wasm |
Required for .as files. Without it every .as file
fails the build. |
wasmSimd | on (default), off,
relaxed |
Which WebAssembly SIMD the module may use. off builds a module
that also runs in browsers without SIMD, such as Safari before 16.4.
relaxed also allows fused multiply-adds, whose results can differ
by platform. See SIMD. |
wasmFuel | true, false (default) |
Adds fuel checks to optimized builds too, so browsers can stop a runaway script at its time budget. They cost a few percent in loops and more in deep recursion. |
dangerouslyFast | true, false (default) |
Compiles the time budget out of every build. A runaway script then hangs
any host, so only use it for trusted content. It cannot be combined with
wasmFuel. |
defines | a map of names to integers or booleans | Build constants; see below. |
audioBuffer | 16 to 8192 frames | The live window’s audio device buffer. A bigger buffer keeps a heavy audio processor from dropping out, at the cost of latency. See Audio Processors. |
A library’s scripts can also compile differently from your own while you
preview: its scriptMode picks whether the debugger steps into it and
how its runaway loops are stopped. See rive docs project/rive-yaml.
Execution budgets and fuel are covered in Where Scripts Run.
Build defines
A build define is an integer constant the compiler knows while it compiles.
Set one with a defines map in rive.yaml, holding integers
or booleans, or with --define=NAME[=n] on the command line, where a
name without a value is 1. The command line wins over the file.
name: game
scripting: wasm
defines:
PIN_TIER: 2
NO_FOG: true
rive . --define=PIN_TIER=2 --define=NO_FOG
A define is not a global variable. The compiler answers for it in two places:
isDefined(NAME)is true when the define is set, or when your source declares something by that name: a local, global, function or class. Enums, namespaces and type aliases do not count.- A use of
NAMEthat your source never declares compiles to the constant. Inside a condition it folds like a literal, short circuits included, soisDefined(X) && X > 1never compiles the use ofXwhenXis not set. A use of a name that is neither set nor declared is an unknown identifier.
An if on a constant never compiles its dead branch, so a define
removes a whole feature from the module rather than skipping it at run time:
export function tier(): i32 {
if (isDefined(PIN_TIER)) {
return PIN_TIER; // --define=PIN_TIER=2 folds this to 2
}
return adaptiveTier();
}
export function draw(): void {
if (!isDefined(NO_FOG)) { // --define=NO_FOG compiles the pass out
drawFog();
}
}
A local, global or function with the same name hides the define, captured
locals included, so a define only stands in for names your source never
declares. A class of that name leaves the define in force. Arithmetic on a
define wraps at 32 bits like the code it replaces. Prefix define names, as in
PIANO_BLOCK, so they never collide with a name the standard
library declares.
Compiler constants
A few constants come from the build itself rather than from your defines. They fold the same way, which is how one source serves several builds:
| Constant | Value |
|---|---|
ASC_FEATURE_SIMD |
1 when wasmSimd is on or relaxed,
0 when it is off |
ASC_FEATURE_RELAXED_SIMD |
1 when wasmSimd is relaxed, 0 otherwise |
ASC_DEBUG |
1 in the live window’s debug build, 0 in optimized builds and in jobs, workers and audio processors |
export function tick(): void {
if (ASC_DEBUG) {
// Only in the debug build; optimized builds drop the branch.
checkInvariants();
}
}
Other ASC_ names from upstream AssemblyScript, such as
ASC_TARGET, compile but are always 0 and never fold, so an
if on one keeps both branches. Gate code on the constants above or on
your own defines.