alexpaynex 8d48eb06b3 feat(task-21): Timmy face expressions + emotion engine
## What changed
- the-matrix/js/agents.js — complete face expression system on Timmy wizard

## Face geometry (all parented to head — follow head.rotation.z tilt)
- White sclera eyes (MeshStandardMaterial f5f2e8, emissive 0x777777@0.10)
  replace old flat dark-blue eye spheres
- Dark pupil spheres (MeshBasicMaterial 0x07070f) as children of each sclera;
  they scale with the parent eye for squint + animate independently for dilation
- Mouth arc: TubeGeometry via QuadraticBezierCurve3; ctrlY = -smileAmount*0.065;
  rebuilt only when |smileDelta| > 0.016 (throttled, not per-frame GC)
- All face meshes are children of `head` mesh — head.rotation.z carries every
  face feature naturally with the existing head-tilt animation

## FACE_TARGETS table (lidScale, pupilScale, smileAmount)
- idle  (contemplative): 0.44 / 0.90 /  0.08 — half-lid, neutral
- active (curious):      0.92 / 1.25 /  0.38 — wide open + dilated pupils, smile
- thinking (focused):    0.30 / 0.72 /  0.00 — narrow squint + constrict, flat mouth
- working (attentive):   0.75 / 1.05 /  0.18 — alert/open eyes, slight grin

## setFaceEmotion(mood) — authoritative public API
- Accepts task-spec names (contemplative|curious|focused|attentive) and internal
  names (idle|active|thinking|working) via MOOD_ALIASES
- Sets timmy._overrideMood; persists across frames, takes precedence over
  deriveTimmyState() in updateAgents()
- Call with null to clear override and return to automatic state-driven expressions

## Per-frame lerp (rate 0.055/frame) in updateAgents
- Uses _overrideMood ?? deriveTimmyState() as effective mood
- lidScale → eyeL.scale.y / eyeR.scale.y (squash for squint/wide-open)
- pupilScale → pupilL/R.scale.setScalar() (uniform dilation)
- smileAmount → drives thresholded TubeGeometry rebuild

## Lip-sync while speaking (1 Hz, range 0.20–0.60)
- speechTimer > 0: smileTarget = 0.40 + sin(t*6.283)*0.20
- Returns to mood expression when timer expires

## Validation
- Vite build: clean (14 modules, no errors)
- testkit: 27/27 PASS (server restarted to clear rate-limit counters between runs)
2026-03-19 03:13:51 +00:00
2026-03-13 23:21:55 +00:00
2026-03-18 17:19:38 +00:00
2026-03-13 23:21:55 +00:00
2026-03-13 23:21:55 +00:00
2026-03-13 23:21:55 +00:00
Description
Timmy Tower World — sovereign AI agent economy
226 MiB
Languages
TypeScript 68.9%
JavaScript 15.5%
Shell 10.6%
HTML 4.5%
CSS 0.4%