npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@blcklab/anyo-animation

v0.1.4

Published

Optional renderer-neutral animation runtime for Anyo with parameters, state machines, events, root motion, authoring contracts, and an explicit Sekai64 adapter.

Readme

@blcklab/anyo-animation

Optional, renderer-neutral animation behavior for Anyo worlds.

The package owns authored playback rules: typed parameters, state machines, deterministic transitions, crossfades, marker actions, snapshots, lifecycle handling, and root-motion ownership. Low-level clip sampling, skeletons, morph targets, and mixers remain in the renderer adapter.

Anyo JSON
   ↓
@blcklab/anyo-animation
   ↓
renderer-neutral animation adapter
   ↓
@blcklab/anyo-animation/sekai64
   ↓
@blcklab/sekai64/animation

The root entry has zero runtime dependencies and never imports Sekai64.

Install

npm install @blcklab/anyo-animation @blcklab/anyo

For the official Sekai64 integration:

npm install @blcklab/sekai64

Quick start

import { createWorld } from '@blcklab/anyo'
import { entitiesPlugin } from '@blcklab/anyo/entities'
import { createSekai64AnimationRuntime } from '@blcklab/anyo-animation/sekai64'

const animation = createSekai64AnimationRuntime({
  canvas: document.querySelector('#world') as HTMLCanvasElement,
  backend: 'auto',
})

const world = createWorld({
  renderer: animation.renderer,
  plugins: [entitiesPlugin(), animation.plugin],
})

await world.load('/world.json')
world.start()

The Sekai64 integration installs its optional animation renderer module lazily and registers the explicit animated-model asset loader. Static Anyo projects remain unchanged.

Player / shared VRM integration

Hosts that install renderer modules directly can use createSekai64AnimationIntegration() and pass the same module to the Avatar VRM loader. The animation adapter can then bind a compatible animated GltfModelNode even when that model was loaded by the VRM loader rather than by Animation's own animated-model loader.

import { createSekai64AnimationIntegration } from '@blcklab/anyo-animation/sekai64'
import { createSekai64VrmAssetLoader } from '@blcklab/anyo-avatar/vrm/sekai64'

const animation = createSekai64AnimationIntegration()

const renderer = {
  modules: [animation.module],
  assetLoaders: [
    animation.assetLoader,
    createSekai64VrmAssetLoader({ animationModule: animation.module }),
  ],
}

const plugins = [animation.plugin]

This is additive: createSekai64AnimationRuntime() and the existing animated-model workflow remain supported.

Authored component

{
  "type": "anyo.animation",
  "clips": {
    "idle": "Idle",
    "walk": "Walk",
    "run": "Run",
    "jump": "Jump"
  },
  "autoplay": true,
  "parameters": {
    "moving": { "type": "boolean", "default": false },
    "speed": { "type": "number", "default": 0, "min": 0, "max": 5 },
    "jump": { "type": "trigger" }
  },
  "stateMachine": {
    "initial": "idle",
    "states": {
      "idle": { "clip": "idle" },
      "walk": { "clip": "walk" },
      "run": { "clip": "run" },
      "jump": { "clip": "jump", "loop": "once" }
    },
    "transitions": [
      {
        "from": "idle",
        "to": "walk",
        "when": { "speed": { "greaterThan": 0.1 } },
        "duration": 0.2
      },
      {
        "from": "*",
        "to": "jump",
        "when": { "jump": { "triggered": true } },
        "priority": 100,
        "duration": 0.1
      },
      {
        "from": "jump",
        "to": "idle",
        "exitTime": 1,
        "duration": 0.15
      }
    ]
  },
  "markers": {
    "footstep-left": {
      "action": "audio.play",
      "params": { "clip": "footstep-left" }
    }
  },
  "rootMotion": {
    "mode": "horizontal-only",
    "node": "Hips"
  }
}

Runtime control

animation.plugin.play('hero', 'walk')
animation.plugin.transitionTo('hero', 'run', 0.25)
animation.plugin.pause('hero')
animation.plugin.resume('hero')
animation.plugin.seek('hero', 0.5)
animation.plugin.setSpeed('hero', 1.25)
animation.plugin.stop('hero')

Commands issued before an animated asset finishes loading are retained and applied after the binding becomes available.

Parameters

