# Malin FC Avatar — MESH-WARP Expression Spec (`warp_v1`)
**Author: Calypso · 6/6 · Jun's idea (the Photoshop Liquify/Puppet-Warp instinct) → the Live2D approach. Spec to guide Hermes's cv2 warp prototype.**

## The pivot (why warp beats inpaint-composite)
Instead of inpainting/compositing generated expression patches onto neutral_warm (drift, seams, color jumps, doubled eyes), **WARP the approved `neutral_warm.png` geometry toward each expression.** This is how Live2D / vtuber avatars work: one image, mesh-deformed.
- **Identity never drifts** — we move HER actual pixels (face/freckles/skin), generate nothing.
- **No seams** — a smooth displacement field is continuous; no composite edges.
- **Color/texture perfect** — moving existing pixels, nothing can jump.

## Method
1. **MediaPipe Face Mesh** on `neutral_warm.png` → 468 landmarks = her canonical geometry. (Hand-placed control points work for a prototype, but landmarks make it principled + reusable.)
2. Per expression, define **target displacements** for the relevant landmark groups (the muscle moves below).
3. **Warp** via a displacement field: thin-plate-spline from the control-point moves, OR `cv2.remap` with a field interpolated (Gaussian-feathered) from the control deltas. (Hermes's cv2-remap displacement-field approach = correct.)
4. **Feather** the field (smooth falloff around each control point) so the warp is natural, not rubbery. Keep displacements SUBTLE — small moves read; big moves distort.

## ★ INTENSITY COMES FREE: scale the displacement field by `intensity` → the `[PERFORM: emotion intensity]` ladder from ONE warp. skeptical 0.3 = field×0.3, 0.9 = field×0.9. No need to render cool/mid/extreme separately.

## Per-expression warp targets (muscle moves, from Jun's reference photos + FACS — see [[malin_avatar_brow_spec]])
- **soft_smile** — mouth corners ↑ + slightly out; cheek apples ↑; lower lids nudged ↑ (smile crinkle).
- **affectionate** — soft_smile + brows ↑ (open/welcoming); eyes soft.
- **amused_smirk** — ONE mouth corner ↑ higher (asymmetric) + that-side cheek ↑.
- **deadpan** — ~zero warp (fully relaxed neutral).
- **judging_unamused** — upper lids slightly ↓ (hooded) + faint lip-corner press inward.
- **skeptical (cool→mid→extreme = SAME field scaled up)** — lids narrow a touch; lips press + push to one side; brows draw together + ↑ (worried); at extreme, mouth presses WIDE (corners out+down, grimace).
- **regretful** — eyelids ↓ heavy; mouth slightly open, corners faint ↓; slack. (GAZE down+away = the avatar's eye/gaze system, not the warp.)
- **focused / eager-listening** — eyes WIDE (upper lids ↑); brows ↑ slightly; mouth relaxed.
- **smartass** — ONE brow ↑ (cocked) + one mouth corner ↑ (smirk). **The TONGUE is the one thing warp CAN'T do** — that stays inpaint.

## LIMIT + the hybrid rule
Warp = GEOMETRY only (move/stretch existing features). It can't ADD content: tongue, teeth, strong new wrinkles. → **HYBRID: warp for the geometric expressions (almost all of them); inpaint ONLY where new content is needed (smartass tongue).** Best of both — warp keeps identity, inpaint adds the rare new thing.

## Live avatar payoff
neutral_warm + the per-expression displacement fields = a Live2D-style rig. Real-time = blend/scale fields per `[PERFORM:]`. Blink/breath/gaze already exist; warp slots in as the expression layer, replacing the brittle inpaint composites.

Links: [[malin_avatar_brow_spec]] (the muscle-move source of truth), [[project_malin_avatar]], [[malin_avatar_control_layer_pack]] (the inpaint approach this supersedes for geometric expressions).
