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.