Retargeting the 15 visemes onto other rigs¶
OpenFaceFX channels are the Oculus/Meta 15-viseme set. openfacefx.retarget
maps them onto other conventions:
from openfacefx import generate_naive, retarget, rename_only, PRESETS, write_json
track = generate_naive("hello world", duration=1.2)
write_json(retarget(track, PRESETS["arkit"]), "track_arkit.json")
# Ready Player Me / Oculus-reference rigs just prefix the same visemes — the
# readyplayerme preset (below) is exactly this rename:
write_json(retarget(track, rename_only(prefix="viseme_")), "track_rpm.json")
Each mapping sends a viseme to one or more target shapes with a weight scale;
retarget resamples on the union of key times, sums contributions, clamps to
[0, 1]. Presets are plain data — copy one and tune it to your mesh.
Presets and their provenance¶
| Preset | Target convention | Provenance |
|---|---|---|
arkit |
Apple ARKit 52 blendshapes | Weights reproduced verbatim from met4citizen/TalkingHead (MIT), a shipping viseme→ARKit map. Apple's canonical shape list: ARFaceAnchor.BlendShapeLocation. |
rhubarb |
Rhubarb Lip Sync mouth shapes A–H + X | Shape semantics from the Rhubarb README; nearest-pose assignment is ours. Uses extended shapes G (F/V) and H (L); if your rig only has the six basic shapes, remap G→A, H→C. |
preston_blair |
Preston-Blair series (Papagayo/Moho/OpenToonz) | Shape set from garycmartin.com and Rhubarb's DAT exporter; assignment ours. Consonant catch-all is named etc (the exact layer name OpenToonz/Moho expect); WQ is omitted — viseme-level input can't split W from UW. |
vrm |
VRM 1.0 expression presets (aa ih ou ee oh) |
Preset semantics from the VRM spec. The five vowels map canonically; VRM has no consonant visemes, so consonants borrow the nearest vowel mouth at reduced weight and PP/sil rest at zero — coarse by design. |
vrm0 |
VRM 0.x BlendShapePreset vowels (A I U E O) |
Preset names from the VRM 0.0 spec: VRM 0.x names the lip-sync presets with uppercase single letters, which the spec pairs one-to-one with 1.0's names — A (aa), I (ih), U (ou), E (E), O (oh). Same five-vowel projection and consonant borrowing as vrm. Use this for VRoid Studio / VRM 0.x exports; use vrm for VRM 1.0. |
cc4 |
Reallusion CC4 / iClone viseme panel (also CC3) | Names from the Reallusion manual; CC4's set is nearly a 1:1 rename of the Oculus 15. The same Viseme Panel phoneme-pair labels back CC3 and CC4 — CC4's ExPlus / "CC4 Extended" additions sit in the facial-profile layer beneath the panel, not in these names — so this preset covers CC3 too. |
readyplayerme |
Ready Player Me Oculus visemes (viseme_*) |
Ready Player Me avatars expose the Oculus 15 as morph targets named viseme_<name> in verbatim Oculus casing (RPM Oculus OVR LipSync; names per the Meta viseme reference). Identical to rename_only(prefix="viseme_"). RPM also ships the ARKit 52 blendshapes — drive those with the arkit preset instead. |
Known quirks worth knowing¶
arkit: kept as published —CHandRRare identical combos, andPPseals withmouthRollLower/Upperrather thanmouthClose. If PP reads weak on your mesh, try{mouthClose: 0.9, mouthPressLeft/Right: 0.3}instead.- Meta does not publish an official Oculus-viseme→ARKit table; every such mapping (including this one) is community-made. Treat weights as starting points.
- Rhubarb/Preston-Blair are pose-based: several consonant visemes collapse onto one "consonants"/B shape. That is how those conventions work, not data loss you can tune away.
vrm/vrm0carry only five vowel mouths, so consonants are coarse (see the table). They are the same projection under two spec versions — pickvrm0for VRM 0.x / VRoid Studio uppercase presets,vrmfor VRM 1.0.
Rigs covered by an existing preset (no new table needed)¶
Some ecosystems reuse another rig's shape names, so they want documentation, not a duplicate table:
- Meta Avatars / Quest: the Oculus/Meta LipSync SDK's 15 visemes are
OpenFaceFX's native channel set (
sil PP FF … O U, Meta viseme reference), so no retarget is needed — orrename_only()for a bare rename. Ready Player Me prefixes them (viseme_*): thereadyplayermepreset. - Epic MetaHuman: the face rig exposes the 52 ARKit shapes as poses with a
bundled ARKit mapping asset (
mh_arkit_mapping_pose), soarkitoutput is the right input for the standard MetaHuman ARKit route. MetaHuman Animator's own ~130CTRL_expressionscurves are a different, proprietary layer — community remaps exist (e.g. AntiAnti/MetahumVisemeCurves' 12LS_*poses) but there is no authoritative table to ship. - NVIDIA Audio2Face-3D: emits ARKit
blendshapes,
so drive it with
arkit. A2F does not animate the tongue — passavailable=withouttongueOutand the arkit fallback reroutes theTH/nntongue weight to a smalljawOpen(below). (A2F'sMouthClosealso folds in jaw opening, deviating from Apple's, but that is moot here:arkitsealsPPwith lip-roll, notmouthClose.) - Reallusion CC3: shares CC4's Viseme Panel labels — use
cc4.
Optional shapes and fallbacks¶
A rig rarely has every shape a preset names. Pass available= (the shapes it
does have) and any missing target reroutes through the preset's fallback table
instead of dropping silently:
from openfacefx import retarget, PRESETS, PRESET_FALLBACKS
# an Audio2Face rig with no tongue shape:
shapes = {"jawOpen", "mouthFunnel", "mouthPucker", ...} # the rig's real ARKit shapes
track = retarget(track, PRESETS["arkit"], available=shapes,
fallbacks=PRESET_FALLBACKS["arkit"]) # tongueOut -> jawOpen
fallbacks is a {target: [(replacement, scale), ...]} table (an empty list
[] means drop this target). Substitutions chain — a replacement that is
itself missing is resolved again, weights multiplying — and cycles are broken,
not looped. With available=None (the default) nothing is filtered, so a plain
retarget(track, mapping) is unchanged. Shipped tables (PRESET_FALLBACKS):
| Preset | Fallback | Rationale |
|---|---|---|
arkit |
tongueOut → jawOpen × 0.2 |
Tongue-less rigs (e.g. Audio2Face) keep a hint of the TH/nn opening instead of losing it. Our heuristic, not an Apple convention. |
rhubarb |
G → A, H → C, X → A |
Rhubarb's own documented basic-set collapse for art with only the six basic shapes. Single source of truth: the cue exporters (--rhubarb-shapes) derive their collapse from this same table. |
The tables are data — extend PRESET_FALLBACKS or pass your own fallbacks=.