Changelog
OpenBone uses one version for the whole repo (packages, editor, desktop app, native runtimes). Set it with pnpm version:set <major|minor|patch|x.y.z> and check it with pnpm version:check. Versions follow Semantic Versioning: while it’s 0.x, minor versions may change the .obone format or APIs.
0.3.0 (2026-11-11)
Section titled “0.3.0 (2026-11-11)”Editor
Section titled “Editor”- Your own layouts: Menu → Layout → “Save current layout as…” keeps where every panel is docked, its tabs, splits and sizes under a name; your layouts are listed at the top of Menu → Layout (the current one ticked) and switch in one click, with Rename and Delete. They are kept by the editor, so they work in every project, and are in the command palette too.
- The toolbar stays on one row from 1280 px up: the pick filter and the view options (Bones, Mesh, Attachments, Order, 3D) are compact drop-downs (Pick ▾, View ▾, whose labels show anything unusual, like “Pick: Parts” or “View: 3D”); on narrower windows the add buttons fold into + Add ▾, “Connect MCP” shortens to “MCP”, and the file name and ⌨ button make room.
- Your own particle presets: ”★ Save as preset…” in the particles Inspector keeps an emitter’s settings under a name (in the editor, so in every project); they appear with a ★ in ”+ Particles…” and “Apply preset…”, and “Delete a preset…” removes one. A preset that uses an image as its particle uses the image of the same name in the open project (round particles if there isn’t one). + Particles… → Custom… starts an emitter from nothing, a built-in or your own preset, with any project image as the particle, in one step.
- Weights tool view (like Spine’s Pies / Overlay / Selected, remembered): each bone gets its own colour, shown as a swatch on the mesh’s bone chips (click it to paint that bone). Overlay tints the mesh with every bone’s colour blended by the weights as a smooth gradient (off: the painted bone’s weights as a smooth heat map, blue 0 → red 1); Pies draws a pie of every bone’s share on each vertex; Selected limits the pies to the selected vertices. With vertices selected, each bone chip shows its average share of them.
- Free-form deformation (FFD): the Mesh tool works in Animate mode. Dragging vertices (or the arrow keys) keys a deform at the playhead, so an image can stretch, squash, bend and bounce over time; the mesh itself is untouched. Works with weighted meshes (the vertex lands exactly where it is dropped in the current pose) and soft selection; Delete puts the selected vertices back in that key; adding and removing vertices stays in Setup mode.
- Command palette (
Ctrl/⌘+KorCtrl/⌘+Shift+P): search every action by name and run it, with its key shown, including the right-click actions for the selection (part and bone transforms, lock, trace mesh, show only this). - Hover preview: with the Select / Move / Rotate / Scale / Pose tools, what a click would grab gets a dashed outline (a part) or a glow (the bone that would move), and the status line names it. Pick filter in the toolbar (Pick: Bones / Parts, remembered) rules out bones or parts for viewport clicks.
- Shift-press on something already selected starts a drag (Shift then snaps it); only a Shift-click without moving takes it out of the selection.
- Each tool’s status line says what it does and what Shift / Ctrl change (for example “Ctrl/⌘-click: add a vertex”), plus the palette’s key.
- Right-click a part for Trace mesh…, Edit mesh points and Re-trace outline… (they were only in the Inspector).
- The view is remembered per file: eyes, locks, show-only-selected and the camera (pan and zoom) come back when a project is opened again.
- The grid and origin lines are drawn over the art (they are for lining parts up) and under the bones and handles; they used to be hidden behind the pictures.
@openbone/render-canvashas abeforeOverlayshook for this. - Lock (Skeleton tree, a lock next to each eye;
Lfor the selection; right-click → Lock): a locked bone or part can’t be clicked in the viewport or the 3D view, so the background stays put while you work on the character. A locked bone locks everything under it (shown dimmed); select locked items in the tree. View menu → Unlock all. Editor-only, like the eyes. - Show only selected (
Shift+H, the ◎ button in the Skeleton header, right-click → Show only this): hides every part except the selection (a selected bone keeps everything under it); press again for Show all, which brings back the eye states from before. - Bone | Part switch (toolbar next to Comp, shortcut
A, remembered): in Setup mode it decides what dragging a picture transforms with the Move, Rotate and Scale tools. Bone moves, rotates or scales its bone; Part moves, rotates or scales the picture itself, around its origin, even where the bone is drawn across it (clicking a bone away from any picture still grabs the bone). In Part mode selected parts turn or scale together around the grabbed one, Shift snaps to 15° / 10% steps, each drag is one undo step, and the viewport status showsSETUP · PARTSand the angle or percentage. Animate mode always poses bones. In Part mode a selected part is highlighted on its own and gets its own gizmo at its origin (the point it turns and scales around): a dashed orange ring with a dot showing where the part points (Rotate), red / green handles along the part’s own axes (Scale), arrows (Move); they can be dragged from outside the picture. The bone’s handles are hidden. While a ring is dragged (a part’s or a bone’s), a wedge shows the turn and a label the change and the result (+40.1° · 130.1°); scaling a part shows its percentage. - Mesh tool cursors: holding Ctrl/⌘ shows a crosshair with a plus where a click adds a vertex, and hovering a vertex shows a move cursor.
- Re-trace outline… works on weighted meshes: the new mesh is bound again to the same bones, with as many influences per vertex as before (it was disabled with no explanation). Linked meshes, and meshes other meshes link to, say why they can’t be re-traced.
- Select several parts: Shift+click adds a part to the selection (or takes it out), Ctrl/⌘+click takes it out, in the viewport, the Skeleton tree and the 3D view (bones worked this way already; in the 3D view Alt+Shift+click adds bones). Every selected part is outlined; dragging one of them with the move tool, or the 3D gizmo, moves them all by the same offset, and the 3D gizmo’s Z arrow moves them through the draw order together, each as one undoable edit.
- 3D view (View → 3D view, toolbar,
Shift+Z): the scene as a stack of layers you orbit around, like an engine’s scene view. Every part sits on its own outlined plane, spaced by draw order; the bones are solid, lit 3D bones at the depth of the parts they carry; an inset shows the normal front view. A move gizmo sits on the selection: red X and green Y move a part (its image on its bone, Setup mode) or a bone (setup pose, or a key in Animate mode), blue Z moves a part through the draw order; Shift snaps to 10 px. Press a part to select it and drag it through the stack to change its draw order (one undoable edit, previewed live, also in the front inset); drag empty space to orbit; Alt+click selects the bone under a part. Depth spacing, outlines, bones and scenery can be toggled. It follows the live pose in Setup, Animate and Play. three.js loads only when the view opens (needs WebGL). - Order view (View → Order view, toolbar,
Z): the character turned to face you, with the draw order as left-to-right depth. Every part stands in its own column at its real height, from the back-most on the left to the front-most on the right, so a part in the wrong layer stands out; drag a part left or right to move it in the draw order (one undoable edit). Backgrounds that span the artboard are left out. - Attachments toggle (View → Attachments, toolbar,
H): hide every attachment and show only the bones, on a dark artboard so they stay readable. - Pen tool (
N, ✒ in the toolbar) for paths: click to place knots, drag to pull out handles, click the first knot to close, Enter or Esc to finish. It creates a slot and path on the selected bone, or, with an end knot of an open path selected, keeps drawing that path. Drag anchors (handles follow) and handles; handles are smooth, mirrored or broken per knot (Alt-drag breaks a handle, Alt-drag an anchor pulls out new ones, right-click a knot to pick a mode or reset its handles). Click the curve to add a knot without changing its shape, Delete removes knots, the panel and context menu toggle Closed. Anchors snap like bones. Weighted paths are re-bound through their bones when knots move; in Animate mode dragging knots keys the path’s deform. Paths draw in their colour, the selected one highlighted, and not at all when their slot is hidden. - Inspector: “Bind knots to bones” for paths (selected knots or all: bind by distance or 100% to a bone, per-bone weight sliders, Unbind).
- Bounding boxes: edit points with the Pen tool (drag, click an edge to add, drag inside to move all, Delete), a colour, a translucent fill in the viewport, “Trace from image…” (wraps the box round the visible pixels of an image or mesh, with a corner count) and Rectangle / Ellipse presets. Undoable, one step per edit.
Examples
Section titled “Examples”- Knight and Walker: limbs are layered the way the art is drawn (upper arm over the forearm’s open top, thigh over the shin’s top, boots under the shins), the knight’s shoulders sit in the breastplate’s armholes, his knee guards sit at the knees, the cape hangs from behind the shoulders (no floating clasp), the helmet sits on the shoulders, and the far hand is a mirrored fist so he has a left and a right hand.
openbone renderno longer draws click areas (bounding boxes) into the picture;--bounding-boxesshows them.
Format
Section titled “Format”- Bounding boxes take an optional editor
color.updateAttachmentwithweights: nullunbinds a path.
Runtimes
Section titled “Runtimes”- Unity: text outlines are drawn (TextMesh has none: a ring of outline-coloured copies under the fill, like MonoGame); outlined titles and subtitles lost their stroke before.
- Video exports with sound, and MP4 / WebM (
export_file,openbone export): AVI and MOV carry the sound the animation or state machine run plays (music, ambience and voice started by states, keys, events and buttons, with fades, loops, buses and mutes), mixed by the newmixAudioCommandsin@openbone/export; newmp4(H.264 + AAC) andwebm(VP9 + Opus) formats. WAV sounds decode in-process; MP3/OGG/FLAC and the MP4/WebM encoders useffmpegfrom the PATH when it is there (not bundled).audio: false/--no-audioleaves the sound out. - Single-frame PNG/JPEG exports of a state machine run step through to the frame, so inputs fired before it take effect (a trigger at 0.5 s showed no effect in a frame at 2 s).
export_fileembed: true, andopenbone export --embed(obone, json, obonb): every image and audio file goes inside the file as data, so a copy opens on its own anywhere. The web editor can’t read files next to an opened file, so a plain copy elsewhere showed every image missing.- Mute buttons: new listener action
setMute {bus, muted: true | false | "toggle"}(bus: null= everything) mutes a bus while playing; the runtime emits amixaudio command and@openbone/playerapplies it on top of the saved mix. The editor’s listener form has toggle mute / mute / unmute (bus). Every native runtime has it too: C++ (the C API gainsOB_AUDIO_MIXand amutedfield appended toob_audio_command; the engine samples’ mixer, Godot and Unreal apply it), Swift and Flutter (newmixcommand case; their players apply it), C# (MonoGame and Unity players), Java (libGDX and Android through the shared mixer) and Haxe; the conformance suite’sinteractivefixture checks it in all of them. process_image: art prep without leaving the MCP. OpskeyColor(flat background out, soft edges, background tint removed from edge pixels, green/blue-screendespill;keepBrighterThankeeps white details; a largesoftnesskeys glows off black),differenceMatte(cut an object out by comparing with a copy where it is painted out, e.g. a tree, so it can sway on its own layer),trim,crop,resize,flip,featherEdges(seamless tiling),keepLargest; the PNG is saved and imported (importAs) or replaces an image in place (replace), andboxsays where it sat in the input picture.set_weights_in_area(core opsetWeightsInArea, so the editor and CLI have it too): weight every vertex inside a box or polygon of the setup pose to the given bones, with a softblendedge, replacing or adding to their influences.trace_meshre-traces meshes in place (same image mapping) and binds them again to the same bones (orbones), like the editor’s Re-trace outline; newtolerancesets how closely the outline follows the edge (the old 1%-of-the-image default flattened details such as feet).set_keysreplace: truesets a whole timeline; by default keys still merge, which left stale keys behind when a motion was redone.import_audioreads the length of MP3 (Xing/Info, VBRI or counted frames, encoder padding left out), OGG Vorbis/Opus and FLAC files; only WAV was measured before, so other formats came in as 0 s.render_gifno longer drops the connection on long renders: a GIF over 4 MB is saved to disk and the result carries a contact sheet of six frames instead (a 14 MB GIF, 19 MB as base64 in one message, made the client close the connection).- Images with big PNG metadata chunks (C2PA “caBX” content credentials from image generators) load everywhere in the MCP; they failed with “Invalid SVG image”. Bundlers loading
@openbone/mcp(Vite, Vitest) get a working GIF encoder.
Packages
Section titled “Packages”- Text no longer gets cut off when it is wider than its box:
@openbone/render-webgland@openbone/engine-kit(Pixi, Phaser, Three) measure the text and grow its texture sideways (textOverflowin@openbone/runtime). New text boxes count Japanese, Chinese, Korean and full-width characters as 1 em wide (they were sized like Latin letters, so a Japanese line got about two thirds of its width). - npm: every
@openbone/*package builds to JavaScript with type definitions, plus theopenboneCLI (npx openbone) andnpx -y @openbone/mcp. - NuGet:
OpenBone.Runtime,OpenBone.Scripting.JintandOpenBone.MonoGame, with symbols and SourceLink. - Unity: the package installs from git (
https://github.com/UXCentury/open-bone.git#upm) and OpenUPM, with the DLLs built in. - Maven Central:
io.openbone:openbone-runtime,openbone-scripting,openbone-gdxandopenbone-android. - Swift:
Package.swiftmoved to the repository root, so SwiftPM installs the package from the repository URL. - Flutter: the plugin can be published to pub.dev with the C++ sources bundled.
- Godot: a ready-to-install
addons/openbonewith Windows, macOS, Linux (x86_64, arm64) and Android libraries. - Publish workflows for each registry, run by the GitHub release (pub.dev and Swift: by the tag).
- Bounding-box queries, in the TypeScript runtime and every native runtime (C++ and its C API, C#, Java, Haxe, Swift, Dart):
skeleton.boundingBoxes()(instance.boundingBoxes()) gives the world polygons of the active bounding boxes and their AABB, withcontainsPoint/containsPointAll,intersectsSegment/intersectsSegmentAll,aabbIntersectsandintersectsBounds(other)for overlap between two skeletons, in skeleton space. The player and the engine adapters expose it with coordinate helpers (Player.clientToSkeleton, pixitoSkeleton, phaserworldToSkeleton, threerayToSkeleton, canvaskitclientToSkeleton). - Bounding boxes can be weighted to bones (
autoWeights,setWeights) and take deform keys, like paths. - Path constraints:
constantSpeed(default on, the existing arc-length placement). Off places bones at the raw bezier parameter inside each curve segment, as Spine does; a “Constant speed” checkbox in the path-constraint Inspector. Existing files pose exactly as before. - Conformance:
bounds(recorded point, segment, AABB and skeleton-overlap queries) andpaths(constant speed on and off on uneven curves) fixtures. - Knight example: the project owner’s hand alignment (arms, forearms, shins, boots, helmet, sword; the breastplate over the front thigh and each vambrace over its upper arm; the road lowered to his feet), built into
examples/build-examples.tsso rebuilding keeps it. The forearms reach up into the upper arms’ open tubes, so there is no gap at the elbow. The far hand has its own gauntlet drawn from the inner side (thumb over the curled fingers) instead of a mirrored copy of the near one, so the two read as a left and a right hand.
Texture packer
Section titled “Texture packer”- New packer options in the Export dialog,
openbone export(--format atlasand--pack) and the MCPpack_atlas/export_filetools, all through the same settings:- Alias (on): identical images, same pixels after trimming, are stored once and their regions share one rect.
- Ignore blank images (on): fully transparent images take no space; their regions are marked
emptyand point at a shared 1×1 transparent block, so every runtime draws nothing for them without changes. - Alpha threshold (0–255) for whitespace stripping, polygons and the blank test.
- Page rules: square pages (the packer already could; now exposed), min width / height, separate max width / height (
--max-sizestill sets both), divisible by 4. - Edge padding and duplicate padding (both on, as before: edge pixels repeat into half the padding; with duplicate padding off, opaque edges repeat as colour only and fade out) and bleed (on unless premultiplied): transparent pixels take the nearest visible colour, alpha 0, so filtered and scaled edges get no dark fringe. Edge padding now fills the whole half of the padding (it stopped at 2 px).
- Several scales in one pack: rows of scale + file suffix (
--scales 1,0.5 --suffixes ,@0.5x,atlas.scalesinexport_file), one data file + page set per scale. - Page format: PNG or JPEG with a quality (JPEG pages are opaque; a warning says when the art has transparency). Resample for scaled packs: bilinear, bicubic (the canvas’s high-quality smoothing, Skia’s cubic in Node and Chrome) or nearest.
- Debug: region outlines drawn on the pages.
- Atlas metadata for runtimes:
atlas.filterMin/filterMag(nearest, linear, mipmap variants),wrapX/wrapY(clamp, repeat, mirroredRepeat) andpixelFormat(RGBA8888, RGB888, RGBA4444, RGB565, Alpha), accepted bysetAtlasand kept in JSON and binary files. render-webgl, Pixi, Three.js and CanvasKit apply the filter and wrap to atlas pages (with generated mipmaps where asked); Phaser and Canvas 2D switch to nearest forfilterMag: nearest. Native runtimes read and ignore the new fields. - Editor: data exports can write the pages as separate files in a ZIP (always with several scales).
Security
Section titled “Security”- MCP server: the editor bridge and the HTTP transport refuse browser requests from pages other than the OpenBone editor, including DNS rebinding. Before, any web page open on the same machine could connect to the bridge and read or change the open project.
- Viewport: a part’s selection outline, its hover preview and clicking on it follow the drawn shape (a traced mesh’s outline, an image’s rectangle), not a click area (bounding box) on the same slot. The knight’s helmet and torso showed square outlines because of their
head-hit/body-hitclick areas. - Viewport picking: a click on a picture no longer grabs a bone hidden under it (one whose own parts are all drawn behind that picture, such as the knight’s back forearm under his shield); the click goes to the picture. Bones with no parts (IK targets, controls) can still be grabbed anywhere.
- Adding particles, a shape, text, a hit box, a point, a mask or a path from the slot Inspector no longer replaces the image the slot shows: when the slot already shows something, the new attachment goes into a new slot on the same bone, just in front of it (one undo removes both).
- Walker example: easing “speed” now lands exactly on 0 (or 1), so after the walker stops the input settles instead of changing every frame by ever smaller amounts. Play mode shows input numbers rounded (no more 4.5e-13 shown as “4.51944”) and updates one log line while a script changes the same input frame after frame.
- Flipping or rotating a bone from the right-click menu keeps its child bones where they are when Compensate is on (flipping the upper arm no longer flips the forearm and hand), and the menu says which will happen: “Transform bone (children follow)” or “(children stay)”. Right-clicking a bone now also offers its part’s own transforms (“Transform “walker-arm-upper””), which flip only the image.
- Flutter example: its bundled knight is the current one (a test now fails if a sample app’s copy of an example goes stale).
- Walker example: the hoodie is drawn over the jeans (its hem covers the tops of the trousers) instead of the front leg over the body, and a new upper-arm sleeve (rounded shoulder and elbow) replaces the blocky one, with the forearm moved up to close the gap at the elbow.
- Knight and walker examples: the feet no longer slide when the character stops. The background now follows the planted foot (it moves exactly as far as that foot does each frame) instead of easing at its own speed, and every animation keys the feet and other animated bones at their rest pose, so a cross-fade from walking into idle (or an attack) blends the stride out instead of snapping it at the end.
- Tail example: the wag is a smooth travelling sine wave. It had an eased key every quarter cycle, so the tail stopped dead four times a cycle and looked like it was skipping frames.
- MCP:
--projectwith a file that can’t be read stops with the reason instead of replacing it with an empty project, andnew_projectrefuses to replace an existing file unless called withoverwrite: true. - CLI and MCP exports: ZIPs carry the format in their name (
hero-atlas.zip,hero-web.zip,hero-spritesheet-godot.zip, …), so exporting several formats into one folder no longer overwrites files. - CLI:
openbone <command> --helpprints that command’s usage (it was read as a file name); the help lists--animationsand--steps. <openbone-player>: settingsrcafter the element is on the page loads it, and changing any attribute reloads the player;backgroundaccepts any CSS colour under WebGL too (short hex such as#fffwas drawn wrong).- Runtime: a document whose scripts would be skipped because no
scriptHostwas given prints a warning once; passscriptHost: nullto opt out on purpose.scriptTimeoutMs, which nothing read, is deprecated. - Editor: the Preview panel runs document scripts, like the viewport.
- Runtimes: cross-fades (state-machine transitions and
AnimationStatemixes) no longer hold a value only the outgoing animation keys and snap it when the fade ends. Like Spine, bone transforms, slot colour / dark colour, constraint mixes, particle rate and deform offsets that the incoming animation doesn’t key now ease from the outgoing value back to the pose underneath (the setup pose on the first layer / track); attachment, draw-order, inherit and other switching keys stay as they were. Walking into idle no longer keeps the stride for the length of the fade and then jumps the feet into the stance. The rule is indocs/runtime-spec.md§5.1a, and every runtime (TypeScript, C++ with Swift and Flutter on top, C#, Java, Haxe) follows it; a newmixingconformance fixture covers it.
Changed
Section titled “Changed”- QuickJS (quickjs-ng 0.10.1) is vendored in
runtimes/cpp/third_party, so no build downloads it. - No fused multiply-add: a source pragma (
exact_math.h) replaces the compiler flag where clang builds the runtime. - The MCP server reports the real version.
0.2.0 (2026-11-10)
Section titled “0.2.0 (2026-11-10)”Native runtimes for C++ (Godot, Cocos2d-x, SDL2, SFML, OpenGL, Unreal), C# (Unity, MonoGame), Java (libGDX, Android), Swift, Flutter and Haxe, all checked against a shared conformance suite; sound in the engine integrations; desktop installers for Windows, macOS and Linux; WebM video export; and the rest of the Spine-style rigging tools.
Native runtimes
Section titled “Native runtimes”- GLFW / OpenGL 3.3 sample (
runtimes/cpp/engines/glfw): the WebGL2 renderer’s shader and blending in plain OpenGL (two-colour tint, premultiplied pages, all blend modes, stencil clipping), mouse input, and a hidden-window--out frame.pngmode. - Sound in more runtimes: the Swift
OpenBoneView/OpenBonePlayer(newOpenBoneAudio, AVFoundation;OpenBoneDocumentgainsaudioAssets,audioBuses,audioMaster), the libGDX module (OpenBoneAudio), the AndroidOpenBoneView(SoundPool / MediaPlayer,SoundSource) and the SDL2 and SFML samples play the document’s sounds (on by default,isAudioEnabled/--muteto opt out; audio callbacks still get every command), on shared, unit-tested gain / voice / fade logic (OpenBoneAudioMixer, JavaAudioMixer,engines/common/openbone_audio.hpp). Unreal still only emits audio commands. - MonoGame integration (
runtimes/csharp/monogame):OpenBone.MonoGamefor MonoGame 3.8 DesktopGL —OpenBoneRenderer(DrawUserIndexedPrimitives, premultiplied blend modes, stencil clipping, two-colour tint through a GLSL effect built in code with aBasicEffectfallback, optional SpriteFont text),OpenBoneMonoGame(load, animations or state machine, mouse / touch → listeners, events) andOpenBoneSoundPlayer(SoundEffectInstancewith bus × master volume, log2 pitch, loops and fades), a DesktopGL knight sample and window-less tests (also in CI). - Haxe runtime (
runtimes/haxe): a pure Haxe 4.3 port of the runtime with no dependencies (.obone/.obonbloading, skins and skin bones, every constraint in custom order, sliders, physics, particles, sequences, packed/trimmed/polygon atlases, text templates, the draw list, state machines with listeners and bindings, events, audio commands) and JS number semantics on every target. All 82 conformance cases pass on the JS target (scripts run on its JavaScript engine throughJsScriptHost); on eval and Python the 6 scripted state-machine cases are skipped and the other 76 pass. - The Flutter plugin moved from
runtimes/darttoruntimes/flutter/openbone: Flutter 3.47’s Swift Package Manager support fails when a plugin’s folder name differs from its package name (flutter#186881). - Java runtime on Gradle 9.9 and Android Gradle Plugin 9.5 (Kotlin 2.4 through the Kotlin Android plugin), without Gradle 10 deprecations.
- Swift Package Manager for the Flutter plugin on iOS and macOS (
runtimes/flutter/openbone/darwin/openbone/Package.swift, with iOS and macOS sharing onedarwin/folder and podspec), so Flutter no longer warns thatopenbonelacks SPM support; the podspecs stay for CocoaPods. - Sound in the native engine integrations: the Flutter
OpenBoneWidget(newOpenBoneAudio, flutter_soloud; the plugin now needs Flutter 3.41+) and the Cocos2d-xOpenBoneNode(cocos2d::AudioEngine) play the document’s sounds, like the GodotOpenBoneSprite. The C API gainsob_document_audio_asset,ob_document_audio_busandob_document_audio_master, and the DartOpenBoneDocumentexposesaudioAssets,audioBusesandaudioMaster. The Flutter example bundles its sounds. - C++ runtime (
runtimes/cpp): a native C++17 port of the runtime with no engine dependencies —.obone/.obonbloading, skins and skin bones, every attachment, animation mixing, IK/transform/path/physics in constraint order, sliders, particles, text templates, the draw list (clipping, sequences, packed/trimmed/polygon atlases, two-colour tint), state machines with listeners and bindings, events, audio commands, and scripts in embedded QuickJS. Passes all conformance cases (CTest). A stable C API (openbone.h,libopenbone) for FFI, a Godot 4 GDExtension (OpenBoneSprite, checked in headless Godot), SDL2 and SFML sample renderers, an Unreal Engine 5 plugin (as source) and a Cocos2d-x 4.0 node (built and run on Linux; it now binds its textures through the program state). - C# runtime (
runtimes/csharp):OpenBone.Runtimefor .NET Standard 2.1 / .NET 8, a native port of the runtime (.oboneand.obonbloading, skins and skin bones, every attachment, mixing, constraints in custom order, sliders, physics, particles, text templates, the draw list, state machines with listeners and bindings, events and audio commands) that passes every case of the conformance suite (dotnet test). Scripts run in Jint (OpenBone.Scripting.Jint) with the sameobAPI and limits as the QuickJS host. - Unity package (
runtimes/csharp/unity/com.openbone.runtime): ScriptedImporter for.obone/.obonb, OpenBone Animation and OpenBone State Machine components (UnityEvents, EventSystem or mouse pointer input), a mesh generator with blend modes, stencil clipping, two-colour tint and premultiplied pages (OpenBone/Skeletonshader), and an AudioSource bridge. Built withruntimes/csharp/unity/build-package.py; tested in Unity 6 (6000.6.8f1) with every example; it now declares itscom.unity.uguidependency, imports art and sounds in any import order, clamps texture edges, and hasStageBoundsfor framing a camera on the document’s stage. - Java runtime (
runtimes/java): a Java 11 port of the runtime with no dependencies (.obone/.obonbloading, skins and skin bones, every constraint in custom order, sliders, physics, particles, sequences, packed atlases, text templates, draw list, state machines, bindings, events, audio commands), state machine scripts in QuickJS (openbone-scripting), a libGDX renderer with an LWJGL3 sample (openbone-gdx), and a Kotlin Android library withOpenBoneView(openbone-android). JUnit runs all 82 conformance cases on the JVM; an instrumented test runs them on Android. - Swift runtime (
runtimes/swift): a SwiftPM package that compiles the C++ runtime from source and wraps its C API —OpenBoneDocument/OpenBoneInstance(.obone/.obonb, skins, animation tracks, state-machine inputs, triggers and pointer, event / audio / state / log callbacks, bones, slots, zero-copy or copied draw list), ARC-owned handles.OpenBoneView(Metal, UIKit and AppKit) and SwiftUIOpenBonePlayerdraw it with blend modes, stencil clipping, two-colour tint, premultiplied pages, particles and CoreText text, and send touches / the mouse to listeners; plus a SwiftUI sample.swift testruns all 82 conformance cases (checked on Linux; the views have not yet been run on Apple hardware). The C API gainsob_instance_slot_count/ob_instance_slot/ob_instance_find_slot. - Flutter / Dart runtime (
runtimes/flutter/openbone): theopenboneFlutter FFI plugin wraps the C++ runtime’s C API withdart:ffi(ffigen bindings) and buildslibopenbonefor Android, Linux and Windows (CMake) and iOS/macOS (CocoaPods). Pure-DartOpenBoneDocument/OpenBoneInstance(skins, tracks, state machine inputs, triggers and pointer, event / audio-command / state streams, draw list; native memory freed bydisposeor finalizers) andOpenBoneWidget+OpenBoneController(Ticker-driven,Canvas.drawVerticeswith blend modes, clipping, two-colour tint and premultiplied pages, gestures → listeners).flutter testruns all 82 conformance cases; the example runs on Linux desktop and an Android emulator (iOS, macOS and Windows not built yet). C API additions: slot accessors (ob_instance_slot_count/ob_instance_slot/ob_instance_find_slot, shared with the Swift package) andob_particle_dot_alpha; CMake optionsOPENBONE_BUILD_STATIC,OPENBONE_INSTALL.
Editor and rigging
Section titled “Editor and rigging”- Dopesheet: multi-key selection (Shift/Ctrl-click, Shift-drag box, Ctrl/⌘+A), drag and arrow-key nudge of the selection, copy / cut / paste at the playhead (across animations), scale in time, curve and delete for all selected keys. New ops
transformKeys,pasteKeys,deleteKeys(also MCP tools). - Compensate (
J, toolbar ⇲ Comp): transform a bone while its child bones keep their place. - Linked meshes (
addLinkedMesh): share another mesh’s geometry and weights with a different image, optionally following its deform keys. - Runtime fixes ahead of the native ports: full-weight rotation keys set the keyed value; draw-order keys keep unlisted slots in place (like Spine); blend states report their stronger animation’s progress (exit times, on complete); transitions to deleted states are skipped; constraint results written to world transforms survive later constraints (
syncApplied); physics stretch uses the world rest length; additive sliders mix their colour and constraint keys; fully transparent shapes aren’t drawn; no NaN from text-driven bindings or short deform keys. - Skin bones and skin constraints:
SkinData.bones/constraints; anything a skin lists is active only while a skin listing it is set (inactive bones and their children are not posed or drawn, inactive constraints are skipped). OpsaddToSkin,removeFromSkin; Inspector “Only in skins” on bones and constraints. - Inherit timeline:
{target: "bone", property: "inherit"}with steppednormal | onlyTranslation | noRotation | noScalekeys; keyable from the Inspector’s Inherit field in Animate mode. - Constraint order: optional
orderon every constraint, applied as one sorted list (default unchanged: IK, transform, path, physics). OpsetConstraintOrder; Inspector “Constraint order” list with ↑↓. - Reference scale:
skeleton.referenceScale(default 100). Physics gravity and wind and IK softness scale with the skeleton’s size relative to it, so a rescaled rig behaves the same. Document inspector “Ref. scale”. - Two-colour tint: a keyable
darkslot colour, drawn by every renderer and runtime: Canvas2D, WebGL2, PixiJS (two-colour mesh shader), Phaser (a two-colour pipeline), Three.js (aDARKmaterial variant) and CanvasKit (a colour-matrix filter per run of equally tinted items). Engine-kit batches carry a per-vertexdarkchannel only when a slot in them uses one, so documents without it draw exactly as before. - Folders for slots, skins, animations, events, constraints and state machines (optional
folderpath; opssetFolder,renameFolder): grouped, collapsible lists with drag into folders, new / rename / delete folder. New Events list in the left panel. - Import data (☰ Menu → Import from project…, op
importData): copy animations and skins from another project by bone/slot name, with the images, sounds and events they use, or bring in its whole skeleton under a new root bone; conflicts rename, replace or skip. - Weight tools: lock bones while painting (locked weights stay, the others renormalise; also Smooth / Auto / Prune / Direct), and
swapWeights(Weights panel → Swap…) to trade two bones’ weights on a mesh or its selected vertices.
Export and art pipeline
Section titled “Export and art pipeline”- Desktop installers: the Desktop installers workflow builds the Tauri app for Windows (
.msi, NSIS.exe), macOS (universal.dmg) and Linux (.deb,.rpm,.AppImage) and attaches them to a draft GitHub Release for eachv*tag (or keeps them as artifacts on a manual run). Optional macOS signing and notarization through repository secrets; installer metadata (publisher, category, descriptions, licence) in the Tauri config. Seedocs/releasing.md. - WebM video export: VP9 (VP8 as fallback) with Opus sound through WebCodecs and
webm-muxer(MIT); with a transparent background the video keeps its alpha channel where the browser’s encoder supports it (Chrome, Edge), otherwise the background is filled with the document’s colour and the dialog says so. - Texture atlas: premultiplied alpha (pages stored as rgb × a, recorded as
atlas.premultipliedAlpha; render-webgl, Pixi, Three.js, CanvasKit and Phaser upload them as they are with the matching blend, Canvas2D converts them back), whitespace stripping (each image packed by its visible bounds; meshes keep what their uvs cover, particle images stay whole) and polygon packing (region attachments draw a tight convex outline,region.polygon, instead of the full quad). In the Export dialog’s Texture atlas settings,pack_atlas/export_fileand thesetAtlasop. One packing path (buildAtlasin render-canvas) for the editor, MCP server and CLI. - Live art reloading: ☰ Menu → Watch art for changes watches the folders the project’s images and sounds come from (desktop, via tauri-plugin-fs), or a folder picked with Watch folder… (web: File System Access API, checked while the editor tab is in front). Changed files replace the art in place, sizes included (region attachments scale with their image), with a toast and no undo steps. New op
updateImage;DocumentStore.applyQuietapplies an op without an undo step. - CLI batch export:
openbone export <files or globs> --format json|obonb|atlas|png|jpeg|gif|apng|psd|avi|mov|spritesheet|web --out <dir>with--animation,--skin,--scale,--fpsand the atlas options. It runs the sameexportFilecode as the MCPexport_filetool. Newatlasformat: runtime JSON plus its atlas pages as PNG files in a ZIP. - Photoshop (
.psd) import: layers become slots and regions in place; groups can become bones.
Tooling, CI and docs
Section titled “Tooling, CI and docs”- CI (
.github/workflows): GitHub Actions run the web tests and build, and every native runtime against the conformance suite when its files change — C++ on Linux, macOS and Windows plus Godot (headless), SDL2 and SFML; C# and the Unity compile check; Java, libGDX and the Android library; Swift on Linux and macOS plus an iOS build; Flutter tests on Linux and macOS and the example built for Linux, Android, macOS, iOS and Windows. - About dialog (☰ Menu → About OpenBone): version, build, credits for the open-source tools OpenBone is built with.
pnpm version:set/pnpm version:checkkeep every package, the Tauri app and the desktop crate on one version.- Conformance suite (
conformance/,pnpm conformance): reference poses, draw lists, events, audio and state-machine traces from the TypeScript runtime for every example plus focused rigs and packed atlases, checked by a drift test;docs/runtime-spec.mdspecifies the evaluation rules for ports to other languages.
First public version.
- Rigging: bones with inherit modes, slots, skins, region / mesh / weighted mesh / shape / text / sequence / particle / clipping / path / bounding box / point attachments, IK (1- and 2-bone, softness, stretch, compress), transform, path and physics constraints, sliders, texture atlas packing.
- Animation: dopesheet and curve graph, auto-key, onion skin, events, audio keys, lip sync, deform keys.
- Interactivity: Rive-style state machines (inputs, blend states, transitions, pointer listeners), data bindings, staged flows, QuickJS-sandboxed scripts.
- Audio: sounds, buses and mixing, played by animations, events, states and listeners.
- Editor: web and desktop (Tauri 2), docking panels, Play mode, rebindable shortcuts (Windows / Linux and macOS), undo history that survives a reload, confirmations before destructive actions, examples gallery.
- Export:
.obone/ JSON / binary data, PNG, JPEG, GIF, APNG, PSD, AVI / MOV (with sound), MP4, web player, sprite sheets and engine packs (Unity, Godot, Flutter, Android, iOS). - Runtimes: TypeScript runtime with Canvas2D and WebGL2 renderers,
<openbone-player>, PixiJS, Phaser 3, Three.js and CanvasKit adapters. - MCP server with tools for everything the editor does, a live bridge to the open editor, and a CLI.
OpenBone is MIT licensed and made by UXCentury. The example art, voices and sounds are credited in the repository.