Facial Expression System

Create dynamic facial animations that react to penetration depth, scene context, and actor size using simple TOML configuration files.

Overview

The facial expression system lets you define presets — sets of rules that control an actor's facial morphs during a scene. Each preset targets a specific orifice (mouth, vagina, anus, or hands) and contains one or more effects that adjust facial morphs like mouth shape, eyebrow position, or custom MFEE expressions.

Presets can respond to penetration depth, so expressions intensify the deeper the penetration goes. They can also be filtered by scene context (like aggressive or loving scenes) and penis size/girth for more nuanced control.

Setup

Facial presets are defined in TOML config files. You can place them in the main accurate-penetration.toml config file, or in override config files inside the ppa-override-configs folder.

Troubleshooting

If facial expressions aren't applying, try installing MFG Fix NG. If they still don't work, another mod is likely overwriting the expressions.

Tip

The expression system can be toggled on/off with EnableExpressionSystem in the [General] section of your config.

Basic Structure

Each preset starts with a [[FacialPreset]] header, followed by its properties. Effects are defined as sub-tables using [[FacialPreset.Effects]]. You can define as many effects per preset as you want.

# A simple preset that targets the mouth
[[FacialPreset]]
Targets = ["Mouth"]
Priority = 10
Smoothing = 8.0
OverridePhonemes = true

# First effect: open the mouth using phoneme 1 (BigAah)
[[FacialPreset.Effects]]
Type = "Phoneme"
Index = 1
TargetValue = 0.8

# Second effect: add squinting using modifier 12
[[FacialPreset.Effects]]
Type = "Modifier"
Index = 12
TargetValue = 0.5

Multiple presets can target the same orifice. When that happens, the system picks the best match based on conditions and priority — more on that in How Presets Are Selected.

Preset Properties

These are the properties you set directly under a [[FacialPreset]] header.

PropertyTypeDescription
Targets string[] Required Which orifice(s) activate this preset. Values: "Mouth", "Vagina", "Anus", "Hands".
Priority integer When multiple presets match, the highest priority wins. Default: 0.
Smoothing float How quickly the face transitions to target values. Higher = faster/snappier, lower = more gradual. Default: 8.0.
Contexts string[] Only activate when the scene matches these context flags. See Scene Contexts. If omitted, the preset matches any scene.
OverridePhonemes bool Zero out all phoneme morphs not set by this preset. Prevents conflicts with other mods controlling mouth shapes. Default: false.
OverrideExpressions bool Zero out all expression morphs not set by this preset. Default: false.
OverrideModifiers bool Zero out all modifier morphs not set by this preset. Default: false.
PenisSizeMin float Only match when the penetrating penis size is at least this value. Default: 0 (no minimum).
PenisSizeMax float Only match when the penetrating penis size is at most this value. Default: 0 (no maximum).
PenisGirthMin float Only match when the penetrating penis girth is at least this value. Default: 0 (no minimum).
PenisGirthMax float Only match when the penetrating penis girth is at most this value. Default: 0 (no maximum).
About Override Flags

The override flags (OverridePhonemes, OverrideExpressions, OverrideModifiers) are useful for preventing conflicts. When enabled, any morph of that type that isn't being set by your preset will be forced to zero. This stops other mods or the game from fighting over the same facial morphs.

Effects

Each preset contains one or more effects defined as [[FacialPreset.Effects]]. An effect tells the system to adjust a specific facial morph. There are four types:

Phoneme — Mouth Shapes

Phonemes control lip sync and mouth shape morphs. Index range: 0–15. See the morph reference for what each index does.

[[FacialPreset.Effects]]
Type = "Phoneme"
Index = 1          # BigAah — wide open mouth
TargetValue = 0.8

Expression — Emotional Morphs

Expressions control broad emotional states like anger or pain. Index range: 0–16.

[[FacialPreset.Effects]]
Type = "Expression"
Index = 15         # Combat Shout
TargetValue = 0.6

Modifier — Auxiliary Morphs

Modifiers control things like brow movement, squinting, and blinking. Index range: 2–13 (indices 0 and 1 are reserved by the engine).

[[FacialPreset.Effects]]
Type = "Modifier"
Index = 12         # Squint Left
TargetValue = 0.5

MFEE — Custom Expressions (Mu Facial Expression Extended)

If you have the MFEE mod installed, you can use custom morph names instead of numeric indices. See the MFEE section for details.

