We love contributions! If you want to run the project locally to test out changes, run the examples, or just see how things work under the hood, read on below.
There are a lot of facets that this runtime exports, so make sure you read up below on the Background section first.
This runtime is driven by a lower-level rive-runtime runtime that defines an abstract renderer. There are two main variants of this runtime, separated into different NPM packages at build and publish time:
The methods and structs defined in rive-runtime that provide the base of this rive-wasm runtime are compiled and bound to JS classes and functions via Web Assembly(WASM) through a tool called Emscripten. You'll notice this in the wasm/src/
folder.
For each of the variants above, there are two sets of APIs to use for rendering Rives in web applications:
- High-level JS API - in the
js/
folder, defining high-level class API's most consumers will use when displaying Rives in web applications.- Use cases:
- Simple displaying of animations or state machines
- Basic control over state machines (i.e grabbing references to state machine inputs to drive in the web app)
- Use cases:
- Low-level API - in the
wasm/
folder, defining Emscripten bindings that expose lower-level C++ functionality into generated JavaScript classes and functions. These exposed classes include pieces likeArtboardInstance
,LinearAnimationInstance
, andStateMachineInstance
classes, and many more that help users build a render loop themselves to display Rives. This allows users to instance multiple artboards, state machines, etc. in one render loop and canvas to get granular control over how scenes operate.- Note: Most people will not use this functionality, but this may the solution to common questions like:
- How can I dynamically combine multiple files/artboards to create a scene in one canvas?
- How can I control the speed of animations?
- How can I control hit detection of certain nodes?
- How can I get coordinates of certain nodes or bones at any given point in the animation cycle?
- The WASM compiled from the underlying runtime layer can be used with any concrete renderer - in case users want to hook it up themselves
- Note: Most people will not use this functionality, but this may the solution to common questions like:
- Clone the project down
cd
into thejs
folder from the top level of the project, and runnpm i
to install all dependencies- Pull down the underlying
rive-runtime
submodule, which should be pointing to a specific commit.
# First time through if you just pulled down the project
git submodule update --init --recursive
# When updating the submodule after the initial pull
git submodule update --recursive
cd submodules/rive-runtime
git checkout origin/master
cd ../../..
-
Install Emscripten. We build against
v3.1.43
in rive-wasm, so make sure to install and activate that version. -
Install Premake5 and add it to your PATH
-
cd
back into thejs
folder and run./build.sh
from your terminal/shell to build the latest WASM and builds for JS API's (high and low level) into thenpm/
folder. This may take some time, grab a coffee! This should finish with Webpack building the JS bundles for the high-level API packages (more on that below)
If you want to work on the exposed high-level API, cd
into the js/src
folder at the top level.
There are two main files to be concerned with when making changes:
rive.ts
- The main file defining the API exposed to consumers- Loads the WASM file that powers this runtime under the hood
- Defines the
Rive
class which allows for instantiating the Rive file, loading animations, state machines, and exposing of methods to control such things - Exports funcionality above, as well as some types for TypeScript consumers
rive_advanced.mjs.d.ts
- The main types definition file for low-level API- Defines types for the low-level API classes and functions
- These are exported in the high-level API packages, as well as low-level ones too
There are some utils defined in the src
folder as well. When working on the main JS runtime, you'll probably be working in the rive.ts
file. When you make changes, you can create a new build by simply running the following command inside the js/
folder:
# Webpack creates the build
npm run build
There are a few example projects that use the high-level API and reference the local builds. Use these projects to help test any changes made to ensure no breaking functionality.
cd
intojs/examples/_frameworks/parcel_example_canvas
to run a simple Rive gallery app- Run
npm i
inside that parcel-based project to install the dependencies for the example app - Run
npm start
and navigate tohttp://localhost:1234
where the server runs - Any changes made to the example app will reflect on save. Feel free to integrate other Rives to display in this gallery app.
In this project, it references the local version of @rive-app/canvas-single
which is in js/npm/canvas_single
. The *-single
version packages are builds that have the WASM encoded inside the JS of the bundle, rather than a separate file inside the bundle, as seen in the non-single version packages (i.e @rive-app/canvas
). The *-single
versions are used in the example applications for ease of setup and debugging.
If you want to work on the underlying JS classes and methods exposed through the compiled WASM, cd
into the wasm/
folder at the top level.
To check out the Emscripten bindings from cpp to JS, look at the wasm/src/bindings.cpp
file (bindings_c2d
for canvas-specific bindings, bindings_skia
for WebGL specific bindings, and bindings_webgl2.cpp
for WebGL2 specific bindings needed for the Rive Renderer).
The files in wasm/js
(specifically renderer.js
, skia_renderer.js
, and webgl2_renderer.js
) use the Canvas2D and WebGL renderers respectfully to expose and implement bindings for drawing to the canvas.
If you're looking to alter or add bindings as a result of a change or new feature in rive-runtime, you'll work with these files (most likely starting with the bindings.cpp
first). When making changes, you will have to re-build WASM to test them out in an app (js/build.sh
script). When changing or adding low-level API functionality for the high-level API, ensure that the js/src/rive_advanced.mjs.d.ts
types file is updated accordingly. This ensures types are up-to-date with any changes.
The build.sh
script makes all the WASM submodules for different packages; this can be a long process. If you're iterating on bindings or even cpp runtime submodule files, you can make it faster by doing the following things:
- In
wasm/build_all_wasm.sh
:- Comment out the line where it makes Skia (if you already ran this script and built it)
- Comment out the lines where it calls
./build_wasm.sh release
on the packages you're not testing against (i.e only want to iterate on Canvas2D instead of WebGL)
There are a few example projects that use the low-level APIs and reference the local builds.
cd
intowasm/examples/parcel_example
to run a simple Rive gallery app similar to the high-level example app, but constructing a render loop manually- Run
npm i
inside that parcel-based project to install the dependencies for the example app - Run
npm start
and navigate tohttp://localhost:1234
where the server runs - Any changes made to the example app will reflect on save. Feel free to integrate other Rives to display in this gallery app.
In this project, it references the local version of @rive-app/canvas-advanced-single
which is in js/npm/canvas_advanced_single
. The *-single
version packages are builds that have the WASM encoded inside the JS of the bundle, rather than a separate file inside the bundle, as seen in the non-single version packages (i.e @rive-app/canvas
). The *-single
versions are used in the example applications for ease of setup and debugging.
The C++ code is formatted using ClangFormat. The JS code is formatted using Prettier. Configuration files are included for both. VSCode will pick them up directly if you have the Prettier and ClangFormat extensions installed.
We also have a suite of unit tests against the high-level API. When adding or changing functionality, add a test in the js/test/rive.test.ts
if necessary.
To run the test suite:
# Top level of the project
cd js
npm test
When you're ready to make changes, push up to a feature branch off of the master
branch. Create a pull request to this repository in Github. When creating commit messages, please be as descriptive as possible to the changes being made.
For example, if the change is simply a bug fix or patch change:
git commit -m "Fix: Fixing a return type from computeAlignment"
Or if it's simply a docs change:
git commit -m "Docs: Adding a new link for another example page"
For minor/major version releases, also ensure you preface commit messages with:
git commit -m "Major: Restructuring the useRive API with new parameters"
These messages help make the changelog clear as to what changes are made for future devs to see.
When changes are merged and releaesd, they are built and deployed to all the variant rive-wasm packages, which you can find here.
You can find the deploy scripts in .github/
to see the logic for deploys.
Many times, fixes to the runtime and feature adds come from the underlying cpp runtime and are propagated to this repository. In these cases, you can test these changes by running the js/build.sh
script to incorporate the fix/feature across the various builds. Check out the example apps to make sure nothing is broken (or to verify a fix) before making a release.