When Apple shipped face tracking with ARKit, it described the face as 52 named shapes, each a value from 0 to 1. A tracked face is simply 52 numbers per frame. The naming stuck: Character Creator, MetaHuman (through Live Link), the former Ready Player Me avatars, VRM avatars with "Perfect Sync" shapes and most avatar tools use the same names, so curves made for one character can drive another.
How a blend shape works
A blend shape (also called a morph target or shape key) is a copy of the face mesh with the vertices moved into one expression, like the jaw fully open. At runtime the engine mixes them:
final face = neutral face + Σ weight × (shape − neutral face)
A weight of 0 leaves the face neutral, 1 applies the full shape, and shapes add up. jawOpen at 0.4 plus mouthFunnel at 0.6 gives a rounded, half-open mouth: the "o" in "go".
The full list
Eyes (14): eyeBlinkLeft, eyeLookDownLeft, eyeLookInLeft, eyeLookOutLeft, eyeLookUpLeft, eyeSquintLeft, eyeWideLeft, and the same seven for the right eye.
Jaw (4): jawForward, jawLeft, jawRight, jawOpen.
Mouth (23): mouthClose, mouthFunnel, mouthPucker, mouthLeft, mouthRight, mouthSmileLeft, mouthSmileRight, mouthFrownLeft, mouthFrownRight, mouthDimpleLeft, mouthDimpleRight, mouthStretchLeft, mouthStretchRight, mouthRollLower, mouthRollUpper, mouthShrugLower, mouthShrugUpper, mouthPressLeft, mouthPressRight, mouthLowerDownLeft, mouthLowerDownRight, mouthUpperUpLeft, mouthUpperUpRight.
Brows (5): browDownLeft, browDownRight, browInnerUp, browOuterUpLeft, browOuterUpRight.
Cheeks (3): cheekPuff, cheekSquintLeft, cheekSquintRight.
Nose (2): noseSneerLeft, noseSneerRight.
Tongue (1): tongueOut.
The shapes speech uses
Speech is mostly jaw and lips. Of the 52, the custom AI model behind our hosts' lip sync outputs 29: the 4 jaw shapes, all 23 mouth shapes, tongueOut and cheekPuff. Eyes and brows are left to the emotion and gaze systems, which move on their own timing.
The ones that carry the most meaning:
| Shape | What it does | Heard in |
|---|---|---|
jawOpen |
Drops the jaw | Open vowels: "a" in "father" |
mouthClose |
Closes the lips while the jaw is open | Holding the lips shut after an open sound |
mouthPressLeft/Right |
Presses the lips together | "p", "b", "m" |
mouthFunnel |
Rounds the lips outward | "o", "sh", "ch" |
mouthPucker |
Pushes the lips into a small kiss | "oo", "w" |
mouthRollLower |
Rolls the lower lip in, under the teeth | "f", "v" |
mouthStretchLeft/Right |
Pulls the corners sideways | "ee", "s" |
mouthUpperUpLeft/Right |
Raises the upper lip | "f", "v", and showing teeth |
mouthLowerDownLeft/Right |
Lowers the lower lip | Open, spread vowels |
mouthSmileLeft/Right |
Lifts the corners | Expression, and "ee" |
tongueOut |
Shows the tongue | "th" |
Two habits make speech read well. Closures (p, b, m) must actually close: the lips have to meet for a frame or two, even in fast speech. And rounding (oo, w, o) must start before the sound, because real lips round in advance. Rule-based lip sync often misses both; a good learned model gets them right.
Visemes vs blend shape curves
Older systems use visemes: one mouth pose per group of sounds, such as the 15 Oculus visemes (sil, PP, FF, TH, DD, kk, CH, SS, nn, RR, aa, E, ih, oh, ou). Each frame picks or blends a few poses.
ARKit curves describe the mouth as many independent muscles, so co-articulation (one sound shaping the next) comes out naturally. A viseme set can be built from ARKit shapes, but not the other way round, which is why ARKit is the better target for new characters.
Checking a model
In three.js, every mesh with blend shapes has a morphTargetDictionary mapping names to indices:
gltf.scene.traverse((o) => {
if (o.morphTargetDictionary) console.log(o.name, Object.keys(o.morphTargetDictionary));
});
Look for the ARKit names on the head, and on the teeth and tongue meshes too: the lower teeth must follow jawOpen, or the mouth opens around a fixed set of teeth. Next: driving these shapes from speech in three.js.