Genies Web SDK v0.1.35
Updated: April 29, 2026
What's New
- Shader State Getters
naf.isToonEnabled()andnaf.isOutlineEnabled()are now exposed on the SDK instance.
- Channel Info
getChannelInfo(channel).currentTimenow wraps into[0, duration)for looping channels (idle, breath, base emotion, etc.) instead of accumulating without bound. Consumers no longer need to computecurrentTime % durationthemselves, and long-running idle channels won't lose float precision after hours of playback. Non-looping channels are unchanged.
- Channel Scrubbing
- Added
controller.getChannelCurrentTime(channel)andcontroller.setChannelCurrentTime(channel, time)to read and seek the playback time of any animation channel. setChannelCurrentTimeenables backward scrubbing and jump-to-frame on gesture/idle/emotion channels — the per-frameupdate(dt)loop only advances time, so any non-forward seek previously had no public path. The new time takes effect on the next pose evaluation.getChannelCurrentTimeis a lighter alternative togetChannelInfo(channel).currentTimefor tight polling loops (no struct allocation).- No clamping is applied: callers are responsible for keeping
timewithin[0, duration)(or wrapping themselves for looping channels).
- Added
- Layer-Agnostic Lifecycle Events
avatar:readyandavatar:loadednow fire fromloadAvatar(Layer 1) too — previously they only fired fromloadAvatarProgressive(Layer 3), so consumers subscribing vianaf.on('avatar:loaded', ...)never saw completion when using the basic loader. Payloads are unchanged: Layer 1 emitspreviousMesh: nullonavatar:ready(no silhouette handoff), andhandle.controllerisnullonavatar:loaded(attach animation separately viaattachAnimation). Layer 3 behavior is unchanged.
- Animation Debug — Per-Avatar Instances
initAnimationDebug(mesh, poseSynchronizer, scene, avatarId?)now creates a fresh debug controller per call instead of mutating a global singleton. Multiple avatars no longer overwrite each other's bind-pose snapshot, sync wrap, or bone filter state.- When
avatarIdis provided, the API is registered atwindow.nafDebugAvatars[avatarId]for targeted console use.window.nafDebugAnimationcontinues to alias the most-recently-initted instance for backwards compatibility. - New
disposeAnimationDebug(avatarId)releases a single avatar's debug instance. The sandbox now calls this on per-avataravatar:unloadedinstead of tearing down all animation UI — fixes a latent bug where removing one of several loaded avatars would clear the panel for the rest. initAnimationDebugnow returns the API object so callers can hold a reference instead of going throughwindow.
- Bind-Pose Correctness
- The bind-pose snapshot used by
resetAllToBindPose/resetBoneis now derived frommesh.skeleton.boneInversesfor skinned bones — those matrices are immutable from skin-bind time, so the snapshot is correct regardless of whetherinit()runs before or after animation has been applied. Non-skinned control bones still fall back to the bone's current transform on first init only. init()is now idempotent on the same mesh: re-init preserves the existing bind-pose snapshot and bone filter state instead of clobbering them.- The ambiguous
nafDebugAnimation.snapshotBindPose()console command (which actually captured current pose, not bind pose) is replaced by two explicit commands: setCurrentAsBindPose()— override bind pose with the avatar's current pose (the previous behavior, made explicit).recaptureBindPose()— re-derive the bind pose fromskeleton.boneInverses(revert any earlier override).
- The bind-pose snapshot used by