[[FacialPreset.Effects]]
Type = "MFEE"
MorphCategory = "Misc"
MorphName = "Biting_lips"
TargetValue = 100
Value Limits

Standard Phoneme, Expression, and Modifier values use a 0.0–1.0 scale. Values above 1.0 will not apply correctly. MFEE effects use a 0–100 integer scale instead.

Effect Properties

These go under each [[FacialPreset.Effects]] entry.

PropertyTypeDescription
Type string Required "Phoneme", "Expression", "Modifier", or "MFEE".
Index integer Which morph to control. Required for Phoneme, Expression, Modifier types. Not used for MFEE.
MorphCategory string MFEE only. The category the morph belongs to (e.g. "Misc", "Expressions").
MorphName string MFEE only. The name of the custom morph (e.g. "Ahegao", "Biting_lips").
TargetValue float The value the morph should reach at maximum intensity. 0.0–1.0 for standard types, 0–100 for MFEE. Default: 1.0.
MinTargetValue float Used with depth scaling. The starting value at MinPenetrationDepth. Default: 0.0.
MinPenetrationDepth float Depth at which this effect starts applying (the morph is at MinTargetValue). Default: 0.0.
MaxPenetrationDepth float Depth at which this effect reaches full TargetValue. Must be greater than MinPenetrationDepth for scaling to work. Default: 0.0.

Depth Scaling

The most powerful feature of the system is depth-based scaling, which smoothly interpolates morph values based on how deep the penetration currently is. This makes expressions feel dynamic and reactive.

To use depth scaling, set both MinPenetrationDepth and MaxPenetrationDepth on an effect. The morph value will smoothly transition from MinTargetValue to TargetValue as depth increases through that range.

Depth: 0 ──── 2.0 ═══════════════════ 10.0 ────── ∞ Value: 0 ──── 0.2 ─── scales up ───── 1.0 ─────── 1.0 ↑ MinPenetrationDepth ↑ MaxPenetrationDepth (MinTargetValue) (TargetValue)
[[FacialPreset.Effects]]
Type = "Expression"
Index = 7
MinTargetValue = 0.2           # Value at minimum depth
TargetValue = 1.0              # Value at maximum depth
MinPenetrationDepth = 2.0     # Effect starts at depth 2
MaxPenetrationDepth = 10.0    # Effect reaches full power at depth 10

If you omit the depth properties (or set both to 0), the effect simply applies at the full TargetValue whenever the preset is active, regardless of depth.

Tip

You can combine multiple effects with different depth ranges in one preset. For example, start with a subtle expression at shallow depth, then layer on additional morphs at deeper penetration for a progressive reaction.

Scene Contexts

Use the Contexts property on a preset to restrict it to specific types of scenes. Context values are provided by the animation framework (like OStim or SexLab). If a preset has no Contexts, it matches any scene.

ContextDescription
VaginalScene involves vaginal penetration
AnalScene involves anal penetration
OralScene involves oral penetration
AggressiveScene is tagged as aggressive/rough
FemDomScene is tagged as female-dominant
LovingScene is tagged as loving/gentle
DirtyScene is tagged as dirty/kinky
BoobjobScene involves a boobjob
HandjobScene involves a handjob
FootjobScene involves a footjob
MasturbationScene involves masturbation

You can combine multiple contexts. The preset will only activate when all specified contexts are present in the scene:

[[FacialPreset]]
Targets = ["Vagina"]
Contexts = ["Aggressive"]   # Only during aggressive scenes
Priority = 5

Morph Reference

Phonemes (0–15)

0Aah
1BigAah
2BMP
3ChJSh
4DST
5Eee
6Eh
7FV
8I
9K
10N
11Oh
12OohQ
13R
14Th
15W

Expressions (0–16)

0–6Dialogue Emotions (Anger → Disgust)
7–13Mood Emotions (Anger → Disgust)
14Combat Anger
15Combat Shout
16Combat Shout 2

Modifiers (2–13)

2Brow Down Left
3Brow Down Right
4Brow In Left
5Brow In Right
6Brow Up Left
7Brow Up Right
8LookDown
9LookLeft
10LookRight
11LookUp
12Squint Left
13Squint Right

MFEE Integration

If you have Mu Facial Expression Extended installed, you can use custom morph names for much more expressive results. Instead of Index, you specify a MorphCategory and MorphName.

Different Scale

MFEE morphs use an integer 0–100 scale, unlike the 0.0–1.0 scale used by standard Phoneme/Expression/Modifier types. Make sure you adjust your values accordingly.

