@openbone/pixi
OpenBone runtime for PixiJS v8: an OpenBone document as a Pixi Container.
OpenBone is open-source 2D skeletal animation: Spine-style rigging,
Rive-style state machines and an MCP server for AI agents. This package plays OpenBone documents inside a PixiJS v8
scene, with state machines, pointer listeners, events, skins, scripts and sound. Playback comes from
@openbone/engine-kit and
@openbone/runtime; drawing uses Pixi meshes built from the same
batches as the reference renderer, @openbone/render-webgl.
For a page without Pixi, use @openbone/player.
Install
Section titled “Install”npm install @openbone/pixi pixi.jspixi.js v8 is a peer dependency (^8.0.0).
Quick start
Section titled “Quick start”import { Application } from "pixi.js";import { OpenBone, loadOpenBone } from "@openbone/pixi";
const app = new Application();await app.init({ preference: "webgl", width: 900, height: 600, background: "#1b1e24" });document.body.appendChild(app.canvas);
// Loads the document, its images (atlas pages when packed) and, when it has scripts, the script sandbox.const knight = await OpenBone.from("/knight.obone", { stateMachine: "knight" });knight.position.set(knight.asset.doc.originX, knight.asset.doc.originY); // skeleton origin, in stage pixelsapp.stage.addChild(knight);
knight.setInput("walk", true);knight.onOpenBone("stateChange", (e) => console.log(e.layer, e.from, "->", e.to));knight.onOpenBone("event", (e) => console.log("event", e.name));setTimeout(() => knight.fire("attack"), 500);
// Load once, then make several copies that share the textures.const asset = await loadOpenBone("/button.obone");for (let i = 0; i < 2; i++) { const button = await OpenBone.from(asset); button.scale.set(0.5); button.position.set(100 + i * 250 + asset.doc.originX * 0.5, 20 + asset.doc.originY * 0.5); app.stage.addChild(button);}knight.obone and button.obone are in the repository’s examples/ folder. The container’s position is the
skeleton origin; at scale 1, one skeleton unit is one pixel, with y down like the rest of Pixi.
| Export | |
|---|---|
OpenBone |
The display object (a Container). await OpenBone.from(src, options) loads and creates it. |
loadOpenBone(src, options) |
Loads a document and its textures into an OpenBoneAsset that many objects can share. |
OpenBoneAsset |
{ doc, baseUrl, textures } (Pixi textures by image or atlas page id). |
OpenBoneOptions, LoadOptions, DriverOptions, InputValue, OpenBoneSource |
Option and value types. |
BatchMesh, PIXI_BLEND |
The mesh class used per batch, and the blend mode mapping. |
src is a URL, bytes (ArrayBuffer or Uint8Array of a .obone or .obonb file), a parsed document or, for
OpenBone.from, an OpenBoneAsset.
Options
Section titled “Options”| Option | Default | |
|---|---|---|
stateMachine / animation / loop |
first state machine, else first animation | What to run. |
skin |
default skin | Skin (outfit) by name or id. |
scripts |
"quickjs" when the document has scripts |
"none" or your own ScriptHost. |
audio |
true |
false or "muted". |
audioContext, audioBaseUrl, resolveAudioUrl, unlockTarget |
Sound setup (see engine-kit). | |
onEvent, onStateChange |
Callbacks. | |
autoUpdate |
true |
Advance on a Pixi ticker. Set false and call update(dt) (seconds) yourself. |
ticker |
Ticker.shared |
The ticker used when autoUpdate is on. |
interactive |
true |
Route Pixi pointer events to the state machine. |
resolution |
devicePixelRatio |
Pixel ratio that text is rasterised at. |
baseUrl, fetch (loading) |
the document’s folder | Where relative image and audio paths load from, and the fetch to use. |
resolveImageUrl(src) (loading) |
Map an image path to the URL to load. |
Methods and properties
Section titled “Methods and properties”| Member | |
|---|---|
setInput(name, value), getInput(name), fire(name) |
State machine inputs and triggers. |
play(animation, loop?), useStateMachine(name?) |
Switch to an animation or a state machine. |
setSkin(name), setText(slot, text) |
Outfit, and text for a slot’s text attachment (null restores it). |
pause(), resume(), paused, speed |
Playback. Pausing also pauses sounds; speed changes their rate too. |
update(dt), redraw() |
Advance and rebuild the meshes; rebuild only (after changing the pose by hand). |
onOpenBone(name, fn) |
Listen to "event", "stateChange" or "audio". Returns an unsubscribe function. |
artboard |
The artboard rectangle in the container’s space (y down). |
boundingBoxes(), toSkeleton(global), fromSkeleton(x, y) |
Bounding-box queries in skeleton space (y up), and conversion from / to Pixi global coordinates: obj.boundingBoxes().containsPoint(...obj.toSkeleton(e.global)). |
drawCalls |
Batches drawn in the last update. |
driver, instance, skeleton, stateMachine, asset |
Lower-level access. |
destroy() |
Stops ticking, stops sounds and frees text and particle textures. Shared asset textures are kept. |
Pointer input
Section titled “Pointer input”With a state machine and interactive on, the object listens to pointerdown, pointerup, pointerupoutside and
globalpointermove, with the artboard as hit area. Points are converted to skeleton space for the state machine’s
listeners. Moving off the artboard ends hovers.
Sound and scripts
Section titled “Sound and scripts”Sounds play with Web Audio and unlock on the first click or key press. Documents with scripts load the QuickJS
sandbox in OpenBone.from. If you use Vite, exclude quickjs-emscripten from dependency pre-bundling
(optimizeDeps: { exclude: ["quickjs-emscripten"] }) so it finds its .wasm file, as the repository’s demo does.
How it draws
Section titled “How it draws”Each frame the pose is grouped into render-webgl’s batches (one per texture or atlas page and blend mode). Each batch
is one Mesh with a small vertex-coloured shader, so meshes, weights, tints, per-vertex particle colours and sprite
sequences look as in the editor. Batches with a two-colour tint (a slot dark colour) switch to a second shader with
a per-vertex dark colour; other batches never upload it. Premultiplied atlas pages are uploaded as they are; other
images are premultiplied on upload.
- Clipping masks become stencil masks (
Graphics). - Blend modes map to Pixi’s
normal,add,multiplyandscreen, which use the same GL blend functions. - Text attachments are rasterised to canvas textures. Particles use the built-in soft sprite.
- Pack an atlas when exporting for the fewest draw calls (
drawCalls).
The shader is GLSL, so use Pixi’s WebGL renderer (preference: "webgl", Pixi’s default).
From a checkout: pnpm --filter @openbone/examples engines, then open http://localhost:5190/pixi/ (add
?example=campfire, rocket, crash… for other examples).
Documentation
Section titled “Documentation”- Architecture: runtime model and rendering
- File format
- Demo source
- Related packages:
@openbone/engine-kit,@openbone/phaser,@openbone/three,@openbone/canvaskit,@openbone/player
License
Section titled “License”MIT, © UX Century and OpenBone contributors. See LICENSE.
OpenBone is MIT licensed and made by UXCentury. The example art, voices and sounds are credited in the repository.