spine_bevy
A Bevy 0.19 plugin that loads, animates, and draws Spine 4.3 skeletons. Each skeleton is an entity, drawn in 2D or 3D.
It builds on spine_runtime, an unofficial Rust port of the Spine runtime. This crate turns that runtime's output into Bevy meshes and materials, and has no unsafe code.
Built for Dead Money's own games and mostly written by AI agents under human direction.
You need a Spine Editor license
This crate's own code is MIT, but it depends on spine_runtime, a translation of Esoteric Software's spine-cpp under the Spine Runtimes License Agreement. Under Section 2 of the Spine Editor License Agreement:
- Every developer who builds software with this crate needs their own Spine Editor license, including to build and run the examples. Players of a game you ship don't need one.
- Include the Spine Runtimes License Agreement in the documentation or other materials that come with your product.
If you're unsure whether your use is covered, ask Esoteric Software.
This release reads Spine 4.3 exports, binary (.skel) or JSON, each paired with its .atlas. 4.2 exports won't load; re-export them from a 4.3 editor.
Quick start
[]
= "0.19"
= "0.2"
It needs Rust 1.99 or newer.
use *;
use ;
examples/spineboy_walk.rs is the runnable version. JSON exports load the same way, with SpineSkeletonJsonLoaderSettings.
Playing animations and skins
SpineSkeleton is a component, so change it from any system:
play(track, name, looping)starts an animation, andset_skin(name)switches skins. Both work before the asset finishes loading; they apply once it does.with_initial_animationandwith_initial_skinset them at spawn.available_animations()andavailable_skins()list what the rig has.time_scale,paused, andphysicscontrol playback.- For anything else, such as queuing animations or blending tracks,
animation_state_mut()andskeleton_mut()give you thespine_runtimeobjects directly.
To change something on the frame it should take effect, order your system .before(SpineSet::Tick).
Events
Animation events keyed in the editor (footsteps, hit frames) arrive as Bevy messages, tagged with the skeleton's entity:
use SpineKeyframeEvent;
SpineStateEvent carries playback events: an animation starting, being interrupted, ending, or completing a loop.
3D
Add SpineRender3d beside the skeleton and use a Camera3d. The rig draws as a flat, unlit plane you can place and rotate with its Transform:
commands.spawn;
2D and 3D skeletons can share one app. examples/spineboy_walk_3d.rs shows a full scene.
Atlases
The materials expect premultiplied-alpha textures. Most Spine exports include a *-pma.atlas / *-pma.png pair beside the regular one; point the loader at it, as in the Quick start. Without settings, the loader looks for an atlas named after the skeleton (dropping -pro, -ess, or -ios). For a straight-alpha atlas, premultiply the PNGs in your asset pipeline.
Known limitations
Blending happens in linear color space. Spine's renderers blend in gamma (sRGB) space; Bevy blends in linear space. Opaque slots match Spine, but additive and semi-transparent slots come out somewhat different: additive highlights look dimmer and overlapping translucent slots less saturated than in the Spine editor. Matching exactly would mean drawing skeletons into a separate gamma-space target and compositing the result.
Performance
Skeletons update in parallel. Each render command is one draw call, and the runtime merges adjacent attachments that share a texture, blend mode, and color, so spineboy draws in one. Measured under Bevy 0.18 on an i9-14900K, 50 skeletons cost about 0.25 ms of CPU per frame and 1000 about 2.75 ms; past roughly 500 on screen, render extraction and draw calls dominate the frame. cargo run --release --example spine_stress measures it on your machine.
Version compatibility
| Bevy | Spine | spine_bevy |
|---|---|---|
| 0.19 | 4.3 | 0.2 |
| 0.18 | 4.3 | f2a00bb |
| 0.18 | 4.2 | 8c7be8a |
Each Bevy minor needs its own spine_bevy release. The 0.18 rows are the last commits on that Bevy version; pair the 4.2 one with spine_runtime's v0.1.0 tag.
Examples
The examples use the rigs from Esoteric's spine-runtimes repository, cloned beside this one. That art is licensed separately and doesn't ship with this crate. A clone of this repo also builds against a spine_runtime checkout beside it.
spine_browseris a gallery of every example rig. Space / Shift+Space switch rigs, N switches animations, S switches skins, R restarts, + / - change speed, Esc quits.--assets <path>orSPINE_EXAMPLES_DIRpoints it at the rigs.spineboy_walkis the Quick start.spineboy_walk_3ddraws spineboy in a 3D scene with a ground plane, a light, and an orbiting camera.spineboy_screenshotsaves frame 60 to a PNG and exits (SPINE_SCREENSHOT,SPINE_SCREENSHOT_FRAMES).spine_stressfills the screen with spineboys. ] / [ change the count, and--csv path.csvlogs timings per frame. Run it with--release.
To record a GIF like the one above, spine_browser can save a frame sequence:
SPINE_BROWSER_RECORD_DIR=/tmp/frames SPINE_BROWSER_RECORD_FRAMES=120 SPINE_BROWSER_RECORD_WARMUP=45 \
| |
Licensing
Source in this repository is © 2026 Dead Money under the MIT License. It's original work; no spine-cpp code is translated here.
spine_runtime, which this crate depends on, is a spine-cpp derivative under the Spine Runtimes License Agreement, not MIT. Its terms, including the Spine Editor license requirement above, apply to anything built with this crate.
Acknowledgements
Built on Bevy and spine_runtime, a port of Esoteric Software's Spine runtime. The upstream spine-runtimes repository is the source of truth for runtime behavior. Report runtime bugs to spine_runtime; report Bevy integration bugs here.