[[FacialPreset.Effects]]
Type = "MFEE"
MorphCategory = "Expressions"
MorphName = "Ahegao"
TargetValue = 100

[[FacialPreset.Effects]]
Type = "MFEE"
MorphCategory = "Misc"
MorphName = "Biting_lips"
MinTargetValue = 10
TargetValue = 100
MinPenetrationDepth = 0.0
MaxPenetrationDepth = 12.0

Available morph categories and names depend on the MFEE morphs installed for your character's head mesh. Common ones include "Ahegao", "Biting_lips", "Puzzled", "Stress", and "EXPR2_Mouth_Open_Wide".

How Presets Are Selected

When a scene is active, the system picks one winning preset per actor. Here's how it decides:

1 Target matching — the preset's Targets must include at least one orifice that is currently being penetrated.

2 Condition checks — if the preset specifies Contexts, PenisSizeMin/Max, or PenisGirthMin/Max, those conditions must all pass.

3 Priority — among all matching presets, the one with the highest Priority value wins.

Example

Say you have two vaginal presets: one for normal scenes at priority 2, and one for aggressive scenes at priority 5 with Contexts = ["Aggressive"]. During an aggressive scene, the priority 5 preset wins because it matches and has higher priority. During a normal scene, the aggressive preset doesn't match (context check fails), so the priority 2 preset wins instead.

Example: Basic Mouth Opening

This is the default configuration that ships with the mod. It uses a combination of phonemes to create a natural open-mouth look during oral scenes. Since SexLab already handles some mouth animation, this preset is optional.

Default Oral Preset

Targets the mouth with very high priority. Uses OverridePhonemes to zero out all phonemes not defined here, preventing conflicts. Combines multiple phoneme shapes (Aah, BigAah, Eee, Eh, FV, K) to create a blended open mouth look.

[[FacialPreset]]
Targets = ["Mouth"]
Priority = 99999
OverrideExpressions = false
OverrideModifiers = false
OverridePhonemes = true
Smoothing = 8.0

[[FacialPreset.Effects]]
Type = "Phoneme"
Index = 0              # Aah
TargetValue = 0.75

[[FacialPreset.Effects]]
Type = "Phoneme"
Index = 1              # BigAah
TargetValue = 0.75

[[FacialPreset.Effects]]
Type = "Phoneme"
Index = 5              # Eee
TargetValue = 1.0

[[FacialPreset.Effects]]
Type = "Phoneme"
Index = 6              # Eh
TargetValue = 1.0

[[FacialPreset.Effects]]
Type = "Phoneme"
Index = 7              # FV
TargetValue = 1.0

[[FacialPreset.Effects]]
Type = "Phoneme"
Index = 9              # K
TargetValue = 0.68

Example: Advanced Multi-Orifice Presets

These examples show more complex setups using depth scaling, MFEE morphs, context filtering, and size-based conditions.

Oral — Depth-Reactive with MFEE

Opens the mouth wider as depth increases, uses a Combat Shout expression that scales with depth, and adds an angry expression at deep penetration. All three override flags are on to prevent any other morphs from interfering.

[[FacialPreset]]
Targets = ["Mouth"]
Priority = 999229
OverrideExpressions = true
OverrideModifiers = true
OverridePhonemes = true
Smoothing = 5.0

# Combat Shout expression: faint at surface, full at depth 12
[[FacialPreset.Effects]]
Type = "Expression"
Index = 15
MinTargetValue = 0.3
TargetValue = 1.0
MinPenetrationDepth = 0.0
MaxPenetrationDepth = 12.0

# MFEE wide mouth: opens between depth 0–8
[[FacialPreset.Effects]]
Type = "MFEE"
MorphCategory = "Misc"
MorphName = "EXPR2_Mouth_Open_Wide"
MinTargetValue = 70.0
TargetValue = 90.0
MinPenetrationDepth = 0.0
MaxPenetrationDepth = 8.0

# Angry look kicks in only at deep penetration (8.5–10)
[[FacialPreset.Effects]]
Type = "MFEE"
MorphCategory = "Misc"
MorphName = "MoodAnger"
MinTargetValue = 0.0
TargetValue = 100.0
MinPenetrationDepth = 8.5
MaxPenetrationDepth = 10.0
Oral — Big Girth Variant

Same orifice as above, but only activates when girth is 1.4 or higher. Has a slightly higher priority (999239 vs 999229) so it wins over the normal oral preset when the size condition is met. Notice the mouth opens even wider (90–100 vs 70–90).

