Files
2026-05-27 23:29:53 +01:00

4.9 KiB
Raw Permalink Blame History

OpenSpec Change: enterprise-gaming-animation-events

  • Change ID: enterprise-gaming-animation-events
  • Package: @suaveplan/animation-events
  • Category: gaming/rigs
  • Target path: gaming/rigs/animation-events
  • Status: PROPOSED
  • Date: 2026-04-24
  • Reference spec: /mnt/s/web/suaveplan/enterprise/animation-events.md

Summary

Deliver @suaveplan/animation-events on the genesis branch from scratch at enterprise quality. The package provides animation event dispatching, foot contact detection, and procedural animation modifiers. An AnimationEventTrack associates named events with normalized animation times (0–1); an AnimationEventDispatcher fires @suaveplan/events messages when playback crosses event timestamps. FootContactDetector analyses Y-velocity of ankle bones to determine ground contact frames. Procedural modifiers (head bob, breathing sway, aim offset) compose additively. Genesis adds Zod schemas, structured errors, OTel event-count counter, and 100% coverage.


Current-state audit (genesis)

Package does not exist on genesis yet — fresh build.


Available workspace deps on genesis

Package Role
@suaveplan/error (workspace:^) AnimationEventsError; ANIM_EVENT.* codes
@suaveplan/types (workspace:^) Clock
@suaveplan/events (workspace:^) Event bus for dispatching animation triggers
@suaveplan/ik-solver (workspace:^) solveLookAt for aim-offset modifier
zod (catalog:forms) Track and modifier schemas
@opentelemetry/api (catalog:observability) Event dispatch counter

Public API surface

// Event track
interface AnimationEvent {
  name: string
  normalizedTime: number   // 0–1
  data?: unknown
}

function createEventTrack(events: AnimationEvent[]): AnimationEventTrack

interface AnimationEventTrack {
  getEvents(fromNorm: number, toNorm: number): AnimationEvent[]
  readonly events: readonly AnimationEvent[]
}

// Dispatcher
function createAnimationEventDispatcher(options: DispatcherOptions): AnimationEventDispatcher

interface DispatcherOptions {
  eventBus: EventBusLike
  namespace?: string    // event name prefix, default 'anim'
}

interface AnimationEventDispatcher {
  update(clipName: string, track: AnimationEventTrack, normalizedTime: number, prevNormalizedTime: number): void
}

// Foot contact detection
function createFootContactDetector(options: FootContactOptions): FootContactDetector

interface FootContactOptions {
  ankleThresholdY?: number    // velocity below which foot is considered planted, default 0.05
  sampleWindow?: number       // frames to average, default 3
}

interface FootContactDetector {
  update(leftAnkleY: number, rightAnkleY: number, dt: number): FootContactState
}

interface FootContactState {
  leftGrounded: boolean
  rightGrounded: boolean
  leftPlantedAt?: Vec3
  rightPlantedAt?: Vec3
}

// Procedural modifiers
interface ProceduralModifier {
  update(dt: number, input: ModifierInput): BonePose[]
}

function createHeadBobModifier(options: HeadBobOptions): ProceduralModifier
function createBreathingModifier(options: BreathingOptions): ProceduralModifier
function createAimOffsetModifier(options: AimOffsetOptions): ProceduralModifier

Error codes

type AnimEventErrorCode =
  | 'ANIM_EVENT_TRACK_EMPTY'
  | 'ANIM_EVENT_INVALID_TIME'
  | 'ANIM_EVENT_DISPATCH_FAILED'

New additions for genesis

  1. Looping event detection — dispatcher handles wrap-around (prevNorm > normTime) correctly, firing events in the wrapped range.
  2. Foot IK plant correction — FootContactDetector outputs planted world positions; caller feeds into @suaveplan/ik-solver for foot placement.
  3. Aim offset modifier — uses solveLookAt to rotate spine/neck toward aim target additively.

File layout

gaming/rigs/animation-events/
  package.json
  tsconfig.json
  vitest.config.ts
  src/
    index.ts
    error-codes.ts
    constants/index.ts
    types/common.ts
    types/common.md
    core/
      event-track/
        event-track.ts
        event-track.md
        event-track.types.ts
        event-track.test.ts
        index.ts
      dispatcher/
        dispatcher.ts
        dispatcher.md
        dispatcher.types.ts
        dispatcher.test.ts
        index.ts
      foot-contact/
        foot-contact.ts
        foot-contact.md
        foot-contact.types.ts
        foot-contact.test.ts
        index.ts
    modifiers/
      head-bob/
        head-bob.ts
        head-bob.md
        head-bob.test.ts
        index.ts
      breathing/
        breathing.ts
        breathing.md
        breathing.test.ts
        index.ts
      aim-offset/
        aim-offset.ts
        aim-offset.md
        aim-offset.test.ts
        index.ts
  tests/
    integration.test.ts

Impact

Downstream: @suaveplan/r3f-animation hooks these modifiers into Three.js AnimationMixer. Prerequisites: @suaveplan/error, @suaveplan/types, @suaveplan/events, @suaveplan/ik-solver.