7ed55b00a6
Root causes of previous issues: 1. Jumping behavior: The old implementation used setState() directly to random positions instead of interpolating toward them. Each idle movement instantly teleported the eyes. 2. Mouse tracking failure: The updateMouseTracking callback had isTracking in its dependencies, causing it to be recreated on every state change. This restarted the animation frame loop constantly, breaking the continuous tracking. 3. State conflict: Idle timeouts and tracking animation frames ran independently and fought each other, causing erratic behavior. Solution - Single animation loop architecture: - ONE requestAnimationFrame loop handles ALL animation - Maintains separate 'current' and 'target' positions - Always interpolates: current = lerp(current, target, smoothing) - Idle behavior only sets new targets (doesn't move directly) - Mouse tracking overrides targets when cursor is nearby - Clean state machine: tracking active = idle paused Smoothing values used: - IDLE_SMOOTHING = 0.03 (very smooth drift) - TRACKING_SMOOTHING = 0.08 (responsive but not snappy) - RETURN_SMOOTHING = 0.04 (gentle return to idle) Timing improvements: - Idle duration: 3-6 seconds between movements - 40% chance to pause at center (natural resting) - Time-scaled smoothing for consistent feel across frame rates Movement constraints: - Baby: 2px max, Adult: 2.5px max - Vertical movement reduced to 70% of horizontal - State updates throttled (only when position changes > 0.001px)