spine2d 0.3.0

Pure Rust runtime for Spine 4.3 (unofficial)
Documentation

spine2d

Experimental, pure Rust runtime for Spine 4.3 (unofficial).

Important notice

  • This project is unofficial and is not affiliated with Esoteric Software.
  • This is a pure Rust runtime, not an FFI binding to the official spine-c / spine-cpp runtimes. Compatibility and behavioural parity are an explicit goal, but you should treat this as an experimental implementation and manage the integration risk accordingly.
  • If you need “official runtime parity + support” today, use the official runtimes (or a binding to them).

Goals

  • Pure Rust implementation (no C/C++ toolchain).
  • Works on wasm32-unknown-unknown (web) and native targets.
  • Core crate is renderer-agnostic; rendering integrations live in separate crates.
  • Drive parity via oracles and upstream tests (not “approximate compatibility”).

Non-goals (for now)

  • FFI bindings to the official runtimes (spine-c, spine-cpp).
  • Guaranteeing perfect behavioural parity with official runtimes from day one (we are iterating toward it, with an oracle-based workflow).

Project layout

  • spine2d: core runtime + parsing + renderer-agnostic render output.
  • spine2d-wgpu: wgpu renderer integration built on top of spine2d.
  • spine2d-bevy: Bevy Material2d backend built on top of the core runtime.
  • spine2d-web: Trunk + wgpu web demo/viewer (wasm32-unknown-unknown).

Assets / redistribution

  • This repository does not ship official Spine example exports by default.
  • Local dev scripts can download spine-runtimes and import example exports into assets/ for testing/demos. Make sure you comply with the Spine license terms for your usage and redistribution.
    • Import examples locally: python3 ./scripts/prepare_spine_runtimes_web_assets.py --scope tests

Optional upstream smoke tests (local data)

Some smoke tests can parse official exported example JSON files from the upstream spine-runtimes repository. These files are not committed here by default.

  • Fetch + import: python3 ./scripts/fetch_spine_runtimes_examples.py --mode json --scope tests
  • Import from an existing checkout: ./scripts/import_spine_runtimes_examples.zsh --mode json --scope tests
  • Run tests: cargo test -p spine2d --features json,upstream-smoke
  • Run tests (including .skel parsing): cargo test -p spine2d --features json,binary,upstream-smoke

Demo

wgpu demo (native)

  • Minimal UV test window: cargo run -p spine2d-wgpu --example basic --features json
  • Render upstream example skeletons (requires importing --mode export): python3 ./scripts/fetch_spine_runtimes_examples.py --mode export --scope tests; cargo run -p spine2d-wgpu --example spine_runtimes --features json -- spineboy run --speed 1.0
    • Note: the demo resets the skeleton to setup pose each frame before AnimationState::apply to avoid accumulated constraint drift.
    • Tip: the demo has an egui top bar to switch example/animation/skin and adjust speed/margin.

Web demo (wasm + Trunk)

  • Install target: rustup target add wasm32-unknown-unknown
  • Install Trunk: cargo install trunk --locked
  • Run dev server: cd spine2d-web && trunk serve
    • Note: the viewer can load local official exports if assets/spine-runtimes/web_manifest.json exists (generated by script; not committed).
    • Note: the web demo tracks devicePixelRatio and reconfigures the surface on resize.
    • Note: the demo auto-fits the initial bounds to the viewport (camera fit is computed once from the first frame).
    • Controls: Play/Pause, Restart, Fit, Speed, Animation, Skin.

Using official example exports locally

  • Prepare assets + web manifest: python3 ./scripts/prepare_spine_runtimes_web_assets.py --scope tests
  • Then run: cd spine2d-web && trunk serve
    • The demo will auto-detect assets/spine-runtimes/web_manifest.json and populate the Example dropdown.
    • Optional URL params: ?example=spineboy&anim=run

Bevy backend

The Bevy integration is published as the spine2d-bevy crate and tracks bevy 0.19. Install it with cargo add spine2d-bevy.

The Bevy examples can run with bundled demo assets, but the interactive viewer needs local Spine example exports for the full example/animation/skin switcher. Prepare those assets first when you want to test with real upstream data:

python3 ./scripts/prepare_spine_runtimes_web_assets.py --scope tests

The script writes ignored files under assets/spine-runtimes/, including assets/spine-runtimes/web_manifest.json. Without that manifest, the viewer falls back to the bundled demo assets.

Run the built-in Bevy examples:

  • cargo run -p spine2d-bevy --example basic
  • cargo run -p spine2d-bevy --example metadata
  • cargo run -p spine2d-bevy --example bounds
  • cargo run -p spine2d-bevy --example multi_spine
  • cargo run -p spine2d-bevy --example viewer

The viewer reads assets/spine-runtimes/web_manifest.json and lets you switch examples, animations, skins, playback state, speed, and camera fit from the keyboard.

The Bevy integration is centered on a public Spine component plus optional runtime control components:

use bevy::prelude::*;
use spine2d_bevy::{Spine, Spine2dPlugin};

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(Spine2dPlugin)
        .add_systems(Startup, setup)
        .run();
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn(Spine::new(
        asset_server.load("demo.json"),
        asset_server.load("demo.atlas"),
    ).with_animation("spin", true));
}

Add SpineAnimation or SpineSkin only when you want component-driven runtime control after spawn. Use SpineFlipY(true) when your project uses a Y-down space and you want the backend to mirror Spine geometry for you. Gameplay systems can also write SpineAnimationCommand messages to set, queue, or clear track animations without accessing internal runtime handles. Animation lifecycle and custom Spine timeline events are published as SpineAnimationEvent messages.

Use the public SpineReady marker or SpineLifecycleEvent messages to observe when an entity has an active runtime instance. SpineLifecycleEvent also reports when that instance is released because the component was removed, the entity was despawned, or a skeleton/atlas asset reload invalidated the runtime. The backend maintains a public SpineBounds component with the current Bevy local-space bounds for camera fitting, hit areas, and gameplay queries.

Coordinate note: spine2d-bevy forwards Spine-local positions into Bevy without an implicit Y flip by default. Bevy world space is also Y-up, so the backend stays convention-neutral unless you opt in to SpineFlipY(true). If a project uses Y-down or screen-space coordinates, attach SpineFlipY(true) to the entity or apply that conversion in the game's own transform/camera layer.

License

Dual-licensed under MIT OR Apache-2.0. See LICENSE-MIT and LICENSE-APACHE.

Spine license notice

Spine is developed by Esoteric Software. This project is not affiliated with them. Using Spine runtimes and Spine data generally requires a Spine Editor license. See the official license terms: https://esotericsoftware.com/spine-editor-license