[[FacialPreset]]
Targets = ["Mouth"]
PenisGirthMin = 1.4
Priority = 999239
OverrideExpressions = true
OverrideModifiers = true
OverridePhonemes = true
Smoothing = 5.0

[[FacialPreset.Effects]]
Type = "MFEE"
MorphCategory = "Misc"
MorphName = "EXPR2_Mouth_Open_Wide"
MinTargetValue = 90.0
TargetValue = 100.0
MinPenetrationDepth = 0.0
MaxPenetrationDepth = 8.0

[[FacialPreset.Effects]]
Type = "MFEE"
MorphCategory = "Misc"
MorphName = "MoodAnger"
MinTargetValue = 0.0
TargetValue = 100.0
MinPenetrationDepth = 8.5
MaxPenetrationDepth = 10.0

[[FacialPreset.Effects]]
Type = "Expression"
Index = 16
MinTargetValue = 0.4
TargetValue = 1.0
MinPenetrationDepth = 0.0
MaxPenetrationDepth = 12.0
Anal — Expressions + Brow/Squint Modifiers

Combines two Expression effects with four Modifier effects. The expressions provide the base emotional look, while the modifiers add brow furrowing and squinting that kick in at deeper penetration (depth 4+).

[[FacialPreset]]
Targets = ["Anus"]
Priority = 22
OverrideExpressions = true
OverrideModifiers = true
OverridePhonemes = true

# Combat Shout scales from subtle to strong over depth 0–12
[[FacialPreset.Effects]]
Type = "Expression"
Index = 15
MinTargetValue = 0.1
TargetValue = 1.0
MinPenetrationDepth = 0.0
MaxPenetrationDepth = 12.0

# Secondary expression for extra intensity at deep penetration
[[FacialPreset.Effects]]
Type = "Expression"
Index = 16
MinTargetValue = 0.0
TargetValue = 0.4
MinPenetrationDepth = 0.0
MaxPenetrationDepth = 16.0

# Squint left/right — only at deeper penetration
[[FacialPreset.Effects]]
Type = "Modifier"
Index = 12            # Squint Left
TargetValue = 0.5
MinPenetrationDepth = 4.0
MaxPenetrationDepth = 12.0

[[FacialPreset.Effects]]
Type = "Modifier"
Index = 13            # Squint Right
TargetValue = 0.9
MinPenetrationDepth = 4.0
MaxPenetrationDepth = 6.0

# Brow down left/right — furrowed brows at depth
[[FacialPreset.Effects]]
Type = "Modifier"
Index = 2             # Brow Down Left
TargetValue = 0.5
MinPenetrationDepth = 4.0
MaxPenetrationDepth = 6.0

[[FacialPreset.Effects]]
Type = "Modifier"
Index = 3             # Brow Down Right
TargetValue = 0.9
MinPenetrationDepth = 4.0
MaxPenetrationDepth = 7.0
Vaginal — Normal vs Aggressive (Context Filtering)

Two presets for vaginal penetration. The first (priority 2) activates during any vaginal scene and plays a lip-biting expression. The second (priority 5) only activates during aggressive scenes and overrides the first with a stressed/puzzled look. This shows how Contexts and Priority work together.

# Vagina — default (any scene)
[[FacialPreset]]
Targets = ["Vagina"]
Priority = 2
OverrideExpressions = true
OverrideModifiers = true
OverridePhonemes = false

[[FacialPreset.Effects]]
Type = "MFEE"
MorphCategory = "Misc"
MorphName = "Biting_lips"
MinTargetValue = 1.0
TargetValue = 100.0
MinPenetrationDepth = 0.0
MaxPenetrationDepth = 12.0

# Vagina — aggressive scenes only (higher priority wins when context matches)
[[FacialPreset]]
Targets = ["Vagina"]
Priority = 5
Contexts = ["Aggressive"]
OverrideExpressions = true
OverrideModifiers = true
OverridePhonemes = false

[[FacialPreset.Effects]]
Type = "MFEE"
MorphCategory = "Misc"
MorphName = "Puzzled"
MinTargetValue = 10.0
TargetValue = 100.0
MinPenetrationDepth = 0.0
MaxPenetrationDepth = 10.0

[[FacialPreset.Effects]]
Type = "MFEE"
MorphCategory = "Misc"
MorphName = "Stress"
MinTargetValue = 0.0
TargetValue = 80.0
MinPenetrationDepth = 8.0
MaxPenetrationDepth = 10.0