VITTO AVATAR DOCUMENTATION

Documentation for Developers

Vitto Animated Avatar — Technical Documentation

Overview

Vitto is an animated cat avatar built as a self-contained TypeScript/Canvas 2D application. It renders a black silhouette cat character with expressive emotions, mouse-following eyes, idle blinking, and smooth mood transitions — all with advanced idle behavior systems, enhanced eyes, and ambient mood glow effects.

The visual design is inspired by the Rive marketplace "Cat following the mouse" by pedroalpera (#3920): a clean black cat silhouette on a golden background with big white eyes, whiskers, a curvy tail, and pink inner ears.


Tech Stack

Layer Technology
Rendering HTML5 Canvas 2D (CanvasRenderingContext2D)
Animation requestAnimationFrame at 60fps with delta-time
Language Pure TypeScript (NX library structure)
Styling Inline CSS
Dependencies None — fully self-contained
Bundle Size ~21KB (minified IIFE)

Why Canvas 2D?

We evaluated three approaches before settling on Canvas 2D:

  1. Rive Runtime — High quality but created integration complexity. Chrome's Private Network Access policy blocked localhost testing from HTTPS pages, and the Rive file format made it difficult to programmatically control individual facial features per-emotion.

  2. SVG/CSS Animation — Good for simple transitions but becomes unwieldy when animating 30+ properties simultaneously with custom easing. SVG DOM manipulation at 60fps can also cause layout thrashing.

  3. Canvas 2D (chosen) — Full programmatic control over every pixel, no DOM overhead, trivial to interpolate any numeric property between states, and the imperative drawing API maps naturally to "draw a cat face" logic.

Trade-offs

Advantage Limitation
Zero dependencies, works offline No vector export (rasterized)
Embeddable anywhere (iframe, React wrapper, Electron) Requires JS for rendering (no static fallback)
Full programmatic control over every visual element Drawing code is manual (no visual editor)
Smooth interpolation of unlimited properties Accessibility requires additional work (ARIA labels)
Tiny file size (~21KB) Resolution-dependent (mitigated by 2x DPI)

Architecture

Rendering Pipeline

requestAnimationFrame(render)
  → Compute idle behaviors (8-channel noise-driven system)
  → Update transition state (interpolate all properties)
  → Update blink timer
  → drawBg()          // Golden gradient background + mood glow + dynamic shadow
  → drawShadow()      // Ground shadow ellipse (scales with mood energy)
  → drawTail()         // Animated bezier curve tail + idle tailVariation
  → drawBody()         // Body ellipse + feet + idle bodyShiftX
  → drawHead()         // Head group (translated by mouse look + bounce)
      → drawEars()     // Triangular ears with pink inner + idle earTwitchL/R
      → drawEyes()     // 3-tier dynamic highlights + mood-tinted iris + eyeWideVariation
      → drawNose()     // Golden triangle nose with highlight
      → drawMouth()    // Muzzle, W-shaped mouth, open mouth, tongue, teeth
      → drawWhiskers() // 6 whiskers (3 per side)
      → drawBlush()    // Pink cheek circles with manga lines
      → drawBrows()    // Expressive eyebrow arcs
  → drawParticles()    // Sparkles, hearts, question marks, sweat, exclamation marks, zzz

Canvas Setup

Coordinate System


Mood System

The 14 Moods

Each mood is defined as an object with ~30 numeric properties that control every visual aspect of the cat's expression:

Mood Key Visual Features Accent Color Energy
Neutral Calm round eyes, relaxed posture #c4b5fd 0.5
Focused Dramatic squint, tiny pupils, furrowed brows #818cf8 0.6
Cheerful Happy arc eyes, blush + manga lines, tongue, floating hearts #f472b6 0.9
Empathetic Big soft eyes, worried brows, gentle head tilt, hearts #a78bfa 0.5
Curious Wide eyes, one raised brow, head tilt, blue question mark #34d399 0.7
Analytical Slightly squinted, asymmetric brows, question mark #60a5fa 0.6
Celebratory Happy arcs, open mouth + fangs + tongue, golden sparkles, bouncing #fbbf24 1.0
Apologetic Sad frown, worried brows, sweat drops, drooped ears, shrunk body #c4b5fd 0.3
Inspired Star-shaped eyes with purple glow, sparkles, open smile #c084fc 0.85
Urgent Huge eyes, tiny pupils, red exclamation marks, orange background #f87171 1.0
Playful Wide bright eyes, open smile, bouncy posture, colorful sparkles #ec4899 0.95
Confused Asymmetric eyes, tilted head, multiple question marks #f59e0b 0.5
Proud Steady eyes, raised chin, sparkle particles, golden aura #fbbf24 0.85
Sleepy Half-closed eyes, relaxed expression, zzz particles, slow breathing #6b7280 0.2

Animatable Properties

These are the numeric properties that smoothly interpolate between moods:

Eyes:

Mouth:

Brows:

Head & Body:

Idle Behavior (Computed):

Particle Effects:

Background:

Mood Transition System

When setMood(key) is called:

  1. All current property values are snapshot into S.from
  2. The transition timer S.tT resets to 0
  3. On each frame, S.tT increments by dt * 2.8 (completing in ~360ms)
  4. A cubic ease function t < 0.5 ? 4t³ : 1 - (-2t+2)³/2 is applied
  5. Every numeric property is lerped: current = lerp(from, target, ease(tT))
  6. Background RGB values are also lerped independently

This means any property change between any two moods is always smooth — there are no hard cuts or jarring transitions.


Idle Behavior System

Overview

The idle behavior system adds organic, subtle micro-animations that keep Vitto feeling alive and reactive during normal interaction. Rather than static poses, the avatar continuously exhibits small movements driven by noise functions when idle intensity is non-zero.

8-Channel Noise-Driven Architecture

The idle system runs 8 independent noise-driven animation channels:

Channel Target Behavior Range
earTwitchL Left ear angle offset Subtle ear flick ±0.02 rad
earTwitchR Right ear angle offset Subtle ear flick ±0.02 rad
headTiltIdle Head rotation variation Gentle side-to-side tilt ±0.03 rad
headBobY Head vertical position variation Soft up-down bobbing ±8 pixels
whiskerFlutter Whisker angle modulation Whisker wiggle ±0.04 rad
tailVariation Tail wag amplitude variation Tail throb/pulse ±0.15 multiplier
bodyShiftX Body horizontal sway Gentle weight shift ±6 pixels
eyeWideVariation Eye height variation Eye flutter/blink-like motion ±0.08 multiplier

Noise Generation

Idle Intensity Calculation

The idle intensity is inversely proportional to mood energy:

moodEnergy = mood.energy  // 0.2 (sleepy) to 1.0 (celebratory)
idleIntensity = 1.0 - moodEnergy

During sleepy mood (energy = 0.2), idle intensity peaks at 0.8, creating prominent micro-animations. During celebratory mood (energy = 1.0), idle intensity is 0, freezing all micro-animations.

Smoothing & Blending

Each computed idle value is smoothed using exponential blending:

smoothedValue = lerp(smoothedValue, noiseValue, 0.15)

A blend factor of 0.15 creates natural lag — rapid enough to feel organic, slow enough to avoid jitter.

Configuration

Idle behaviors are controlled via RendererConfig:

enableIdleBehaviors: boolean  // Default: true
idleBlendFactor: number       // Default: 0.15
idleAmplitude: number         // Default: 1.0 (scale factor for all channels)

Disable idling for performance-critical contexts or to freeze the avatar in a specific state.


Enhanced Eyes System

3-Tier Dynamic Highlights

The eye rendering system now includes three layers of highlights for realistic depth:

  1. Primary Gaze-Following Highlight — Main glossy spot that follows the mouse cursor, positioned at the pupil center offset
  2. Secondary Opposite Corner Highlight — Smaller accent highlight in the opposite corner, creating an occlusion/shadow effect for depth
  3. Tertiary Pulsing Sparkle — Micro-highlight that gently pulses based on sparkle intensity, adding shimmer and life

Mood-Tinted Iris

The iris color is dynamically derived from the current background RGB values, creating a subtle chromatic harmony:

irisColor = interpolateFromBG(mood.bg)  // Harmonizes eye color with mood background

This creates visual cohesion where the cat's eyes "glow" with the same chromatic tone as the surrounding aura.

Idle Eye Variation

The eyeWideVariation channel from the idle system creates gentle eye flutter even during neutral expressions, mimicking natural biological eye movement and blinking patterns.


Ambient Mood Glow & Dynamic Shadow

Ambient Mood Glow Aura

Behind the cat, a pulsing colored aura reacts to emotional intensity:

Dynamic Shadow Scaling

The ground shadow beneath the cat scales with mood energy:

This creates an intuitive visual metaphor where emotional intensity physically weighs the avatar down or lifts it up.


Interactive Features

Mouse-Following Pupils

Idle Blinking

UI Controls


API Integration

Basic Usage

To change Vitto's mood programmatically from your application:

// Direct call
setMood('cheerful');

// From an external iframe
document.getElementById('vitto-iframe').contentWindow.setMood('playful');

// Available mood keys (14 moods)
const moods = [
  'neutral', 'focused', 'cheerful', 'empathetic', 'curious',
  'analytical', 'celebratory', 'apologetic', 'inspired', 'urgent',
  'playful', 'confused', 'proud', 'sleepy'
];

Connecting to Vitto's Emotion API

// Example: poll emotion endpoint and update avatar
async function syncVittoMood() {
  const response = await fetch('https://your-api.com/vitto/emotion');
  const { mood } = await response.json();
  setMood(mood);
}

// Poll every 2 seconds
setInterval(syncVittoMood, 2000);

// Or use WebSocket for real-time updates (SSE stream integration planned)
const ws = new WebSocket('wss://your-api.com/vitto/ws');
ws.onmessage = (event) => {
  const { mood } = JSON.parse(event.data);
  setMood(mood);
};

Embedding Options

Iframe:

<iframe src="vitto-avatar.html" width="420" height="500" frameborder="0"></iframe>

React Wrapper:

function VittoAvatar({ mood }) {
  const iframeRef = useRef(null);

  useEffect(() => {
    if (iframeRef.current?.contentWindow?.setMood) {
      iframeRef.current.contentWindow.setMood(mood);
    }
  }, [mood]);

  return <iframe ref={iframeRef} src="/vitto-avatar.html" width={420} height={420} />;
}

File Structure

libs/libs-anim/vitto-avatar/src/lib/engine/
├── index.ts                    # Main entry point
├── renderer.ts                 # Canvas rendering orchestration
├── moods.ts                    # Mood definitions (14 moods)
├── types.ts                    # TypeScript type definitions
├── math.ts                     # Interpolation, easing, transforms
├── noise.ts                    # Simplex noise & FBM (NEW)
├── drawing/
│   ├── background.ts           # Mood glow + dynamic shadow (enhanced)
│   ├── body.ts                 # Idle bodyShiftX + tailVariation
│   ├── head.ts                 # Idle headBobY + headTiltIdle + earTwitchL/R
│   ├── face.ts                 # Enhanced eyes (3-tier highlights + mood iris)
│   ├── effects.ts              # Particle effects including zzz
│   └── shapes.ts               # Shape drawing primitives
└── behaviors/
    ├── breathing.ts            # Chest rise/fall animation
    ├── saccades.ts             # Eye movement patterns
    └── idle.ts                 # 8-channel idle behavior system (NEW)

Future Enhancement Roadmap

Phase 1: COMPLETED ✓

Phase 2: Expanded Properties

  1. Pupil Dilation — Eyes respond to mood intensity (wide pupils = excited/surprised, pinned pupils = focused)
  2. Ear Droop — Ears flatten and droop during sad/apologetic moods for additional expressiveness
  3. Chest Rise/Fall — Breathing animation that accelerates with mood energy
  4. Paw Curl — Front paws curl/relax based on emotional state
  5. Fur Ruffle — Localized fur bristle animation during tense moods
  6. Eye Moisture — Subtle glisten or tear effects during emotional moods

Phase 3: Live Emotional Expression

Future Enhancements


Design Decisions Log

Decision Rationale
Canvas 2D over Rive Full programmatic control needed for 30+ interpolated properties per mood; Rive's state machine couldn't express the granularity we needed
Canvas 2D over SVG Better performance for 60fps with many animated properties; no DOM thrashing
NX library structure Modular, testable, reusable components across the platform; enables proper TypeScript tooling and code splitting
840×840 internal resolution 2× DPI at 420px display for crisp rendering on all screens
Cubic easing for transitions Feels natural — slow start, fast middle, soft landing. More organic than linear or quadratic
Property-based mood system Adding a new mood is just adding an object with numeric values — no animation code changes needed
Threshold-based eye rendering happyEye > 0.5 and starEye > 0.5 allow smooth transitions where special eyes "pop in" at the midpoint
Noise-driven idle behaviors Simplex + FBM creates organic, unpredictable micro-motions without hardcoded animation sequences
Inverse mood energy for idle intensity Low-energy moods benefit most from micro-animation (sleepy = prominent twitches), while high-energy moods are already visually dynamic
Mood-tinted iris from background Creates chromatic harmony — the cat's eyes feel like they belong in the emotional environment
Dynamic shadow scaling Visual metaphor for emotional weight; grounded/heavy when energetic, light/withdrawn when sad