Skip to content

Animation State Machine ​

This document describes the avatar animation system, including the state machine, transitions, and visual expressions.

Overview ​

The avatar uses a finite state machine to manage animations and visual feedback. Each state has distinct visual characteristics expressed through eye shapes, mouth curves, and animation parameters.

State Diagram ​

States ​

StateDescriptionEye ShapeMouth
IDLEDefault resting state○ ○ (circle)Neutral
AWAREUser detected, attentive○ ○ (circle)Neutral
LISTENINGVoice input active◎ ◎ (large)Neutral
THINKINGProcessing/reasoning− − (closed)Neutral
SPEAKINGTTS playback with lip-sync○ ○ (circle)Animated
CONFIRMINGAwaiting user confirmation○ ◯ (one tilted)Slight smile
ERRORError state (non-aggressive)× × (X marks)Troubled
SLEEPLow-power/waiting mode− − (closed)Neutral

Visual Expression System ​

Eye Shapes ​

Circle (default):    ○ ○     - IDLE, AWARE, SPEAKING
Large circle:        ◎ ◎     - LISTENING (more attentive)
Closed (line):       − −     - SLEEP, THINKING
X marks:             × ×     - ERROR
Tilted:              ○ ◯     - CONFIRMING (one eye tilted)

Mouth Curves ​

StateCurve ValueVisual
ERROR-0.5Slightly downturned (troubled)
CONFIRMING0.2Slight upturn (friendly)
Others0Neutral

Wake Word Integration ​

When wake word mode is enabled, the avatar displays SLEEP state while waiting for the wake word phrase.

Effective State Override ​

typescript
const effectiveAvatarState = useMemo(() => {
  if (isWakeWordEnabled && wakeWordMode === "waiting" && !isSpeaking && !isThinking) {
    return "SLEEP";
  }
  return avatarState;
}, [isWakeWordEnabled, wakeWordMode, isSpeaking, isThinking, avatarState]);

Animation Parameters ​

Per-State Configuration ​

StateBlink IntervalBreathing CycleGaze Tracking
IDLE8-14s5.5-6.5sNo
AWARE6-10s4.5-5.5sYes (250ms delay)
LISTENING4-8s4-5sYes (200ms delay)
THINKING3-6s3.5-4.5sNo (internal focus)
SPEAKING4-8s4-5sYes (300ms delay)
CONFIRMING5-9s4.5-5.5sYes (200ms delay)
ERROR6-10s5-6sNo
SLEEP15-25s7-9sNo

Timing Configuration ​

typescript
const TIMING_CONFIG = {
  maxResponseDelay: 150,      // Max acceptable response delay (ms)
  lingeringDuration: 2000,    // Fade effect after speaking ends
  longThinkingThreshold: 5000, // Show pulse after 5s in THINKING
  errorAutoDismiss: 3000,     // Auto-dismiss error state
  sleepShiftInterval: 90000,  // Burn-in prevention (90s)
};

Architecture ​

File Structure ​

src/
├── lib/animation/
│   ├── types.ts          # Type definitions
│   ├── config.ts         # Animation parameters
│   ├── state-machine.ts  # State machine implementation
│   ├── transitions.ts    # Transition definitions
│   ├── broadcast-adapter.ts  # BroadcastChannel integration
│   └── index.ts          # Public exports
├── hooks/
│   ├── useAvatarState.ts        # State machine hook
│   ├── useAnimationController.ts # Animation values hook
│   └── useLongThinkingPulse.ts  # Long thinking indicator
└── components/
    └── SimpleAvatar.tsx   # Three.js avatar component

Events ​

EventDescriptionTypical Transition
USER_DETECTEDUser presence detectedIDLE → AWARE
USER_LOSTUser no longer detectedAWARE → IDLE
MIC_ACTIVATEDMicrophone enabled→ LISTENING
MIC_DEACTIVATEDMicrophone disabled→ IDLE
PROCESSING_STARTLLM processing beginsLISTENING → THINKING
PROCESSING_ENDLLM processing endsTHINKING → IDLE
TTS_STARTText-to-speech startsTHINKING → SPEAKING
TTS_ENDText-to-speech endsSPEAKING → IDLE
ERROR_OCCURREDError detected→ ERROR
ERROR_DISMISSEDError acknowledgedERROR → IDLE
SLEEP_TIMERIdle timeoutIDLE → SLEEP
WAKEWake eventSLEEP → IDLE

Demo Page ​

A demo page is available at /demo for testing state transitions:

  • Keyboard Controls: Press 1-8 to force states
  • Space Bar: Test mouth animation
  • Mouse Movement: Test gaze tracking
[1] IDLE
[2] AWARE
[3] LISTENING
[4] THINKING
[5] SPEAKING
[6] CONFIRMING
[7] ERROR
[8] SLEEP

Released under the MIT License.