@openbone/phaser
OpenBone runtime for Phaser 3: a scene plugin, a loader file type and a GameObject.
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 in Phaser 3 games,
with state machines, pointer listeners, events, skins, scripts and sound, on both the WebGL and the Canvas renderer.
Playback comes from @openbone/engine-kit; WebGL drawing
streams the same batches as @openbone/render-webgl into
Phaser’s pipeline, and Canvas drawing uses @openbone/render-canvas.
Install
Section titled “Install”npm install @openbone/phaser phaser@3phaser is a peer dependency (>=3.60.0 <4).
Quick start
Section titled “Quick start”import * as Phaser from "phaser";import { type OpenBoneGameObject, OpenBonePlugin } from "@openbone/phaser";
class Level extends Phaser.Scene { declare openbone: OpenBonePlugin; // the scene plugin, under its mapping knight!: OpenBoneGameObject;
preload() { // .obone or .obonb; images load relative to the file (atlas pages when packed). this.load.openbone("knight", "/knight.obone"); }
create() { const doc = this.openbone.get("knight")!.doc; // x, y is the skeleton origin: here the artboard fills the 900 × 600 game. this.knight = this.add.openbone(doc.originX, doc.originY, "knight", { stateMachine: "knight" }); this.knight.setInput("walk", true).fire("attack"); this.knight.onOpenBone("stateChange", (e) => console.log(e.layer, e.from, "->", e.to)); this.knight.onOpenBone("event", (e) => console.log("event", e.name)); }}
new Phaser.Game({ type: Phaser.AUTO, width: 900, height: 600, plugins: { scene: [{ key: "OpenBonePlugin", plugin: OpenBonePlugin, mapping: "openbone" }] }, scene: Level,});knight.obone is in the repository’s examples/ folder; its knight state machine has a walk input and attack
and salute triggers. Registering the plugin adds this.load.openbone and this.add.openbone (typed through global
declarations when you import the package).
| Export | |
|---|---|
OpenBonePlugin |
Scene plugin. Registers the openbone loader file type and game object factory. get(key) returns the loaded OpenBoneCacheEntry. |
OpenBoneGameObject |
The game object created by this.add.openbone. |
OpenBoneFile, OpenBoneFileConfig |
The loader file type and its options. |
OpenBoneCacheEntry |
{ doc, baseUrl, textureKeys, scriptHost? }, stored in this.cache.custom.openbone. |
OpenBoneObjectOptions, DriverOptions, InputValue |
Option and value types. |
CACHE_KEY, textureKey(fileKey, id) |
The cache name and the Phaser texture key used for each image or atlas page. |
DARK_PIPELINE, twoColorPipeline(renderer), TwoColorPipeline |
The two-colour tint pipeline (registered automatically when needed). |
Loading: this.load.openbone(key, url, config?)
Section titled “Loading: this.load.openbone(key, url, config?)”| Config | Default | |
|---|---|---|
baseUrl |
the document’s folder | Base for relative image and audio paths. |
resolveImageUrl(src) |
Map an image path to the URL to load. | |
scripts |
true |
Load the QuickJS sandbox while preloading when the document has scripts. false skips it. |
xhrSettings |
Phaser XHR settings for the document request. |
The document’s images (or atlas pages) are queued on the same loader, so create() runs after they have loaded.
Adding: this.add.openbone(x, y, key, options?)
Section titled “Adding: this.add.openbone(x, y, key, options?)”| Option | Default | |
|---|---|---|
stateMachine / animation / loop |
first state machine, else first animation | What to run. |
skin |
default skin | Skin (outfit) by name or id. |
audio |
true |
false or "muted". |
scripts |
the sandbox loaded with the file | "none" or your own ScriptHost. |
interactive |
true |
Route the scene’s pointer events to the state machine. |
audioContext, audioBaseUrl, resolveAudioUrl, unlockTarget, onEvent, onStateChange |
As in engine-kit. Sounds use Phaser’s own AudioContext by default. |
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. |
onOpenBone(name, fn) |
Listen to "event", "stateChange" or "audio". Returns an unsubscribe function. |
artboard |
The artboard rectangle in local space (y down). |
boundingBoxes(), worldToSkeleton(x, y), skeletonToWorld(x, y) |
Bounding-box queries in skeleton space (y up), and conversion from / to Phaser world coordinates: const w = cam.getWorldPoint(p.x, p.y); obj.boundingBoxes().containsPoint(...obj.worldToSkeleton(w.x, w.y)). |
batches |
Batches drawn in the last WebGL frame. |
driver, instance, skeleton, stateMachine |
Lower-level access. |
destroy() |
Unbinds pointer handlers, stops sounds and removes its text textures. |
setInput, fire, setText, setSkin, play, pause and resume return the object, so calls chain. The object
also has Phaser’s Alpha, BlendMode, ComputedSize, Depth, Pipeline, ScrollFactor, Transform and Visible components: its
x/y is the skeleton origin, and position, rotation, scale, depth, alpha, visibility and scroll factor work as on any
GameObject. It advances in the scene’s update.
Pointer input
Section titled “Pointer input”With a state machine and interactive on, the object listens to the scene’s pointerdown, pointerup and
pointermove. Pointers over its artboard (through the camera and the object’s transform) reach the state machine’s
listeners; moving off the artboard ends hovers. Hidden or inactive objects ignore pointers.
How it draws
Section titled “How it draws”- WebGL: the pose is grouped into render-webgl’s batches and streamed into Phaser’s MultiPipeline, which can merge
several textures into one draw call. Vertex colours use Phaser’s tint (8 bits per channel). Batches with a
two-colour tint (a slot
darkcolour) go throughOpenBoneTwoColorPipeline, a MultiPipeline with a second packed colour per vertex, registered on the renderer the first time it is needed. Blend modes map to Phaser’s (additive is registered asONE, ONElike the reference renderer). Clipping masks write the stencil buffer, so an OpenBone object with clipping should not itself be inside a Phaser geometry mask. Text is rasterised to canvas textures; particles use the built-in soft sprite. - Canvas (
type: Phaser.CANVAS): drawn by@openbone/render-canvasin the object’s transform. - Premultiplied atlas pages are replaced once by straight-alpha copies in Phaser’s texture manager, because Phaser premultiplies on upload and its Canvas renderer expects straight alpha.
From a checkout: pnpm --filter @openbone/examples engines, then open http://localhost:5190/phaser/ (?canvas for
the Canvas renderer, ?example=campfire for other examples).
Documentation
Section titled “Documentation”- Architecture: runtime model and rendering
- File format
- Demo source
- Related packages:
@openbone/engine-kit,@openbone/pixi,@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.