animation.plugin.setBoolean('hero', 'moving', true)
animation.plugin.setNumber('hero', 'speed', 2.5)
animation.plugin.setTrigger('hero', 'jump')
animation.plugin.resetTrigger('hero', 'jump')

const speed = animation.plugin.getParameter('hero', 'speed')

Number parameters are clamped to their authored minimum and maximum. Triggers are one-shot values and are consumed automatically by matching transitions.

Subscriptions and host bindings

const unsubscribe = animation.plugin.subscribeParameter(
  'hero',
  'speed',
  (speed) => console.log(speed),
)

const unbind = animation.plugin.bindParameter(
  'hero',
  'speed',
  () => movementController.speed,
)

Bindings are renderer-neutral and do not import controls, physics, Player, or Editor packages.

State machines

Transitions support:

equals
notEquals
greaterThan
greaterThanOrEqual
lessThan
lessThanOrEqual
triggered
notTriggered

Additional policies:

all / any condition mode
any-state transitions with "*"
normalized exit time
priority
interruptible transitions
trigger consumption
crossfade duration
state entry and exit actions

Sekai64 executes the blend. Anyo Animation determines when and why a state changes.

Marker actions

Sekai64 clip markers are forwarded as animation:marker events. Authored marker bindings run existing Anyo actions using JSON-compatible parameters. Executable JavaScript is never stored in world JSON.

Root motion

Supported modes:

disabled
extract-only
apply-to-entity
apply-to-controller
horizontal-only

For Sekai64, the selected root translation track is removed from the rendered clip and sampled separately. This prevents double motion when the extracted delta is written into Anyo's transient transform store.

apply-to-controller and extract-only emit animation:root-motion; applications can also use onRootMotion.

Snapshots

const entityParameters = animation.plugin.createParameterSnapshot('hero')
animation.plugin.restoreParameterSnapshot('hero', entityParameters)

const session = animation.plugin.createSnapshot()
localStorage.setItem('animation', JSON.stringify(session))
animation.plugin.restoreSnapshot(JSON.parse(localStorage.getItem('animation')!))

Snapshots contain plain JSON-compatible values and never revive entities that are no longer present.

Player and recovery lifecycle

The plugin reacts to Player/world pause and resume events and reconciles after renderer recovery or world replacement. It cleans up bindings, subscriptions, parameter bindings, root-motion transform layers, cloned root-motion clips, and mixer listeners.

For explicit teardown:

await animation.plugin.disposeAsync()

World actions

The plugin registers:

animation.play
animation.state
animation.parameter
animation.trigger

These make animation control available to other data-driven Anyo systems without direct package coupling.

Authoring integration

Editor and generator tools should import the optional authoring subpath:

import {
  createAnimationComponentDefinition,
  listAnimationClipReferences,
  validateAnimationComponentData,
} from '@blcklab/anyo-animation/authoring'

This entry is renderer-neutral and safe for Editor forms, prefab validation, inspectors, and schema-driven tooling.

Custom renderer adapters

import { createAnimationPlugin } from '@blcklab/anyo-animation'

const animation = createAnimationPlugin({
  adapter: myAnimationAdapter,
  strict: true,
  onDiagnostic(diagnostic) {
    console.warn(diagnostic.code, diagnostic.message)
  },
})

Implement AnimationRuntimeAdapter and AnimationEntityBinding. Crossfades, events, normalized time, and root-motion extraction are optional capabilities; basic playback remains supported.

Package boundaries

@blcklab/anyo-animation
  parameters, state machines, actions, snapshots, lifecycle

@blcklab/anyo-animation/authoring
  editor-safe component creation and validation

@blcklab/anyo-animation/sekai64
  animated glTF/GLB loader, mixers, crossfades, markers, root motion

Stable guarantees

See Compatibility for the 0.1.x compatibility contract.

Externally prepared clips

Renderer-specific importers/retargeters can prepare Sekai64 AnimationClip objects for an already-loaded model and hand playback ownership to Anyo Animation:

const unregister = integration.adapter.registerExternalClips(model, clips, {
  restorePose,
})

This hook does not parse or retarget animation data. The producer owns the clips and must dispose them after unregistering. Anyo Animation owns mixer/play/stop/crossfade behavior while the registration is active. This is intended for integrations such as VRMA humanoid retargeting without duplicating the renderer animation runtime.