Rigby2DDocs

Shipping

Runtimes

Rigby2D animations play in games and apps through the included runtimes. All of them load skeleton .json (3.8, 4.2, 4.3) or binary .skel (4.3) with an .atlas, and evaluate meshes, weights, skins, image sequences, clipping, IK, transform, path, physics and slider constraints, draw order, events and layered tracks with crossfades.

RuntimeLanguageStatus
JavaScript (src/core) + WebGL renderer (src/webgl)JavaScript, no dependenciesComplete
Rust (runtimes/rust/rigby-runtime)Rust, no dependencies; 3 to 4× fasterComplete
Godot (RigbySkeleton2D)GDExtension on the Rust corePreview
UnityOn the Rust corePlanned

The landing page of the website plays NILA with the JavaScript runtime.

#JavaScript

Copy src/core/ into your project (and src/webgl/ for a ready-made renderer):

import { AnimationState, Pose, parseAtlas, readSkeletonJson } from './core/index.js';
import { WebGLRenderer } from './webgl/renderer.js';

const { skeleton } = readSkeletonJson(JSON.parse(jsonText), 'hero'); // hero.json
const pose = new Pose(skeleton);
const state = new AnimationState();
state.play(skeleton, 'walk', true, 0.2);              // track 0, 0.2 s crossfade
state.setAnimation(skeleton, 1, 'shoot', false, 0.1); // track 1 layers on top
state.addAnimation(skeleton, 0, 'idle', true, 0.3);   // queued after walk

const renderer = new WebGLRenderer(gl);
const textures = renderer.atlasTextures(parseAtlas(atlasText), { 'hero.png': pageImage });

function frame(dt) {
  state.update(skeleton, dt);
  state.apply(skeleton, pose);
  renderer.begin(canvas.width, canvas.height, { x: 0, y: 200, zoom: 1 });
  renderer.drawSkeleton(skeleton, pose, textures);
  renderer.end();
}

For .skel files use readSkeletonJson(readSkeletonBinary(bytes), 'hero'). With another renderer, draw pose.quads(skeleton): world-space positions (Y up), UVs, tint and blend mode per region and mesh. examples/webgl.html is a small player (npm run web:dev, then open /examples/webgl.html).

#Events

state.addListener({
  start: (entry) => {},
  complete: (entry) => {},
  event: (entry, e) => playSound(e.audio ?? e.name, e.volume), // { name, time, int, float, string, audio, volume, balance }
});

Physics simulates while AnimationState updates; pose.resetPhysics() starts it over.

#Rust

use rigby_runtime::{load_skeleton, Atlas, AnimationState, Pose, RegionLookup, RenderOutput};

let data = load_skeleton(&std::fs::read("hero.skel")?)?;
let atlas = Atlas::parse(&std::fs::read_to_string("hero.atlas")?);
let lookup = RegionLookup::new(&data, &atlas);
let (mut pose, mut state, mut out) = (Pose::new(&data), AnimationState::new(), RenderOutput::default());
state.play(&data, "walk", true, 0.2);

// Every frame:
state.update(&data, dt);
state.apply(&data, &mut pose);
out.build(&data, &pose, &lookup, [1.0; 4]);
for batch in out.batches() { /* positions, UVs, colors, indices for atlas page `batch.page` */ }

It matches the JavaScript runtime frame by frame (golden tests). Build and test with cargo test --release --workspace in runtimes/rust. See the runtimes README.

#Godot

The RigbySkeleton2D node for Godot 4.3+ (preview; prebuilt for Windows x64, build from source elsewhere). Install the rigby2d-godot-addon zip from the Releases into your project, add the node, and set Skeleton File, Atlas File and Animation.

@onready var hero: RigbySkeleton2D = $Hero

func _ready() -> void:
	hero.animation_event.connect(_on_event)
	hero.play("walk", true, 0.2)
	hero.queue(0, "idle", true, 0.3, -1.0)

See the Godot guide for every property and method.

#Performance

One character, one frame (apply, constraints, world transforms and vertices), median of 2,000 frames:

RigJavaScriptRust
67 bones, 276 mesh vertices50 µs14.5 µs
76 bones, 667 weighted vertices, 9 IK71 µs22 µs
143 bones, 879 vertices, 21 transform/path constraints90 µs22 µs

Edit this page on GitHub