Using low-level JS APIs to construct Rive scenes.
<canvas>
element. This is useful if you’re building a game!Load the Rive Web Assembly (WASM) file, which contains the module with lower-level APIs
Load the Rive file in
Create instances for Artboards, LinearAnimations, and StateMachines
Build the render loop function to manipulate the instances created above
Advance any animation instances and apply it
Advance any state machine instances
Advance the artboard
Render the updated artboard on the canvas
Request the next animation frame
Clean-up created instances when finished
@rive-app/canvas-advanced
or @rive-app/webgl2-advanced
libraries (by default, we recommend @rive-app/canvas-advanced
for a smaller dependency, unless you need to use WebGL2). When the WASM file is loaded into your app, you’ll gain access to necessary APIs such as the renderer for canvas/WebGL, along with relevant JS classes generated from underlying CPP bindings via rive-cpp, the core c++ runtime used as the base for several other Rive runtimes. You’ll use these classes to construct your rendering scene in the canvas below.
You can load the Rive WASM file via unpkg (hosts our NPM modules for the JS runtimes), which will make a network call to the CDN, or you can choose to host the WASM file on your own servers. With unpkg
, the URL will look something like this:
https://unpkg.com/@rive-app/canvas-advanced@2.26.1/rive.wasm
locateFile
, which is a function that returns the URI of the WASM file. This can be either the unpkg
URL or the URI to your self-hosted version of it. Simply await
for the call to resolve, and then you’ll get a reference to the low-level Rive runtime APIs.
makeRenderer()
API and pass in the canvas element on which Rive should render. The renderer draws Rive onto the <canvas>
element with a rendering context. If you’re using @rive-app/canvas-advanced
, it will create a Canvas2D rendering context. If you’re using @rive-app/webgl2-advanced
, it will create a WebGL rendering context.
load()
API. You can fetch this at a URL or from somewhere within your project.
.load()
call, as it synchronously tries to load assets from the File
. Additionally, pass in the ArrayBuffer
to a Uint8Array
view before sending it as a param to .load()
File
object, you can begin instancing all the artboards, state machines, and linear animations from the Rive file. Instancing creates an underlying CPP reference and allows you to control how each entity advances over time. More on that further down this guide.
The main components you will most likely want to instance are:
Artboard
- Instance 1 or more artboards from the Rive file you want to drawStateMachineInstance
- Instance a state machine from a given artboardLinearAnimationInstance
- Instance a single timeline animation from a given artboardrequestAnimationFrame
(rAF) to build animations frame-by-frame in between the browser’s repaint cycle. If not, check out this guide as a starting point for building a render loop.
In the case of a Rive render loop, you’ll be using a custom Rive API that wraps rAF, so you’ll need to use rive.requestAnimationFrame()
as well as rive.cancelAnimationFrame()
. The structure should be similar to any other rAF loop you build for other animations, but you’ll be advancing the instances you created above and aligning the artboard to the canvas as you see fit.
Start by creating your callback loop for the rAF cycle and tracking the last time since the previous rAF callback to get an elapsed time in seconds. Then, clear the canvas by using the renderer’s .clear()
API.
LinearAnimationInstance
has a set of keyframes to apply to objects in an artboard. In the render loop, you’ll want to call .advance()
on the created animation instances to get those keyframes and, like the API is named, advance the animation by a certain amount of time (in seconds).
.apply()
call. When the animation applies the interpolated values from the keyframes, it blends these values with the current values on the artboard objects. This allows you to “blend” into an animation, which is helpful if you have two animation instances applying a keyframe value on the same property of an object. The default mix value to replace the old property values with the new keyframe values should be 1
.
After applying an animation’s values to the artboard, advance the artboard (more on that below) to update the artboard’s objects and resolve the property value changes.
To summarize all of this, the order of operations in advancing a linear animation is as follows:
StateMachineInstance
is similar to the LinearAnimationInstance
flow above, with a few differences. With state machines, you don’t need to apply a mix value since you should only have one state machine instance correlated to an artboard, and mix values are determined by the transitions set between timeline animations. Additionally, the .advance()
method updates the properties of objects on the artboard. Therefore, the order of operations for advancing a state machine is simplified to:\`
save()
API on the rendering context to save the state of the canvas. Then call the align()
API on the context to provide:
Fit
and Alignment
valuesFit
and Alignment
. For the latter two parameters, provide an axis-aligned bounding box (AABB). See the snippet below for an example of the align()
API.
Finally, after calling the align()
API, pass the renderer to the artboard via the draw()
method to draw the artboard on the canvas, then end with a call to the restore()
API on the renderer to restore the saved state of the canvas.
@rive-app/webgl2-advanced
, you will need to call renderer.flush()
to empty different buffer commands.requestAnimationFrame
with this callback to queue up the next callback for the next frame.
Altogether, this looks like the following:
.delete()
API on any instances created from the Rive runtime when they are no longer needed. An example is shown below:
.play()
, .pause()
, .stop()
, etc.onStateChange
, onLoad
, etc. that would allow you to hook into specific Rive lifecycle eventsrequestAniationFrame()
) and do not want to use the Rive-wrapped requestAnimationFrame()
API, you can do so with an extra API call at the end of your render loop. Call the rive.resolveAnimationFrame()
API at the end of the render loop before calling requestAnimationFrame()
again.
See more on usage in the Rive Parameters doc.