Character embodiment
Understand how a character's embodiment modules share one composition root, tick deterministically, and never write the same bone twice.
Every embodiment module on a Convai character — Gaze, Body Animation, Body Language, Conversation Flow, and Emotion — plugs into the same composition root for that character. This root owns the character's tick order and its only path to the Animator and the face. Understanding it explains why modules cooperate instead of fighting over the same bone or blendshape.
The composition root
EmbodimentContext (SDK/Runtime/Embodiment/EmbodimentContext.cs) is the composition root: one instance per character. Convai adds it automatically the first time any embodiment module resolves itself on that character — you never add it yourself, and it carries [AddComponentMenu("")] so it does not appear in the Add Component menu.
The context exposes the shared infrastructure every module reads:
CharacterRoot
Transform
The character's root transform.
EventHub
IEventHub
The event bus a module listens to for domain events.
Logger
ILogger
Structured diagnostics logging.
RigBinding
IStandardRigBinding
Semantic access to the character's rig — bones and blendshapes by role, not by name.
Character
ConvaiCharacter
The owning character, when one exists on the hierarchy.
It also raises three events a module can subscribe to: RigBindingChanged (the semantic rig was rebuilt or replaced), EmbodimentConfigurationChanged (a module's configuration changed at runtime, for example a preset swap), and DependenciesPopulated (runtime-only infrastructure such as EventHub became available).
Every module controller — ConvaiGazeController, ConvaiBodyAnimationController, ConvaiBodyLanguageController, ConvaiConversationFlowController, and ConvaiEmotionController — derives from ConvaiCharacterModule<TProfile> (SDK/Runtime/Embodiment/ConvaiCharacterModule.cs), which resolves this same shared context in OnEnable and registers the module as a profile receiver on it. A module that cannot resolve a context — because it is not on a Convai character — disables itself and logs why, rather than running with nothing to drive.
Auto-provisioned infrastructure
The context also lazily provisions four more infrastructure components the first time a module needs them: StandardRigBinding (works out which bones and blendshapes play which semantic role, for Humanoid, ARKit, CC3/CC4, and other common rigs), AnimatorConductor, EmbodimentTickScheduler, and FacialBlendshapeCompositorHost. All four are ordinary components you can select and inspect in the hierarchy — the SDK deliberately does not hide them, because a component you cannot see is a component you cannot debug. AnimatorConductor, EmbodimentTickScheduler, and FacialBlendshapeCompositorHost carry [AddComponentMenu("")]; treat all three as behavior Convai provides, never as components you add or an API you call directly.
Deterministic tick order
Every module with per-frame work implements IEmbodimentTickable and registers with the context through RegisterTickable/UnregisterTickable instead of relying on Unity's own Update/LateUpdate order. Registering in OnEnable and unregistering in OnDisable is the supported pattern; registering the same instance twice is a no-op.
The scheduler runs every tickable through three phases, always in this order:
Cognition
Conversation Flow, Gaze, and Emotion directors sample signals and update their readings.
Expression
Gaze, body, and facial actuators translate those readings into bone, blendshape, and Animator-parameter writes.
Finalize
The facial compositor and the Animator conductor finalize their writes after Cognition and Expression have both settled.
Within a phase, a tickable's TickOrder decides who runs first — lower values earlier — and ties break by registration order. This is why the tick order does not depend on hierarchy order or which module happened to enable first: reparenting a GameObject or adding a module in a different order cannot silently change which one writes to a shared bone first.
Single-writer access to the Animator and the face
Two modules writing to the same Animator parameter or the same blendshape in the same frame is the kind of bug that only shows up as a visible glitch, not a compile error. Convai avoids it by giving the Animator and the character's facial blendshapes exactly one writer each:
AnimatorConductoris the single authoritative writer of Animator parameters. A module submits a named parameter write through the conductor instead of callingAnimator.SetFloatdirectly; the conductor records which module registered each parameter and refuses a conflicting registration from another module.FacialBlendshapeCompositorHostis the single writer of facial blendshapes. It composites every module's contribution — Emotion, LipSync, and any custom layer — into one set of blendshape values, using the region rules in aConvaiFacialCompositionProfileto decide how much of the mouth, brow, eye, cheek, and jaw regions each contributor gets.
Both are auto-provisioned infrastructure, not components you configure directly. If you need to influence what they write, do it through the module that owns the behavior — Emotion for expression, LipSync for mouth shape — not by writing blendshapes on the same mesh yourself.
The five embodiment modules
Module
Component
Add Component menu path
ModuleIds constant
Gaze
ConvaiGazeController
Convai/Embodiment/Gaze
ModuleIds.Gaze (convai.gaze)
Body Animation
ConvaiBodyAnimationController
Convai/Embodiment/Body Animation
ModuleIds.BodyAnimation (convai.body-animation)
Body Language
ConvaiBodyLanguageController
Convai/Embodiment/Body Language
ModuleIds.BodyLanguage (convai.body-language)
Conversation Flow
ConvaiConversationFlowController
Convai/Embodiment/Conversation Flow
ModuleIds.ConversationFlow (convai.conversation-flow)
Emotion
ConvaiEmotionController
Convai/Embodiment/Emotion
ModuleIds.Emotion (convai.emotion)
Each ModuleIds (SDK/Domain/Embodiment/Modules/ModuleIds.cs) constant is the routing key a ConvaiEmbodimentPreset slot uses to hand that module its settings asset. All five modules are optional and add independently — a character can run only Gaze, only Emotion, or any combination, and each module degrades gracefully when a peer is absent.
Next steps
Dialogue stateAsset ownership and copy-on-writeLast updated
Was this helpful?