Skip to content

OpenBone conformance suite

Reference results from the TypeScript runtime (packages/runtime), so runtimes in other languages (C++, C#, Java, Swift, Dart…) can prove they pose, draw and react exactly like it. The rules they implement are in docs/runtime-spec.md.

conformance/
fixtures.ts focused rigs built from ops (one area each: transforms, constraints, meshes, dynamics, interactive)
fixtures/*.obone those rigs as files (generated; runtimes load these)
expected/*.json what the TypeScript runtime produced, per fixture and per example in examples/
generate.ts writes fixtures/ and expected/ → pnpm conformance
conformance.test.ts fails when the TypeScript runtime no longer matches expected/ (part of pnpm test)

When the runtime changes on purpose, run pnpm conformance and commit the new files with the change. Every other runtime then has to follow.

Each expected/<name>.json has document (path from the repo root), dt (1/60) and sampleEvery (10), plus a list of cases:

kind how to run it
setup Load the document, set skin skin, pose the setup pose (advance 0). One sample.
animation Load, set skin, play animation looping on track 0, then advance(0) and sample, then step advance(dt) duration / dt times, sampling after every sampleEvery steps.
stateMachine Load, start state machine stateMachine, advance(0) and sample, then step as above. Before the step that starts at time t, apply every action whose t is within dt/2 of t, in order: {input, value} sets an input, {fire} fires a trigger, {pointer, x, y} sends a pointer event in skeleton space (y up).

A step must do what OpenBoneInstance.advance(dt) does (see packages/runtime/src/instance.ts): update the animation state or state machine, set the setup pose, apply animations, advance physics and particles, update world transforms.

field meaning tolerance
t seconds since the case started exact (4 decimals)
bones world matrix [a, b, c, d, x, y] per bone, in document order 1e-3 for x/y, 1e-4 for a–d
slots per slot in document order: [attachment name or null, [r,g,b,a], [r,g,b] dark or null] names exact, colours 1/255
draw the draw list in draw order: slot, attachment, type, blend, texture (image or atlas page id), vertices (world x,y pairs), uvs, indices, color, optional dark, colors (per-vertex RGBA, particles), clip (index of the clipping mask in this sample), text (resolved text content) vertices/uvs 1e-3, colours 1/255, everything else exact; particle vertices 1e-2
events events fired since the previous sample: name, time, int, float, string exact (float 1e-4)
audio audio commands since the previous sample (play / stop / fade); play ids are renumbered p1, p2… in order of appearance exact (numbers 1e-4)
states state machines only: the active state per layer after the step exact
bounds files with queries only: see below vertices/aabb 1e-3, the rest exact

A file with a top-level queries (points [x, y], segments [x1, y1, x2, y2], aabbs [minX, minY, maxX, maxY], others [x, y]) asks them of every sample, after the step, on skeleton.boundingBoxes() (docs/runtime-spec.md §8.10):

bounds field meaning
boxes the active bounding boxes in draw order: slot, attachment, world vertices
aabb overall [minX, minY, maxX, maxY], or null with no box
points per query point: indices into boxes of containsPointAll(x, y) (its first is containsPoint)
segments per query segment: indices of intersectsSegmentAll (its first is intersectsSegment)
aabbs per query rectangle: aabbIntersects
others per placement: load the same document in a second instance, set its skeleton x, y to the placement, advance(0) (setup pose, default skin), and record intersectsBounds(theirs) as [my box index, their box index] or null

The generator drops any candidate query whose answer changes when nudged by 0.002 in some direction in some sample, so no answer depends on float rounding.

Numbers are rounded to 4 decimals and -0 is written as 0; round the same way (or compare with the tolerances above).

fixture covers
transforms every inherit mode, shear, non-uniform scale, all curve types (linear, stepped, presets, custom bezier with overshoot), colour, dark colour, attachment and draw-order keys, events with payloads
constraints 2-bone IK with softness, stretch, bend keys and mix keys; 1-bone IK with stretch/compress/uniform; world and local/relative transform constraints with offsets
meshes weighted mesh with deform keys, a linked mesh in another skin that follows them, an ellipse clipping mask, a path constraint (chain rotate mode) with a keyed position, two-colour tint
dynamics physics chain (inertia, strength, damping, gravity, wind) driven by a moving root, a bone-driven slider, a ping-pong sequence, a seeded particle emitter, a text template
interactive a state machine with number / trigger / bool / number / string inputs, a blend1d state, any-state transition, exit time, a pointer listener on a hit box (fire trigger + report event), an input binding, an event that plays a sound, and a script that counts clicks into a text template
bounds bounding-box queries (above): an unweighted polygon box, a box weighted to two bones, a box switched on and off by attachment keys, a box on a skin-only bone, a draw-order key that reorders boxes, a region that is ignored
paths path constraints on a curve with very uneven handles, with constant speed on and off: an open path (chain mode, keyed position running off both ends) and a closed one (tangent and chainScale, percent spacing, wrapping)
rigging skin bones and a skin IK + physics that exist only in the hero skin, inherit-mode keys, a custom constraint order (transform before IK), reference scale 50
mixing a walk → idle cross-fade where walk alone keys a bone (translation and a wrapping rotation), a colour, a dark colour, an attachment and a transform-constraint mix: the blending values fade back to the setup pose, the attachment keeps the walk’s key until the fade ends
example-* every project in examples/, all animations and state machines
atlas-polygons the knight packed with an octagon polygon per region (polygon packing) and premultiplied alpha
atlas-knight, atlas-walker the knight and walker packed into a texture atlas: uv remapping, trimmed regions, one shared page

OpenBone is MIT licensed and made by UXCentury. The example art, voices and sounds are credited in the repository.