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

@certe/atmos-animation

v0.8.8

Published

Skeletal animation system for the Atmos Engine — keyframes, blending, cross-fade

Readme

🎬 @certe/atmos-animation

Skeletal animation system for the Atmos Engine. Provides skeleton data structures, keyframe sampling, pose blending, and an AnimationMixer component for clip playback and cross-fading.


🔑 Key Concepts

  • Skeleton — Joint hierarchy with inverse bind matrices and a rest pose
  • AnimationClip — Named collection of keyframe tracks targeting individual joints
  • AnimationMixer — Component that plays clips, blends weighted layers, and outputs bone matrices for GPU skinning

🚀 Quick Start

import { AnimationMixer, createSkeleton, createAnimationClip } from '@certe/atmos-animation';

// Typically created automatically by instantiateModel() for glTF skinned meshes
const mixer = gameObject.addComponent(AnimationMixer);
mixer.skeleton = skeleton;
mixer.addClip(walkClip);
mixer.addClip(runClip);

// Play
const walkLayer = mixer.play(walkClip, { loop: true });

// Cross-fade to run over 0.3 seconds
const runLayer = mixer.play(runClip, { loop: true, weight: 0 });
mixer.crossFade(walkLayer, runLayer, 0.3);

📖 API Overview

Skeleton

import { createSkeleton, getInverseBindMatrix } from '@certe/atmos-animation';

const skeleton = createSkeleton(
  joints,               // Array<{ name, parentIndex }>
  inverseBindMatrices,  // Float32Array (jointCount × 16)
  restT, restR, restS   // Optional rest pose arrays
);

// Read one joint's IBM
const ibm = Mat4.create();
getInverseBindMatrix(ibm, skeleton, jointIndex);

AnimationClip & Tracks

import { createAnimationClip } from '@certe/atmos-animation';

const clip = createAnimationClip('walk', [
  {
    jointIndex: 0,
    channel: 'rotation',
    interpolation: 'LINEAR',
    times: new Float32Array([0, 0.5, 1.0]),
    values: new Float32Array([/* quaternion keyframes */]),
  },
]);
// clip.duration is auto-computed from max track time

Keyframe Sampling

import { sampleTrack } from '@certe/atmos-animation';

const out = new Float32Array(4); // 4 for rotation, 3 for translation/scale
sampleTrack(out, track, time);
// Binary search + LINEAR (lerp/slerp) or STEP interpolation

AnimationMixer Component

| Method | Description | |---|---| | play(clip, opts?) | Play a clip, returns AnimationLayer | | playByName(name, opts?) | Play by clip name with optional cross-fade | | crossFade(from, to, duration) | Smooth transition between layers | | stop(layer) | Stop and remove a layer | | resetToRestPose() | Clear all layers | | addClip(clip) | Register a clip for playByName |

| Property | Description | |---|---| | skeleton | The Skeleton to animate | | boneMatrices | Float32Array output (jointCount × 16) for GPU upload | | clipNames | Sorted list of registered clip names | | speed | Global playback speed multiplier | | loop | Default looping behavior |

Blending Algorithm

Each frame, onUpdate(dt):

  1. Sample all active layers' tracks at their current time
  2. Blend translation/scale as delta-from-rest weighted by layer weight
  3. Blend rotation via weighted quaternion accumulation with shortest-path sign flip
  4. Fill undriven joints with rest pose
  5. Call computeBoneMatrices() to produce final GPU-ready matrices

📁 Structure

packages/animation/src/
  index.ts            # Public API
  skeleton.ts         # Skeleton type + factory
  animation-clip.ts   # AnimationClip + KeyframeTrack
  keyframe-sampler.ts # Binary search + lerp/slerp (zero-alloc)
  pose.ts             # computeBoneMatrices() — two-pass world × IBM
  animation-mixer.ts  # AnimationMixer component
  animation-handler.ts # Animation event handling
  register-builtins.ts # Component registry integration

🔗 Dependencies

  • @certe/atmos-core — Component lifecycle
  • @certe/atmos-math — Vec3, Mat4, Quat for pose computation and interpolation