4E Virtual Design · Driver Mapping · Full Reference · Recipes

Binding Guide

Everything you need to wire any DataBus channel to any property on any Unity object. Start with the concept, walk through every channel and target type with worked examples, then copy-paste recipes straight into your scene.

Companion to SetupGuide.html — read that first if you haven't set the system up yet.
?
What Is a Binding?
The 30-second explanation

A binding is a single rule that says: "read this number from the DataBus, transform it, and apply it to that property on that object."

One binding drives one property. If you want a light to react to your arm, a material colour to cycle with music, and a particle system to burst on beat — that's three bindings. They're cheap, and the editor UI makes them fast to create.

pose/joint/rightElbow/bend
DataBus channel
DriverBinding
remap + mode + smooth
Character mouth opens
SkinnedMeshRenderer blendshape

How to think about it

The 4E Data Flow System is a giant post office. Your body, the music, the keyboard — all write numbers into named mailboxes on the DataBus. A binding is a little delivery worker that stands by one mailbox, grabs whatever number is in it every frame, transforms it, and hands it to one Unity property.

The worker can do smart things too: hold the value, reshape it, only work on certain days, or fire a pulse when something important happens. That's what binding modes are for.

1
Anatomy of a Binding
The five parts every binding has

Every binding, regardless of what it drives, has the same five-stage pipeline. Most stages are optional — a minimal binding is just a source and a target.

1
Source
Which DataBus channel to read (e.g. pose/joint/rightElbow/bend). Plus an Active toggle to mute the binding without deleting it.
2
Value Mapping (Remap + Curve)
Convert the raw channel value into whatever range your target expects. Optional response curve shapes it non-linearly.
3
Behaviour (Mode)
What kind of signal is this — continuous, on/off switch, gated, latched hold, stepped sequence, or decaying pulse.
4
Smoothing
Optional low-pass filter (0–0.99) to kill jitter from the camera or tame noisy inputs.
5
Target
What to write the final value into — a transform, a blendshape, a material, a light, an audio source, or any public field via reflection.
ℹ️
The pipeline runs every frame inside DriverMappingRunner.LateUpdate(). Idle bindings are nearly free — no work happens until someone writes to the channel they're listening to.
2
Scene Bindings vs Asset Mappings
Two ways to store bindings — pick the right one

Scene Bindings — recommended for most cases

Bindings are stored directly on the DriverMappingRunner component in your scene, under the Scene Bindings list. You drag scene objects straight into each binding's target field.

  • Scene references persist when you save the scene — you won't have to rewire anything.
  • No extra assets to manage.
  • Easiest to debug — everything lives on one component.

Use when: you're building a single scene, or each scene needs its own unique mapping.

Asset Mappings — for reuse across scenes

Bindings are stored inside a DriverMapping ScriptableObject asset (create via Assets → Create → 4E → Driver Mapping). The asset holds the recipe — channels, ranges, modes, curves. Scene references are supplied separately through the Asset Targets list on the runner.

  • One asset, many scenes — share a mapping across an entire show.
  • Swap multiple assets at runtime to change the entire performance feel (see Runtime Swap).
  • Scene references do not persist inside the asset — they're re-wired at Start() from the runner's Asset Targets list.

Use when: multiple scenes should react the same way, or you want to A/B test two mapping presets on the same show.

💡
You can use both at once. The runner applies scene bindings first, then the asset mapping. Use scene bindings for the "always on" core rig, and swap asset mappings for per-song / per-scene variations.
The Binding Pipeline
Exactly what happens inside DriverBinding.Apply() each frame

This is the literal order of operations, taken from DriverBinding.cs:

// For each binding, every frame: float raw = DataBus.ReadFloat(channelKey); // 1. Remap: inputMin..inputMax → outputMin..outputMax float v = Remap(raw, inputMin, inputMax, outputMin, outputMax); if (invert) v = Invert(v); if (clamp) v = Clamp(v, outputMin, outputMax); // 2. Response curve (optional, applied in normalised space) if (useResponseCurve) v = responseCurve.Evaluate(normV); // 3. Binding mode logic (Direct / Switch / Gate / Latch / Sequence / Pulse) v = ApplyMode(v, raw); // 4. Smoothing (lerp toward new value) if (smoothing > 0.001) v = Lerp(v, lastV, smoothing); // 5. Write to target property (transform / blendshape / material / ...) ApplyToTarget(v);
3
Source Channels
All the numbers you can read from the DataBus

Source channels come from the DataBus — populated by MediaPipe (body + face + hands), audio analysis, time-based LFOs, and keyboard/mouse input. This is not every channel the system writes, but it's a complete cheat sheet of the ones you'll use in practice.

Body Pose (33 landmarks)

MediaPipe tracks 33 body points. The PoseChannelSource writes the raw landmark positions plus several derived channels — joint bend angles, limb raise angles, overall body centre, and movement velocity.

ChannelRangeWhat it measures
pose/joint/name/bend0–180°Angle at the joint. 180 = straight, 0 = fully folded.
pose/joint/name/bendNorm0–1Same angle, normalised. Ready to use without a remap.
pose/joint/name/raise0–180°Limb angle vs world axis. 0 = above head, 180 = at side.
pose/joint/name/raiseNorm0–1Same, normalised.
pose/body/velocity0–1How much the whole body is moving. 0 = still, 1 = fast.
pose/body/centre/x−1 to 1Lean left (−1) or right (+1).
pose/body/centre/y−1 to 1Crouch (−1) or stand tall (+1).
pose/body/centre/z−1 to 1Forward / back. Noisy — MediaPipe Z is a rough estimate.
pose/landmark/name/x|y|z−1 to 1Raw landmark position. Available for all 33 points.
pose/landmark/name/visibility0–1How confident MediaPipe is that it sees this landmark.

Joint names for bend / bendNorm

leftElbow, rightElbow, leftWrist, rightWrist, leftKnee, rightKnee, leftHip, rightHip, headTilt

Limb names for raise / raiseNorm

leftUpperArm, rightUpperArm, leftForeArm, rightForeArm, leftThigh, rightThigh, shoulderWidth, hipWidth

All 33 landmark names

nose, leftEyeInner, leftEye, leftEyeOuter, rightEyeInner, rightEye, rightEyeOuter, leftEar, rightEar, mouthLeft, mouthRight, leftShoulder, rightShoulder, leftElbow, rightElbow, leftWrist, rightWrist, leftPinky, rightPinky, leftIndex, rightIndex, leftThumb, rightThumb, leftHip, rightHip, leftKnee, rightKnee, leftAnkle, rightAnkle, leftHeel, rightHeel, leftFootIndex, rightFootIndex

Face (478 landmarks + 52 blendshapes)

MediaPipe's face mesh gives you 478 raw landmark points plus 52 ARKit-compatible blendshape values that capture expressions like smile, blink, brow raise, and jaw open. Blendshapes are the usual choice — landmarks are more useful for bespoke measurements.

ChannelRangeWhat it measures
face/blendshape/jawOpen0–1How open the mouth is.
face/blendshape/mouthSmileLeft0–1Left-side smile intensity.
face/blendshape/eyeBlinkLeft0–1Left eye closed amount.
face/blendshape/browInnerUp0–1Eyebrows raised.
face/blendshape/cheekPuff0–1Cheeks puffed out.
face/landmark/i/x|y|z0–1Raw face mesh point. 478 points available (i = 0–477).

All 52 blendshapes

browDownLeft, browDownRight, browInnerUp, browOuterUpLeft, browOuterUpRight, cheekPuff, cheekSquintLeft, cheekSquintRight, eyeBlinkLeft, eyeBlinkRight, eyeLookDownLeft, eyeLookDownRight, eyeLookInLeft, eyeLookInRight, eyeLookOutLeft, eyeLookOutRight, eyeLookUpLeft, eyeLookUpRight, eyeSquintLeft, eyeSquintRight, eyeWideLeft, eyeWideRight, jawForward, jawLeft, jawOpen, jawRight, mouthClose, mouthDimpleLeft, mouthDimpleRight, mouthFrownLeft, mouthFrownRight, mouthFunnel, mouthLeft, mouthLowerDownLeft, mouthLowerDownRight, mouthPressLeft, mouthPressRight, mouthPucker, mouthRight, mouthRollLower, mouthRollUpper, mouthShrugLower, mouthShrugUpper, mouthSmileLeft, mouthSmileRight, mouthStretchLeft, mouthStretchRight, mouthUpperUpLeft, mouthUpperUpRight, noseSneerLeft, noseSneerRight, _neutral

Hands (21 landmarks per hand)

Each hand gets 21 tracked points: wrist, four joints per finger (MCP, PIP, DIP, tip). Hand labels are already swapped to user perspective — hand/right is your right hand, not the camera's.

ChannelRangeWhat it measures
hand/left/detected0 or 1Whether the left hand is currently visible.
hand/right/detected0 or 1Whether the right hand is currently visible.
hand/left/i/x|y|z−1 to 1Left hand landmark position. i = 0 to 20.
hand/right/i/x|y|z−1 to 1Right hand landmark position.

Useful landmark indices

0 wrist · 4 thumb tip · 8 index tip · 12 middle tip · 16 ring tip · 20 pinky tip. Indices 1–3, 5–7, 9–11, 13–15, 17–19 are the intermediate finger joints.

Audio

Requires an AudioAnalysisSource component in the scene with an AudioSource playing music. FFT analysis splits the spectrum into low / mid / high bands and runs a beat detector.

ChannelRangeWhat it measures
audio/amplitude0–1Overall volume level.
audio/bass0–1Low-frequency energy (kick drums, bass guitar).
audio/mid0–1Mid frequencies (vocals, guitars, snare body).
audio/high0–1High frequencies (hi-hats, cymbals, sibilance).
audio/beat0 or 11 on the frame a beat is detected, otherwise 0.

Time (LFOs and clocks)

The TimeSource writes automatic oscillators and a BPM clock — useful when you want something to pulse or cycle without any performer input. Three pre-configured LFOs ship with the system.

ChannelRangeWhat it measures
time/lfo/slow0–1Sine wave, ~4-second cycle (0.25 Hz). Good for ambient drift.
time/lfo/medium0–1Sine wave, ~1-second cycle (1 Hz). Natural breathing feel.
time/lfo/fast0–1Sine wave, ~0.25-second cycle (4 Hz). Rapid pulsing.
time/clock/pulse0 or 1Fires once per beat at the configured BPM (default 120).

Keyboard & Mouse

The InputSource writes keyboard and mouse state to the DataBus using Unity's new Input System. Great for dress-rehearsing effects before you've got MediaPipe running.

ChannelRangeWhat it measures
input/key/name0 or 1Key is currently held down. E.g. input/key/space, input/key/digit1.
input/key/name/down0 or 11 on the single frame the key was pressed. Useful for one-shot triggers.
input/mouse/x0–1Mouse horizontal position, normalised to screen width.
input/mouse/y0–1Mouse vertical position, normalised to screen height.
💡
How to discover channels in your actual scene: press F12 in Play mode to open the DataFlowMonitor, which lists every live channel with its current value. Press F11 to open the ChannelSliderPanel, which lets you override any channel with a slider so you can test bindings without a camera.
4
Value Remap
Convert the channel's native range into whatever your target needs

A channel usually speaks in its own units — degrees for a joint bend, 0–1 for a blendshape, −1 to 1 for a landmark position. Your target usually wants something else. Remap is the built-in converter.

FieldTypeWhat it does
useRemapboolMaster toggle. When off, the raw channel value passes through unchanged. Default: true.
inputMinfloatThe raw value that maps to outputMin.
inputMaxfloatThe raw value that maps to outputMax.
outputMinfloatThe value written when input ≤ inputMin.
outputMaxfloatThe value written when input ≥ inputMax.
clampboolIf true, output is restricted to [outputMin, outputMax]. Leave on for most cases. Default: true.
invertboolFlip the mapping: high input → low output. Useful for "hand down = loud" instead of "hand up = loud".
Example: Your elbow bend channel produces values from about 60° (arm folded tight) to 180° (arm straight). You want 0–1 output for a blendshape weight.
Set inputMin = 60, inputMax = 180, outputMin = 0, outputMax = 1. When the elbow is at 120°, the output is 0.5.
💡
Finding the right input range: press F12 in Play mode, watch the channel while you do your full range of motion, and note the actual minimum and maximum values. Use those as inputMin and inputMax. This ensures the full range of your movement maps to the full range of the output.
ℹ️
The editor's channel picker auto-fills sensible defaults. Pick a joint bend and you get 0–180 → 0–1; pick a face blendshape and you get 0–1 → 0–100. You usually only need to tune, not build from scratch.
Smart range defaults: Input/output min/max values now auto-populate based on the channel type and target property when you select them. Choosing a pose joint channel pre-fills the input range to degrees; choosing a BlendShape target pre-fills the output range to 0–100; choosing a LightIntensity target pre-fills 0–8; and so on. You can always override after the defaults are set.
5
Response Curve
Non-linear shaping, optional

A response curve is a Unity AnimationCurve that reshapes the remapped value non-linearly. It operates in normalised 0–1 space between outputMin and outputMax, so swapping your output range doesn't invalidate the curve. X axis = input, Y axis = output.

FieldTypeWhat it does
useResponseCurveboolMaster toggle. Default: false.
responseCurveAnimationCurveStandard Unity curve editor, 0 to 1 on both axes. Default: linear ramp.

Common curve shapes

ShapeEffectUse case
LinearEven response across full rangeDefault — most cases
Ease-in (x²)Subtle at low values, dramatic at highVolume, particle counts, intensity
Ease-out (√x)Punchy at low values, gentle at highPosition, scale, blendshapes where small motion should read strong
S-curveGentle at the edges, sharp in the middleCrossfades, audio transitions, natural animation
Step (flat then jump)Dead zone, then sudden responseThreshold effects without using Switch mode
6
Binding Modes
Six ways to interpret the signal

The default mode is Direct — the remapped value just passes straight through to the target. The other five modes add logic: on/off switches, two-channel gates, rising-edge latches, stepped sequences, and decaying pulses. Pick the one that matches the feel you want, not just the math.

Direct — default, passthrough

The remapped value passes through unchanged. Source → Remap → Target.

Settings: none

When to use: most of the time. Any continuous control where the output should smoothly follow the input — volume, position, colour hue, scale, blendshape weights.

Switch — binary on/off

Above the threshold → outputMax. Below → outputMin. No in-between.

Settings: modeThreshold

Example 1: Source pose/joint/rightUpperArm/raiseNorm, threshold 0.5 → Target LightIntensity, out 0→5. Arm above shoulder = light ON at 5. Below = light OFF.
Example 2: Source face/blendshape/mouthSmileLeft, threshold 0.4 → Target AnimatorBool "IsHappy". Smile past a certain point and the animator transitions to the happy state.
Example 3: Source pose/body/velocity, threshold 0.3 → Target ParticleEmission, out 0→200. Stand still = no particles. Start dancing = full emission rate. No gradient — it's either on or off.

Gate — two-channel AND

Passes the signal only when a separate gate channel is above 0.5. When the gate is closed, output snaps to outputMin.

Settings: gateChannel (a DataBus channel key)

Example 1: Source pose/joint/rightElbow/bend, gate hand/left/detected. The elbow bend only drives the target when your left hand is visible. Hide your left hand to disengage the effect without losing the right-arm position.
Example 2: Source pose/body/velocity, gate input/key/space. Hold spacebar to enable the motion-driven effect. Release to freeze the target. Great for "arming" an effect from the control desk.
Example 3: Source audio/bass, gate audio/beat. The bass-reactive effect only fires exactly on the beat frame — between beats, nothing happens. Creates a very rhythmic, gated feel.

Latch — capture and hold

On rising edge above the threshold, capture the current value and hold it forever — until a separate reset channel fires.

Settings: modeThreshold, resetChannel

Example 1: Source hand/left/0/y → Target MaterialColor. Threshold 0.3, reset input/key/space. Move your hand to pick a colour; as soon as you lift it past the threshold the colour locks in. Press Space to unlock and pick a new one.
Example 2: Source pose/joint/rightElbow/bendNorm → Target Transform LocalPositionY. Threshold 0.5, reset hand/right/detected. Bend your arm to set an object's height; it stays there until you show your right hand to the camera.
Example 3: Source audio/bass → Target LightIntensity. Threshold 0.7, reset time/clock/pulse. The first loud bass hit freezes the light at peak brightness; the next BPM pulse releases it. Produces a strobe-hold effect tied to the song's tempo.

Sequence — step through a list

Each time the signal crosses the threshold from below, step to the next value in sequenceValues[]. Wraps back to the start after the last value.

Settings: modeThreshold, sequenceValues (float array)

Example 1: Values [0, 0.33, 0.66, 1.0] → Target AudioCrossfade. Each arm raise switches to the next of four audio tracks, cycling through them.
Example 2: Values [0, 90, 180, 270] → Target Transform LocalRotationY. Each gesture rotates the object 90° to the next position. Four gestures = full turn.
Example 3: Values [1, 0] → Target LightIntensity. Each trigger alternates between 1 and 0 — i.e. the binding is a clap-activated toggle.

Pulse — hit and fade

On rising edge above the threshold, snap to outputMax. Then decay back to outputMin over pulseDecay seconds.

Settings: modeThreshold, pulseDecay (seconds)

Example 1: Source audio/beat, decay 0.15s → Target LightIntensity, out 0→10. Light flashes on every beat and fades quickly. Classic beat-reactive stage lighting.
Example 2: Source pose/joint/rightElbow/bendNorm, threshold 0.7, decay 0.5s → Target ParticleEmission, max 500. A quick arm bend triggers a half-second particle burst that fades away.
Example 3: Source input/key/space/down, decay 1.0s → Target Transform UniformScale, out 1→3. Press Space and the object pulses to 3× size, then shrinks back to normal over one second.

Mode-specific fields reference

These fields only matter when the corresponding mode is selected — they're hidden in the inspector otherwise.

FieldTypeUsed by
modeThresholdfloatSwitch, Latch, Sequence, Pulse. The rising-edge trigger level, in remapped space. Default: 0.5.
gateChannelstringGate. The DataBus channel that must be > 0.5 for the signal to pass.
resetChannelstringLatch. The DataBus channel that clears the latched value when it rises above 0.5.
sequenceValuesfloat[]Sequence. The list of values to step through. Default: [0, 0.5, 1].
pulseDecayfloatPulse. Seconds to decay from outputMax back to outputMin. Default: 0.3.
Rising-edge gotcha: Switch, Latch, Sequence and Pulse all detect the moment the signal crosses the threshold from below. If your channel already starts above the threshold when the scene loads, the first trigger won't fire until the channel dips and rises again. This is usually fine, but it can confuse you during testing — if nothing's firing, momentarily drop the channel (press F11 and drag the slider down) before raising it.
Gate / Reset use raw values: the gate channel and the Latch reset channel use a plain > 0.5 test on whatever's in the DataBus — they ignore your binding's remap. If the channel you're using for a gate doesn't cleanly cross 0.5, pre-process it through a ThresholdProcessor first so you get a clean 0/1 signal.
7
Smoothing
Tame jitter

The final stage before the target. A lerp-based low-pass filter: output = lerp(newValue, lastValue, smoothing). A value of 0 is instant; values approaching 0.99 feel molasses-slow.

FieldTypeWhat it does
smoothingfloat (0–0.99)Smoothing factor. Default: 0.

Smoothing level quick reference

ValueFeelBest for
0Instant — no smoothing at allTriggers, switches, beat flashes, clap detection
0.1–0.3Light smoothingMost body tracking — removes MediaPipe jitter without killing response
0.4–0.6Medium smoothingSlow, flowing effects — colour sweeps, material blends
0.7–0.9Heavy smoothingAmbient, gradual changes — lighting washes, slow drift
0.99Very slow — barely movesUltra-slow ambient drift, almost frozen
💡
Smoothing costs very little CPU but costs a lot in feel. The more you smooth, the less responsive the effect, and audiences read lag as "broken". Find the lowest smoothing that kills the jitter — don't default to 0.8 because "more is better". If 0.6 isn't cutting it, chain a dedicated SmoothProcessor upstream and leave the binding smoothing near 0.
8
Target Types
All 28 things a binding can drive — full reference with examples (including 2 audio targets for dB Reaper and dB Unity)

The targetType field selects which inspector fields are shown and which Unity API gets written each frame. One target per binding — if you need to drive a light's intensity and its colour, make two bindings pointing at the same Light.

Transform

Move, rotate, or scale a scene object on one axis.

What to drag: any scene object — the binding uses its Transform component.

FieldTypeNotes
targetTransformTransformThe object to move, rotate, or scale.
transformPropertyenumLocalPositionX/Y/Z (metres) · LocalRotationX/Y/Z (degrees) · LocalScaleX/Y/Z · UniformScale.
Example: Source pose/joint/rightElbow/bend → Remap In 60–180, Out 0–360 → Target Cube → LocalRotationY.
Result: bending your arm rotates the cube a full turn.

BlendShape

Deform a 3D mesh using morph targets (facial expressions, body morphs). Drives SkinnedMeshRenderer.SetBlendShapeWeight.

What to drag: a SkinnedMeshRenderer — the inspector dropdown shows all available blendshapes by name.

Output range: 0–100 (Unity blendshapes use 0–100, not 0–1).

FieldTypeNotes
targetMeshSkinnedMeshRendererThe mesh that holds the blendshapes.
blendShapeIndexintIndex in the mesh's blendshape list. Use the inspector dropdown.
blendShapeNamestringOptional: set blendShapeIndex = -1 and this name is resolved to an index on first Apply. Handy if your mesh reorders blendshapes.
Example: Source face/blendshape/jawOpen → Remap In 0–1, Out 0–100 → Target Character mesh → "MouthOpen" shape.
Result: opening your mouth opens the character's mouth 1:1.

AnimatorFloat

Sets a float parameter on an Animator Controller continuously every frame. Use it for blend trees, speed multipliers, or any parameter that should track a smooth value.

What to drag: an Animator component.

Output range: 0–1 typically, but can be any float range your blend tree expects.

FieldTypeNotes
targetAnimatorAnimatorThe Animator component on the character/object.
animatorParamstringParameter name — must match the Animator Controller exactly.
Example: Source pose/body/velocity → Remap In 0–1, Out 0–1 → Target Character Animator → Float "Speed".
Result: the blend tree smoothly transitions from idle to walk to run based on how fast you're moving.

AnimatorBool

Sets a bool parameter on an Animator Controller. The parameter becomes true when the value is at or above boolThreshold, and false when below.

What to drag: an Animator component.

FieldTypeNotes
targetAnimatorAnimatorThe Animator component.
animatorParamstringParameter name — must match the Animator Controller exactly.
boolThresholdfloatValue at or above this = true. Default: 0.5.
Example: Source face/blendshape/mouthSmileLeft, threshold 0.4 → Target Character Animator → Bool "IsHappy".
Result: smile past a certain point and the animator transitions to the happy state.

AnimatorTrigger

Fires a trigger parameter once when the value crosses above boolThreshold (rising edge). The value must dip back below the threshold before the trigger can fire again — no repeated firing while held.

What to drag: an Animator component.

FieldTypeNotes
targetAnimatorAnimatorThe Animator component.
animatorParamstringParameter name — must match the Animator Controller exactly.
boolThresholdfloatRising-edge threshold. Default: 0.5.
Example: Source pose/joint/rightUpperArm/raiseNorm, threshold 0.3 → Target Character Animator → Trigger "Wave".
Result: raise your right arm and the character plays a wave animation once. Lower it and raise again for another wave.

MaterialFloat

Drive any numeric shader property — metallic, smoothness, emission strength, dissolve amount, custom properties on your own shaders. Writes to an instance of the material, not the shared asset.

What to drag: a Renderer (MeshRenderer / SkinnedMeshRenderer).

FieldTypeNotes
targetRendererRendererThe Renderer whose material instance will be written.
materialPropertystringShader property name, e.g. _Metallic, _EmissionStrength. Default: _Value.
materialIndexintWhich sub-material on the Renderer to target. Default: 0.

Common URP Lit properties: _Metallic (0–1), _Smoothness (0–1), _BumpScale (0–2).

Example: Source audio/bass → Remap In 0–1, Out 0–1 → Target Floor renderer → _Metallic.
Result: the floor becomes shiny on bass hits.

MaterialColor

Sweep through rainbow colours. The channel value 0–1 is treated as hue and converted to RGB via HSV. 0 = red, 0.33 = green, 0.66 = blue, 1 = red again.

What to drag: a Renderer. The binding writes to the named colour property on its material instance.

FieldTypeNotes
targetRendererRendererSame as MaterialFloat.
materialPropertystringColour property name. Falls back to _BaseColor then _Color if the named property doesn't exist.
materialIndexintSub-material index.
matColorSaturationfloatHSV saturation. 1 = vivid, 0 = greyscale. Default: 1.
matColorBrightnessfloatHSV value/brightness. 1 = full, 0 = black. Default: 1.
Example: Source time/lfo/slow → Remap In 0–1, Out 0–1 → Target Sphere renderer → _BaseColor.
Result: the sphere slowly cycles through the full rainbow on its own, no performer needed.
URP uses _BaseColor, not _Color. The built-in render pipeline uses _Color. For emission, URP Lit uses _EmissionColor and the material's Emission keyword must be enabled in the material inspector — otherwise you'll write a value that never appears.

LightIntensity

Set Light.intensity.

What to drag: a Light component (Point / Spot / Directional / Area).

Typical output range: 0–8. 0 = off, 1 = standard indoor light, 8 = very bright.

FieldTypeNotes
targetLightLightThe Light component.
Example: Source pose/body/velocity → Remap In 0–1, Out 0.2–8 → Target Point Light → LightIntensity.
Result: the light brightens when you move and dims when you're still.

LightRange

Set Light.range — how far a Point or Spot light reaches before falling off.

Typical output range: 1–50. Has no effect on Directional lights.

Example: Source audio/bass → Remap In 0–1, Out 3–20 → Target Point Light → LightRange.
Result: the illuminated area expands and contracts with the bass.

LightColor

Set Light.color. Like MaterialColor, treats the value as hue and converts to RGB via HSV.

FieldTypeNotes
targetLightLightThe Light component.
lightColorSaturationfloatHSV saturation. Default: 1.
lightColorBrightnessfloatHSV value/brightness. Default: 1.
Example: Source pose/landmark/rightWrist/y → Remap In −0.8–0.8, Out 0–1 → Target Spot Light → LightColor.
Result: raising your right hand cycles the spotlight through the colour wheel.

ParticleEmission

Set emission.rateOverTime. The binding multiplies your output by maxEmissionRate so you can keep the output in a familiar 0–1 range.

FieldTypeNotes
targetParticlesParticleSystemThe particle system to drive.
maxEmissionRatefloatUpper bound in particles/second when output = 1. Default: 100.
Example: Source audio/amplitude → Remap In 0–1, Out 0–1 → Max rate 200 → Target Particle System.
Result: the system emits more particles when the music is louder. Max 200/s at peak volume.

AudioVolume

Set AudioSource.volume. The binding auto-plays the AudioSource on first apply if it has a clip assigned but isn't already playing.

What to drag: an AudioSource with a clip assigned. Enable Loop for continuous effects.

Output range: 0–1 (0 = silent, 1 = full).

Example: Source hand/left/0/y → Remap In −0.5–0.5, Out 0–1 → Target AudioSource → AudioVolume.
Result: raise your left hand to increase the volume. Auto-plays the clip the first time you run it.

AudioPitch

Set AudioSource.pitch. 0.5 = one octave down, 1 = normal, 2 = one octave up. Internally clamped to [−3, 3].

Example: Source pose/body/velocity → Remap In 0–1, Out 0.8–1.5 → Target AudioSource → AudioPitch.
Result: the music speeds up as you move faster, slows down when you're still.

AudioCrossfade

Blend between two audio sources. v = 0 is full Source 1, v = 1 is full Source 2. Auto-plays both if they have clips.

What to drag: two AudioSources, each with a different clip. Enable Loop on both.

FieldTypeNotes
targetAudioAudioSourceSource 1 — fades in as output approaches 0.
targetAudio2AudioSourceSource 2 — fades in as output approaches 1.
Example: Source hand/left/0/y → Remap In −0.5–0.5, Out 0–1, Smoothing 0.3 → Target Track A + Track B.
Result: hand up = Track A, hand down = Track B, smooth crossfade in between.

AudioPlayStop

Play the audio when the value rises above audioPlayThreshold. Stop when it falls back below.

FieldTypeNotes
targetAudioAudioSourceThe source to start/stop.
audioPlayThresholdfloatRising-edge threshold for Play. Default: 0.5.
Example: Source pose/joint/rightUpperArm/raiseNorm, mode Switch, threshold 0.5 → Target AudioSource → AudioPlayStop.
Result: raise your arm past your shoulder and the music starts. Lower it and the music stops.

AudioSpatialX

Moves the AudioSource's local position on the X axis (left/right). Creates spatial audio effects — the sound seems to move horizontally in 3D space.

What to drag: an AudioSource component. Must have Spatial Blend set to 1 (fully 3D).

Output range: -10 to 10 (metres from the source's origin).

Example: Source hand/right/0/x → Remap In −1–1, Out −5–5 → Target AudioSource → AudioSpatialX.
Result: your right hand position controls where the sound appears to come from left/right.

AudioSpatialY

Moves the AudioSource's local position on the Y axis (up/down). Creates vertical spatial audio movement.

What to drag: an AudioSource component. Must have Spatial Blend set to 1 (fully 3D).

Output range: -10 to 10 (metres from the source's origin).

Example: Source hand/left/0/y → Remap In −0.5–0.5, Out −3–3 → Target AudioSource → AudioSpatialY.
Result: raising your left hand moves the sound source upward in the 3D mix.

AudioSpatialZ

Moves the AudioSource's local position on the Z axis (forward/back). Creates depth-based spatial audio movement.

What to drag: an AudioSource component. Must have Spatial Blend set to 1 (fully 3D).

Output range: -10 to 10 (metres from the source's origin).

Example: Source pose/body/centre/z → Remap In −1–1, Out −5–5 → Target AudioSource → AudioSpatialZ.
Result: stepping forward/backward in the space moves the sound source closer or further away.

AudioReverbZone

Set the decay time of an AudioReverbZone component found on the same GameObject as the AudioSource.

Output range: clamped to [0.1, 20] seconds.

Example: Source pose/body/centre/z → Remap In −1–1, Out 0.5–10 → Target AudioSource → AudioReverbZone.
Result: stepping back in the space adds reverb, stepping forward makes it drier.

Reflection — the escape hatch

Drives ANY public float, bool, or int field on ANY component — the most flexible target type. Specify the component type name and the field or property name, and the binding writes to it via C# reflection. If there's no dedicated target for what you want, this is the fallback.

What to drag: a GameObject that has the component you want to drive.

Supported member types: float, bool, int, double. Anything else (Vector3, string, Color) silently fails — check the Console for a warning.

FieldTypeNotes
reflectionTargetGameObjectThe GameObject hosting the component.
reflectionComponentTypestringClass name, e.g. Light, FogController, PostProcessVolume. Case-insensitive Contains-match on short or full type name.
reflectionMemberNamestringExact public field or property name. Private/serialised-private fields are not supported.
Example: Source audio/bass → Remap In 0–1, Out 0.1–2.5 → Target GameObject with MyVFXController script → field glowAmount.
Result: your custom VFX controller's glow reacts to the bass frequencies.
💡
If you find yourself using Reflection for the same custom script over and over, consider adding a dedicated target type to DriverBinding.cs and its switch statement. Reflection is slower than a direct write (still cheap, just not free) and has no inspector help.
9
UI Element Targets
Drive UGUI widgets directly — gauges, bars, sliders, buttons, text — for gamified HUDs and dashboards

These six target types let any DataBus channel drive a Unity UI element in real time. They are the fastest way to build a HUD, dashboard, or interactive overlay that reacts to body movement, audio, or any other source. All six work with standard UnityEngine.UI components — no extra packages required. For TextMeshPro, use Reflection on the text property until dedicated TMP targets are added.

UGUISliderValue

Drive a UGUI Slider.value. The Slider's own Min/Max range defines the visible bounds, so your outputMin / outputMax should match.

What to drag: a UGUI Slider component.

FieldTypeNotes
targetSliderSliderThe Slider whose value will be written each frame.
Example: Source pose/joint/rightElbow/bendNorm → Remap In 0–1, Out 0–1 → Target Slider (Min 0, Max 1).
Result: the slider's handle tracks your elbow bend in real time. Use as a live "input meter" or as a numeric input that you control with movement.

UGUIToggleIsOn

Set a Toggle.isOn based on whether the value crosses a threshold. Toggle is true when the value is at or above boolThreshold, false otherwise.

What to drag: a UGUI Toggle component.

FieldTypeNotes
targetToggleToggleThe Toggle to drive.
boolThresholdfloatThe value at or above which the toggle becomes ON. Default: 0.5.
Example: Source face/blendshape/mouthSmileLeft, threshold 0.4 → Target Toggle "HappyMode".
Result: smile to enable a setting in your HUD; relax to disable it.

UGUIButtonTrigger

Fire a Button.onClick event once on the rising edge above boolThreshold. Same rising-edge logic as AnimatorTrigger — the value must dip back below before the next click can fire.

What to drag: a UGUI Button component. Wire its onClick event in the inspector to whatever you want to trigger (load scene, play sound, advance dialogue, etc).

FieldTypeNotes
targetButtonButtonThe Button whose onClick fires.
boolThresholdfloatRising-edge trigger level. Default: 0.5.
Example: Source pose/joint/rightUpperArm/raiseNorm, threshold 0.7 → Target Button "Confirm".
Result: raise your right arm above shoulder height to "click" the Confirm button. Lower it to re-arm.
Pair this target with Pulse binding mode for cleaner gesture-to-click behaviour. Pulse-mode signal is naturally a sharp rising edge, which makes it impossible to accidentally retrigger.

UGUIImageFillAmount

Drive an Image.fillAmount (clamped 0–1). The most flexible UI target — works for horizontal bars, vertical bars, and radial gauges depending on the Image's Fill Method setting.

What to drag: a UGUI Image with Image Type = Filled in its inspector. Then choose the Fill Method (Horizontal / Vertical / Radial 90 / Radial 180 / Radial 360) to get the visual style you want.

FieldTypeNotes
targetImageImageThe Image whose fill amount will be written. Must have Image Type = Filled.
Example: Source pose/body/velocity → Remap In 0–1, Out 0–1 → Target Image (Filled, Horizontal).
Result: a horizontal stamina/energy bar that fills as you move. Combine with a background Image for the empty-bar look.
💡
Radial gauges are exactly the same target — just set the Image's Fill Method to Radial 360. The needle / arc rotates as the value changes. Centre a UGUITextValue on top of it for a complete dial widget.

UGUIImageColor

Set Image.color. Treats the value as hue and converts to RGB via HSV — same model as MaterialColor and LightColor. Saturation and brightness stay fixed.

FieldTypeNotes
targetImageImageThe Image whose color will be written.
imageColorSaturationfloatHSV saturation. Default: 1.
imageColorBrightnessfloatHSV value/brightness. Default: 1.
Example: Source hand/right/detected → Remap In 0–1, Out 0–0.33 → Target Image (status indicator pill).
Result: the indicator turns red (hue 0) when the hand is hidden and green (hue 0.33) when it's visible. Quick "is the system seeing me?" feedback at a glance.

UGUITextValue

Write the value into a UGUI Text using a C# format string. Great for live counters, percentage readouts, score displays, and "current value" labels next to gauges.

What to drag: a UGUI Text component.

FieldTypeNotes
targetTextTextThe Text to write into.
textFormatstringC# format string. The channel value is the only argument ({0}). Default: "{0:F2}".

Common format strings

FormatOutputUse case
{0:F0}5300Whole numbers — score, count, energy
{0:F2}0.75Two decimal places — debug values, raw channels
{0:P0}75%Percentage — gauge labels, progress
{0:N0}1,117Thousand separators — large counters
Score: {0:F0}Score: 5300Labelled value
{0:F1} m/s1.2 m/sValue with units
Example: Source pose/body/velocity → Remap In 0–1, Out 0–9999 → Target Text → Format "{0:F0}".
Result: a big number that counts up as you move faster. Pair with a gauge for a HUD-style readout.
Bad format strings (e.g. typo, missing brace) won't crash — they fall back to v.ToString("F2") silently. Test your format strings in Play mode and watch the Text update live.
🎛
Audio Targets (dB Reaper + dB Unity)
Two target types for driving audio effects from body movement — one via Reaper/PaulXStretch (OSC), one via Unity's built-in audio filters (zero latency)

PaulXTrackProperty — Tab 5 "dB Reaper"

Drives a named property on a PaulXTrack component, which routes the value through OSC to PaulXStretch running inside Reaper. Set your output range to the property's display range — the binding produces the display value, and PaulXTrack handles the JUCE-skew normalised-to-OSC conversion internally.

FieldTypeWhat it does
targetPaulXTrackPaulXTrackDrag a scene GameObject that has a PaulXTrack component (an AudioSlot).
paulXPropertyPaulXPropertyWhich PaulXStretch parameter to control: StretchAmount, PitchShift, FreqShift, FilterLow, FilterHigh, Spread, Compress, MainVolume, Freeze.
PropertyOutput rangeWhat it does
StretchAmount0.1 – 1024Time stretch factor. 1.0 = real-time. 10 = 10× slower. 1024 = drone. Log-skewed internally.
PitchShift-24 – +24Semitones. ±12 = one octave.
FreqShift-1000 – +1000Constant frequency offset in Hz.
FilterLow / FilterHigh20 – 20000Low/high cutoff in Hz.
Spread0 – 1Frequency spread (smearing).
Compress0 – 1Dynamic range compression.
MainVolume-24 – +12Output gain in dB.
Freeze0 or 1Hold current spectral frame. Treated as bool (≥ 0.5 = frozen).
ℹ️
Requires Reaper running in the background with the EKO_Audio.RPP project loaded and OSC control surface active on port 8000. PaulXTrack sends OSC via PaulXDispatcher. If Reaper isn't running, the binding still evaluates but the audio doesn't change (graceful degradation).

UnityAudioFXProperty — Tab 6 "dB Unity"

Drives a Unity-native audio filter property directly on a UnityAudioFX component. Zero latency — writes directly to Unity's audio components on the audio thread. No Reaper, no OSC, no network. Set your output range to the property's native range.

FieldTypeWhat it does
targetUnityAudioFXUnityAudioFXDrag a scene GameObject that has an AudioSource + audio filter components + UnityAudioFX.
unityAudioFXPropertyUnityAudioFXPropWhich filter property to control (see table below).
PropertyFilterRangeWhat it does
VolumeAudioSource0 – 1Master volume.
PitchAudioSource-3 – 3Playback speed. 1 = normal, 2 = octave up.
PanStereoAudioSource-1 – 1Stereo pan. -1 = full left, +1 = full right.
SpatialBlendAudioSource0 – 10 = 2D, 1 = fully 3D positioned.
LowPassCutoffAudioLowPassFilter10 – 22000 HzFrequencies above this are removed. Lower = more muffled.
LowPassResonanceAudioLowPassFilter1 – 10Resonant peak at the cutoff. Higher = sharper, more "synthy".
HighPassCutoffAudioHighPassFilter10 – 22000 HzFrequencies below this are removed. Higher = thinner.
EchoDelayAudioEchoFilter10 – 5000 msTime between echo repeats.
EchoDecayAudioEchoFilter0 – 1How quickly echoes fade. Higher = longer tail.
EchoWetAudioEchoFilter0 – 1Mix of echo effect. 0 = dry, 1 = full echo.
DistortionLevelAudioDistortionFilter0 – 1Overdrive intensity.
ChorusRateAudioChorusFilter0 – 20 HzSpeed of the chorus modulation.
ChorusDepthAudioChorusFilter0 – 1Intensity of the chorus effect.
ReverbDecayTimeAudioReverbFilter0.1 – 20 sLength of the reverb tail. Short = room, long = cathedral.
ReverbDryLevelAudioReverbFilter-10000 – 0 dBDry signal level. Lower = less dry, more reverb-heavy.
💡
Auto-discovery: UnityAudioFX scans the same GameObject for audio filter components on Awake. You only see sliders for filters that actually exist on the object. Add an AudioLowPassFilter and the LowPassCutoff/Resonance properties appear. Remove it and they disappear. No manual wiring needed.
Your First Binding
Step-by-step walkthrough from empty scene to working effect
  1. 1
    Confirm the runner exists. Your scene needs one GameObject with a DriverMappingRunner component. If you're starting fresh, create an empty GameObject called 4E_Runner and add the runner component. Also make sure the 4E_DataBus and a source (like 4E_PoseChannelSource) exist — see SetupGuide if you haven't done the initial wiring.
  2. 2
    Press Play briefly. Press F12 to open the DataFlowMonitor. Confirm that pose channels are being written. If nothing is flowing, fix the source first — bindings reading from an empty bus will silently do nothing.
  3. 3
    Stop Play and pick your approach. Scene binding for a one-off effect in this scene; asset mapping if you want reuse or runtime swap. For this walkthrough we'll use a scene binding.
  4. 4
    Expand the Scene Bindings list on the runner in the inspector. Click + to add a new binding.
  5. 5
    Pick a channel. Click the channel picker. Filter by category (Joints / Body / Face / Hands / Audio / Time / Input). In Play mode, each row shows the current live value so you can verify you're picking a channel that's actually changing.
  6. 6
    Pick a target type. The inspector collapses unrelated fields so you only see what's relevant.
  7. 7
    Drag the scene target into the relevant field — transform, mesh, renderer, light, particle system, audio source, or reflection target GameObject.
  8. 8
    Adjust the remap. The editor auto-fills sensible defaults but you'll usually want to tune. Watch the live value while you move, then set inputMin = your "rest" value, inputMax = your "full range" value.
  9. 9
    Press Play and test. Iterate on remap, smoothing, and response curve until it feels right. You can edit while in Play mode — changes take effect immediately.
  10. 10
    Exit Play mode. Unity automatically saves the binding configuration with the scene. But: edits you made during Play mode do NOT persist. Write them down, or use Copy Component → Paste Component Values.
Play-mode edits don't persist. This is Unity's default behaviour, not a 4E quirk. If you tune a binding in Play mode and love the result, copy the numbers before you stop Play — otherwise they're lost.
Deploy to Scene
Move bindings from a DriverMapping asset into the Runner's scene bindings

Asset templates vs scene bindings

DriverMapping ScriptableObjects are template libraries — they store the recipe (channels, remap ranges, modes, curves, target types) but hold no scene references. They're reusable across any scene in the project.

The DriverMappingRunner in your scene has a sceneBindings list where actual scene object references live and persist with the scene. To get bindings from an asset into the runner, you deploy them.

How to deploy

  1. 1
    Select the DriverMapping asset in the Project window. Its custom inspector shows all the bindings stored in the asset.
  2. 2
    Click "Deploy to Scene" to copy a single binding into the Runner's sceneBindings list. The binding's recipe (channel, remap, mode, curve, target type) is copied — but the target field is left empty for you to assign.
  3. 3
    Click "Deploy All to Scene" to copy every binding from the asset that isn't already deployed. Bindings that already exist in sceneBindings (matched by channel key + target type) are skipped to avoid duplicates.
  4. 4
    Assign scene targets on the Runner. Select the Runner in the Hierarchy, expand sceneBindings, and drag your scene objects into the target fields. These references persist with the scene — the asset keeps its templates clean for reuse.
💡
The asset stays untouched. Deploying copies bindings out of the asset — it never modifies the asset itself. You can deploy the same asset to multiple scenes, each with different scene targets. Edit the asset to update the recipe, then re-deploy to propagate changes.
🍳
Single-Binding Recipes
Copy-paste starting points for common one-shot effects
RECIPE 01
Right elbow → character mouth open
Bend your arm to make a character yawn. Classic first effect.

Scene setup

  • A rigged character with a SkinnedMeshRenderer that has a mouth blendshape (e.g. jawOpen or mouthOpen).

Binding values

channelKey
pose/joint/rightElbow/bend
useRemap
true
inputMin / Max
60 / 180 (tight fold to straight arm)
outputMin / Max
0 / 100 (blendshape weights are 0–100)
clamp
true
mode
Direct
smoothing
0.5 (tames MediaPipe jitter)
targetType
BlendShape
targetMesh
The character's face SkinnedMeshRenderer
blendShapeIndex
Use the dropdown to pick jawOpen

Tuning

  1. Press Play and bend your arm while watching the mouth.
  2. If the mouth opens too early, raise inputMin (e.g. to 90°). If it never fully opens, lower inputMax (e.g. to 160°).
  3. If the movement feels twitchy, raise smoothing to 0.7. If sluggish, lower to 0.3.
RECIPE 02
Hand height → light colour hue
Raise your hand to cycle through the colour wheel.

Scene setup

  • A Point Light or Spot Light somewhere visible in the scene.

Binding values

channelKey
pose/landmark/rightWrist/y
inputMin / Max
−0.8 / 0.8 (low hand to high hand, Unity-space Y)
outputMin / Max
0 / 1 (full hue wheel)
clamp
true
mode
Direct
smoothing
0.3
targetType
LightColor
targetLight
Drag your light here
lightColorSaturation
1.0 (vivid colours)
lightColorBrightness
1.0

Also drive intensity in a second binding

Add a second binding on the same light — same channelKey, but target LightIntensity and output 0.5 → 4.0. Now raising your hand both cycles the hue and increases brightness.

RECIPE 03
Clap → particle burst (Pulse mode)
Pulse mode turns any momentary signal into a hit-and-fade effect.

Source setup

You need a channel that spikes when you clap. Two options:

  • Audio path: audio/beat from AudioAnalysisSource.
  • Motion path: pose/body/velocity as a proxy, tuning the threshold to your energy level.

Binding values

channelKey
audio/beat (or your chosen trigger)
inputMin / Max
0 / 1
outputMin / Max
0 / 1
mode
Pulse
modeThreshold
0.5
pulseDecay
0.4 seconds
smoothing
0 (let the pulse be snappy)
targetType
ParticleEmission
targetParticles
Your particle system
maxEmissionRate
500

Result

Each time the beat channel crosses 0.5, emission snaps to 500/s and decays to 0 over 0.4s. The particle system pulses on each hit.

RECIPE 04
Pose-locked effect (Latch mode)
Strike a pose to freeze a value; trigger to unfreeze.

Concept

The first time your right elbow bends past halfway, capture that bend value and hold it. Press spacebar to release the lock. Useful for "aim and freeze" style interactions.

Binding values

channelKey
pose/joint/rightElbow/bend
inputMin / Max
60 / 180
outputMin / Max
0 / 1
mode
Latch
modeThreshold
0.5 (capture past halfway)
resetChannel
input/key/space
targetType
MaterialFloat or whatever you want driven
Latch's reset channel uses a raw > 0.5 test. Key channels (input/key/space) work cleanly. For noisy analogue channels, pre-process with a ThresholdProcessor first.
RECIPE 05
Step through sections (Sequence mode)
Tap-to-advance through a list of preset values.

Concept

Each time you raise your right arm above the threshold, the binding steps to the next value in the sequence. Useful for cycling colours, advancing to the next song section, or stepping through camera angles.

Binding values

channelKey
pose/landmark/rightWrist/y
inputMin / Max
−0.5 / 0.5
outputMin / Max
0 / 1
mode
Sequence
modeThreshold
0.7
sequenceValues
[0.0, 0.25, 0.5, 0.75, 1.0]
targetType
MaterialFloat (or Animator / Light / etc.)

The sequence wraps — after the last value, the next trigger cycles back to index 0.

RECIPE 06
Two-handed gate (Gate mode)
"Only listen to my right arm when my left hand is visible."

Concept

The signal passes through unchanged when the gate channel is above 0.5. When the gate drops, the output snaps to outputMin. This lets you engage and disengage an effect using a separate gesture.

Binding values

channelKey
pose/joint/rightElbow/bend
inputMin / Max
60 / 180
outputMin / Max
0 / 1
mode
Gate
gateChannel
hand/left/detected (opens gate when left hand is visible)
targetType
any continuous target
💡
hand/left/detected is 0 or 1 — it crosses 0.5 cleanly. For analogue channels (like a landmark Y position), pre-process with a ThresholdProcessor if it doesn't cross 0.5 cleanly.
RECIPE 07
Custom script property (Reflection)
Drive something the built-in target types don't cover.

Scenario

You've got your own FogController script on a GameObject, with a public float field density. You want to drive it from body velocity.

Binding values

channelKey
pose/body/velocity
inputMin / Max
0 / 1
outputMin / Max
0.1 / 2.5 (the range your FogController expects)
mode
Direct
smoothing
0.4
targetType
Reflection
reflectionTarget
The GameObject hosting FogController
reflectionComponentType
FogController
reflectionMemberName
density

Supported member types: float, bool, int, double. Anything else will fail to write silently — check the Console for the warning.

📊
Dashboard / UI Recipes
Build live HUDs and dashboards from UGUI elements driven by DataBus channels

These recipes are the building blocks of the sample dashboard generated by Window > 4E > Create Sample Dashboard. Each one is a single binding (or two coordinated bindings) that produces one HUD widget. Combine them to build a full TouchDesigner-style control panel.

The Sample Dashboard — 6 tabs, 48 widgets

The generated dashboard lives under [4E_Dashboard] in the scene. It has a header with six clickable tabs and a body that shows one tab panel at a time. Each panel is a 4×3 grid of 12 cards, and the widget choice inside each tab is motion-matched to the data it visualises — vertical bars for vertical motion, horizontal meters for side-to-side motion, radial gauges for rotational motion.

TabPurposeTypical widgets
ALLGeneral mix — one of every widget typeGauge, donut, key metrics, bars, big number, sliders, status, gesture button, histogram, two line graphs, heatmap
BODYPose joints, limb raises, velocityTwo radial gauges (elbow bends), two donuts (velocity + head tilt), vertical bars for arm/leg raise, horizontal KeyMetrics for widths / wrist X
HANDSFingertip positions + detectionTwo radial gauges (hand openness proxies), two donuts (L/R detected), vertical bars for right fingertip Y, horizontal KeyMetrics for hand X
FACEBlendshapes — jaw, smile, blink, browGauges for jaw/eye-wide, donuts for pucker/cheek, vertical bars for blink + brow, horizontal KeyMetrics for smile + mouth stretch
dB REAPERPaulXStretch parameters via OSCSliders and gauges for stretch amount, pitch shift, filter cutoffs, freeze toggle
dB UNITYUnity built-in audio filtersSliders and gauges for low-pass, high-pass, reverb, chorus, echo parameters

Click a tab in the header to swap panels. Press F9 to show or hide the whole dashboard — the toggle component lives on the Canvas so it keeps working even while the root is hidden.

💡
Two wiring modes: Wire Sample Dashboard (Self-Test LFOs) drives every widget from time/lfo/* so you can see the whole thing animate without a camera. Wire Sample Dashboard (Real Channels) swaps in motion-matched pose / hand / face channels per tab. Start with self-test to verify layout, then switch to real channels when you're ready to move.
RECIPE 08
Radial gauge with centre value label
Two bindings: one for the gauge ring, one for the number in the middle.

Scene setup

  • Create an Image. Set Source Image to UISprite (or any sprite), Image Type = Filled, Fill Method = Radial 360, Fill Origin to Top.
  • Add a child Text centred on the Image — this is the value label.

Binding 1 — the ring

channelKey
pose/joint/rightElbow/bendNorm
inputMin / Max
0 / 1
outputMin / Max
0 / 1
smoothing
0.4
targetType
UGUIImageFillAmount
targetImage
The radial Image

Binding 2 — the centre label

channelKey
pose/joint/rightElbow/bendNorm (same)
outputMin / Max
0 / 100 (display as percent)
targetType
UGUITextValue
targetText
The child Text element
textFormat
{0:F0}%

Both bindings read the same channel but produce different visualisations of it. The smoothing on the ring keeps it from jittering; the label updates instantly.

RECIPE 09
Vertical bar chart from multiple channels
One binding per bar — a column for each frequency band, joint angle, or whatever you want to compare.

Scene setup

  • Create N Images side-by-side in a horizontal row, each with Image Type = Filled, Fill Method = Vertical, Fill Origin = Bottom.
  • Add a Background Image behind each one for the empty-bar look (slightly darker).

Bindings (one per bar, all the same shape)

Bar 1 channelKey
audio/bass
Bar 2 channelKey
audio/mid
Bar 3 channelKey
audio/high
Bar 4 channelKey
audio/amplitude
inputMin / Max
0 / 1
outputMin / Max
0 / 1
smoothing
0.2 (light smoothing — audio is fast)
targetType
UGUIImageFillAmount
targetImage
Each bar's Image (one per binding)

Add four bindings to your DriverMappingRunner, one per bar, swapping the channelKey and targetImage. Press Play and you have a live audio EQ visualiser.

RECIPE 10
Big number counter (score / energy display)
Channel value scaled into a HUD-style large number.

Scene setup

  • One Text element, font size 60+, centred. Optional small caption Text underneath.

Binding values

channelKey
pose/body/velocity
inputMin / Max
0 / 1
outputMin / Max
0 / 9999
clamp
true
smoothing
0.6 (heavy — counters look weird flickering)
targetType
UGUITextValue
targetText
The big number Text
textFormat
{0:N0} (thousand separators) or {0:F0}

The trick is heavy smoothing — without it the number flickers wildly because every frame's velocity is slightly different. With smoothing 0.6 the counter rises and falls smoothly like a real telemetry display.

RECIPE 11
Status indicator pill (red / green / amber)
A coloured dot that turns green when something is OK and red when it isn't.

Scene setup

  • One small circular Image (circle sprite or rounded square). Set its colour to white initially.
  • Optional adjacent Text label, e.g. "Hand Tracking".

Binding values

channelKey
hand/right/detected
inputMin / Max
0 / 1
outputMin / Max
0.0 / 0.33 (red hue → green hue)
mode
Direct
targetType
UGUIImageColor
targetImage
The pill Image
imageColorSaturation
1.0
imageColorBrightness
1.0

The remap from 0–1 to 0.0–0.33 is the trick: hue 0 is red, hue 0.33 is green. Channel value 0 (no hand) → red, 1 (hand visible) → green.

Variation: amber when uncertain

If your channel is gradient (e.g. pose/landmark/nose/visibility), set the output range to 0.0–0.33 with no clamp tweaks — values around 0.5 will naturally land on yellow/amber.

RECIPE 12
Horizontal slider as a live input meter
Slider acts as both display and input — moves with body, can be grabbed by mouse.

Scene setup

  • Standard UGUI Slider, horizontal layout. Set Min Value = 0, Max Value = 1.
  • Optional: hide the handle (set its alpha to 0) for a pure display look. Show the handle if you want to allow manual override.

Binding values

channelKey
pose/joint/rightElbow/bendNorm
outputMin / Max
0 / 1
smoothing
0.3
targetType
UGUISliderValue
targetSlider
The Slider

Note: the binding writes the slider value every frame, so manual mouse drags get overwritten. If you want the slider to be truly bidirectional (binding writes when channel is "active", user controls when not), use Gate mode with a "performer present" channel.

RECIPE 13
Gesture-triggered button (raise arm to click)
Use Pulse mode to fire a UI button cleanly with one gesture.

Scene setup

  • A standard UGUI Button. Wire its onClick in the inspector to whatever you want to trigger — load scene, advance dialogue, fire VFX, etc.
  • Optional: a small status Image next to the button that flashes when the button fires.

Binding values

channelKey
pose/joint/rightUpperArm/raiseNorm
inputMin / Max
0 / 1
outputMin / Max
0 / 1
mode
Pulse
modeThreshold
0.7 (must raise arm above 70% of full range)
pulseDecay
0.5
boolThreshold
0.5 (button fires when pulse value > 0.5)
targetType
UGUIButtonTrigger
targetButton
The Button

Why Pulse + ButtonTrigger together: Pulse turns the slow continuous arm-raise into a sharp spike, then decays. The button fires once on the rising edge and can't fire again until the pulse fully decays and you do the gesture again. Eliminates accidental retriggers when the user holds the pose.

RECIPE 14
Audio histogram (12-bar EQ visualiser)
Lots of thin vertical bars driven by audio — the classic dashboard visualiser.

Scene setup

  • 12 thin Image elements in a horizontal row, each with Image Type = Filled, Fill Method = Vertical, Fill Origin = Bottom.
  • Use HorizontalLayoutGroup on the parent for even spacing.

Binding pattern

The current AudioAnalysisSource only exposes three bands (audio/bass / audio/mid / audio/high). For a true 12-band histogram you'd need to extend AudioAnalysisSource to write per-FFT-bin channels. For now, two practical patterns:

  • Three groups of four: bars 1–4 → audio/bass, 5–8 → audio/mid, 9–12 → audio/high. Each group has a slightly different smoothing or response curve so they don't all move identically.
  • Phased LFOs: for self-test mode, drive each bar with the same LFO but apply a different remap per bar (offset the input range) so they appear to ripple.

Per-bar binding values:

channelKey
audio/bass / mid / high (per group)
outputMin / Max
0 / 1
smoothing
0.1–0.3 (vary per bar)
targetType
UGUIImageFillAmount
💡
Quick start: the menu item Window > 4E > Create Sample Dashboard generates the full 6-tab dashboard built from these exact recipes. Then Wire Sample Dashboard (Self-Test LFOs) auto-binds every widget to time-based LFOs so you can press Play and see the whole thing animate without any input setup. Wire Sample Dashboard (Real Channels) swaps in the motion-matched pose / hand / face channels for live use. Press F9 at runtime to show / hide the dashboard.
🎵
Audio Effect Recipes
Body-driven audio — from filter sweeps to time-stretching to reverb control. Two paths: dB Reaper (PaulXStretch via OSC) and dB Unity (built-in filters, zero latency)

These recipes use the two audio target types. dB Reaper recipes require Reaper running with PaulXStretch on each track. dB Unity recipes work entirely in Unity with no external dependencies. Both can be driven by body movement, video feed, or the F11 channel slider panel.

RECIPE 15
Body-driven filter sweep (dB Unity)
Classic DJ-style low-pass filter controlled by arm raise. Zero latency.

Scene setup

  • AudioSource with a music clip + AudioLowPassFilter + UnityAudioFX on the same GameObject.

Binding values

channelKey
pose/joint/rightUpperArm/raiseNorm
inputMin / Max
0 / 1
outputMin / Max
200 / 22000 (Hz — arm down = muffled, arm up = bright)
smoothing
0.15
targetType
UnityAudioFXProperty
targetUnityAudioFX
The GameObject with the filter
unityAudioFXProperty
LowPassCutoff

The most immediately satisfying audio binding. Raise your right arm and the sound opens up. Lower it and it goes muffled. Everyone understands it the first time.

RECIPE 16
Hand height → echo delay (dB Unity)
Move your hand up and down to change how far apart the echoes are.

Scene setup

  • AudioSource + AudioEchoFilter + UnityAudioFX.

Binding values

channelKey
pose/landmark/leftWrist/y
inputMin / Max
-0.5 / 0.5 (calibrate with F12 for your seated range)
outputMin / Max
50 / 2000 (ms)
smoothing
0.3 (echo delay changes sound weird if they jitter)
targetType
UnityAudioFXProperty
unityAudioFXProperty
EchoDelay
RECIPE 17
Movement = distortion (dB Unity)
Stand still = clean sound. Move = gritty overdrive.
channelKey
pose/body/velocity
inputMin / Max
0 / 0.1 (seated; use 0/1 for full-body)
outputMin / Max
0 / 0.8 (don't go full 1.0 — it clips hard)
smoothing
0.4
targetType
UnityAudioFXProperty
unityAudioFXProperty
DistortionLevel
RECIPE 18
Arm raise → time stretch (dB Reaper)
Raise your arm to stretch time. Lower it to return to real-time playback.

Scene setup

  • PaulXTrack on a scene GameObject pointing at Reaper AudioSlot_01.
  • Reaper running with audio loaded in PaulXStretch on that track.

Binding values

channelKey
pose/joint/rightUpperArm/raiseNorm
inputMin / Max
0 / 1
outputMin / Max
1 / 200 (stretch factor: 1× to 200×)
smoothing
0.3
targetType
PaulXTrackProperty
paulXProperty
StretchAmount

The signature PaulXStretch gesture. Arm down = real-time playback. Arm up = time bends into an ambient drone. The transition is smooth because of the log-skew on the stretch parameter — most of the slider travel covers the musically useful 1–50× range.

RECIPE 19
Gesture-triggered freeze (dB Reaper)
Raise both arms above your head to freeze the current spectral frame. Lower to resume.
channelKey
pose/joint/rightUpperArm/raiseNorm
mode
Switch
modeThreshold
0.7 (arm must be quite high to trigger)
outputMin / Max
0 / 1
targetType
PaulXTrackProperty
paulXProperty
Freeze

Uses Switch mode so the freeze is binary (on/off), not gradual. The threshold of 0.7 means you have to deliberately raise your arm high — accidental shoulder shrugs won't trigger it. Combine with Recipe 18 for "stretch while moving, freeze when you stop".

RECIPE 20
Head tilt → pitch shift (dB Reaper)
Tilt your head to shift the pitch up or down.
channelKey
pose/joint/headTilt/bendNorm
inputMin / Max
0.3 / 0.7 (calibrate: head tilt range is usually narrow)
outputMin / Max
-12 / 12 (±1 octave)
smoothing
0.25
targetType
PaulXTrackProperty
paulXProperty
PitchShift
RECIPE 21
Stillness → reverb cathedral (dB Unity)
Stand still and the sound fills with reverb. Move and it dries out.
channelKey
pose/body/velocity
inputMin / Max
0 / 0.1
outputMin / Max
15 / 0.5 (INVERTED — low velocity = long reverb, high velocity = short)
invert
true
smoothing
0.5 (reverb changes should feel slow and ambient)
targetType
UnityAudioFXProperty
unityAudioFXProperty
ReverbDecayTime

The inversion is the key: most bindings are "more motion = more effect". This one is the opposite — stillness is the gesture. Standing perfectly still in front of the camera creates a 15-second reverb cathedral. Start moving and the space shrinks. Beautiful for meditative performance.

💡
Combining dB Reaper + dB Unity: you can bind the same pose channel to BOTH a PaulXTrack property AND a UnityAudioFX property simultaneously. For example: arm raise drives PaulXStretch stretch amount (extreme time-warp on processed sounds) AND Unity's low-pass cutoff (filter sweep on dry sounds). One gesture, two different audio streams responding in complementary ways. That's the body orchestra.
🎛
Tuning UI Bindings
Take full control of how a channel maps onto every UI widget

Every dashboard widget is driven by a DriverBinding — the same pipeline described in the Pipeline section. The job of a UI binding is to squeeze the useful slice of a channel's range into the widget's native display range. This section is a complete guide to doing that well.

Why tuning matters

A channel rarely spans the exact range a widget expects. A blendshape is 0–1, a bend angle is 0–180, a body velocity hovers between 0 and about 0.1 in seated use. A donut wants 0–1 fill. A big-number text might want 0–9999. A colour image might want 0–0.33 (hue red→green).

The remap stage is where you tell the binding: "when the channel is at this, the widget should show that". Get that right and the widget feels alive. Get it wrong and the widget is either pinned at full, frozen at empty, or jitters between two values with no middle.

The four knobs you actually tune

KnobLives inWhat it controls
inputMin / inputMaxDriverBinding > Value MappingThe slice of the channel you care about. Anything below inputMin reads as "empty", anything above inputMax reads as "full".
outputMin / outputMaxDriverBinding > Value MappingWhat the widget shows at "empty" and "full". For a radial gauge fillAmount, that's 0 and 1. For a big number, maybe 0 and 9999.
clampDriverBinding > Value MappingLeave on. Prevents the widget from overshooting past outputMin/outputMax when the channel exceeds your input range.
smoothingDriverBinding > SmoothingHow much to average across frames (0 = instant, 0.9 = syrupy). Add enough to kill MediaPipe jitter; not so much that the widget feels laggy.

That's it. Mode, response curve, invert — all useful, all optional. For 95% of dashboard widgets, these four knobs are the whole job.

Worked example: 0.3–0.6 of a channel mapped onto a 0–100% donut

Say you're driving a donut chart from face/blendshape/mouthSmileLeft. The channel technically goes from 0 to 1, but in practice the blendshape only ever reaches 0.6 with a big grin, and anything below 0.3 is just resting face noise. You want:

  • Neutral / small smile → empty donut
  • Medium smile (0.45) → ~50% donut + centre label reads "50%"
  • Big grin (0.6 or above) → fully filled donut + centre label reads "100%"

Binding 1 — the donut ring (Image fillAmount)

channelKey
face/blendshape/mouthSmileLeft
useRemap
true
inputMin
0.3 — the dead zone's upper edge
inputMax
0.6 — the realistic peak for this user
outputMin
0 — donut fillAmount empty
outputMax
1 — donut fillAmount full
clamp
true
smoothing
0.2 — face blendshapes need only light smoothing
targetType
UGUIImageFillAmount
targetImage
The donut ring Image

Binding 2 — the centre label ("50%")

channelKey
face/blendshape/mouthSmileLeft — same channel
inputMin
0.3 — same slice
inputMax
0.6
outputMin
0 — number range, not fill
outputMax
100
smoothing
0.15
targetType
UGUITextValue
targetText
The centre-of-donut Text element
textFormat
{0:F0}% — zero decimals, trailing %
How the remap reads the value, step by step:
Channel reads 0.2 (not smiling) → below inputMin → ring = 0, label = "0%".
Channel reads 0.3 (starting to smile) → exactly at inputMin → ring = 0, label = "0%".
Channel reads 0.45 (halfway through your range) → (0.45 − 0.3) / (0.6 − 0.3) = 0.5 → ring = 0.5, label = "50%".
Channel reads 0.6 (full grin) → at inputMax → ring = 1.0, label = "100%".
Channel reads 0.8 (even bigger grin, somehow) → clamped → ring = 1.0, label = "100%".

Both bindings share the same channel and the same input range. Only the output range differs, because the donut ring natively wants 0–1 and the label natively wants 0–100. This twin-binding pattern — one for the visual, one for the number — is how every gauge / donut card in the sample dashboard is wired.

Finding the right input range for a new channel

Guessing inputMin / inputMax is the most common source of "my widget doesn't move" bugs. Don't guess — measure.

  1. Press Play. Open the DataBus Monitor with F12.
  2. Filter to the channel you care about (e.g. click the "Face" filter button, or type smile into the search field).
  3. Do your full range of motion — rest, halfway, maximum. Watch the value column.
  4. Note the lowest useful value (anything below is "noise / rest") and the highest comfortable value (anything above is "asking for too much"). Those are your inputMin and inputMax.
  5. Plug them into the binding, stop Play, Play again, re-check.
💡
Set inputMax to your comfortable peak, not the theoretical max. If the real peak is 0.6 and you use 1.0, the widget will only ever fill to 60% — and that last 40% of "no one can reach it" range is wasted visual space.

Common widget output ranges — cheat sheet

Widget targetoutputMinoutputMaxNotes
UGUIImageFillAmount01Native Unity range. Same for radial gauges, donuts, and vertical / horizontal bars.
UGUISliderValue01Assuming the Slider's Min/Max are 0 and 1, which the generator uses.
UGUIImageColor (hue)0.00.330 = red, 0.17 = yellow, 0.33 = green. Leave saturation/brightness at 1.
UGUIImageColor (full spectrum)01Full hue wheel. Good for ambient "channel activity" backgrounds.
UGUITextValue — percent0100Pair with {0:F0}% format.
UGUITextValue — big counter09999Pair with {0:N0} for thousand separators. Use heavy smoothing (0.4–0.6).
UGUITextValue — degrees0180Pair with {0:F0}°. Feed from raw bend channel (not bendNorm).
UGUIToggleValueRemap doesn't apply. Uses boolThreshold instead — default 0.5.
UGUIButtonTriggerSame. Combine with Pulse mode to fire cleanly once per gesture.

Smoothing recommendations per widget type

WidgetSuggested smoothingWhy
Radial gauge / donut0.15 – 0.25The eye reads rings as analog meters — small jitter reads as broken. Moderate smoothing lands in the sweet spot.
Horizontal key metrics0.15 – 0.25Same as gauges. If the value also drives a label, use the same smoothing on both bindings so they stay in sync.
Vertical / horizontal bar chart0.10 – 0.20Lighter — bars look good bouncing with the signal. Too much smoothing makes them feel mushy.
Big number counter0.40 – 0.60Heavy. A counter that flickers between 2,347 and 2,349 every frame is unreadable.
Sliders (display-only)0.15 – 0.30Medium. The handle position needs to feel physical, like a real fader.
Status pills (hue colour)0.10 – 0.20Light. If the channel is binary (hand/*/detected), you can set smoothing to 0 for instant snap.
Line graph0 (none)The graph already smooths visually by plotting history. Adding smoothing here gives you double filtering and hides the detail.
Heatmap cells0.15Same logic as bars.
Gesture button (Pulse mode)0Pulse needs a crisp rising edge. Smoothing the input defeats the purpose.

When to reach for invert and response curve

The four knobs above cover most cases. These two are for when a linear 0→1 response doesn't feel right.

  • invert — flips the mapping. "Hand down = full" instead of "hand up = full". Easier than swapping outputMin and outputMax, and it plays nicely with clamp.
  • response curve — a Unity AnimationCurve that reshapes the 0–1 space after remap. Use an ease-in curve (quadratic) when you want small motion to be subtle and big motion to be dramatic. Use ease-out (square root) when small motion should already read strongly.
Dead-zone hack: a curve that's flat at zero for the first 20% and then ramps linearly creates a software dead zone — noise near the resting channel value won't wiggle the widget, but real input still moves it. Useful when you can't tighten inputMin further without losing valid input.

Where to actually change these values

  1. Find the [4E_Runner] GameObject in the scene Hierarchy.
  2. In the Inspector, expand DriverMappingRunner > Scene Bindings. There's one entry per widget — the list is long.
  3. Each entry has a channelKey at the top and an expandable Value Mapping foldout with the four knobs.
  4. Tune in Play mode to see the effect live, but copy the final numbers — Play mode edits are lost on stop. Right-click the component header → Copy Component, stop Play, Paste Component Values.
Target entries, not recipes. Wire Sample Dashboard (…) wipes and regenerates every dashboard binding. If you've hand-tuned values in the Inspector and then re-run the wire menu, your edits are gone. Either stop re-running it, or edit the wire code in SampleDashboardGenerator.cs to bake your tuned values into the regeneration.
📦
Preset Packs
Multi-binding presets — several bindings working together for a coordinated effect

Real performances usually layer multiple bindings on the same object or channel. These are starting points — drop all the rows into one DriverMappingRunner's Scene Bindings list, adjust to taste, press Play.

1. Body-driven lighting show

A single spotlight reacts to your movement, music, and a slow colour drift all at once.

#SourceTargetRemapModeResult
1pose/body/velocityLightIntensity0→1 / 0.2→8DirectMove = bright
2time/lfo/slowLightColor0→1 / 0→1DirectRainbow cycle
3audio/beatLightIntensity0→1 / 0→10Pulse 0.15sBeat flash

Note: bindings 1 and 3 both drive LightIntensity — the Pulse binding writes after the continuous one each frame, so the flash overrides. If you want them to add instead, route binding 3 through a Reflection target on an intermediate script.

2. Audio orchestrator

Gesture-controlled audio mixing. Hand crossfades between two tracks, body velocity drives tempo, right-hand visibility plays or pauses the whole thing.

#SourceTargetRemapModeResult
1hand/left/0/yAudioCrossfade−0.5→0.5 / 0→1Direct, Smooth 0.3Hand height = track blend
2pose/body/velocityAudioPitch0→1 / 0.9→1.2Direct, Smooth 0.5Movement = tempo
3hand/right/detectedAudioPlayStop0→1 / 0→1Switch 0.5Show right hand = play

3. Interactive character

A rigged character mirrors your face and head in real time. Bindings 1 and 2 drive facial blendshapes; binding 3 tilts the head with yours.

#SourceTargetRemapModeResult
1face/blendshape/jawOpenBlendShape "MouthOpen"0→1 / 0→100DirectYour mouth = character mouth
2face/blendshape/eyeBlinkLeftBlendShape "EyeBlinkL"0→1 / 0→100DirectYour blink = character blink
3pose/joint/headTilt/bendNormTransform RotationZ0→1 / −15→15Direct, Smooth 0.2Head tilt = character tilt

4. Particle explosion controller

Particle emission layered from continuous motion (velocity), discrete pulses (bass hits), and hand-controlled size.

#SourceTargetRemapModeResult
1pose/body/velocityParticleEmission (200)0→1 / 0→1DirectMovement = particles
2audio/bassParticleEmission (500)0→1 / 0→1Pulse 0.3sBass hit = burst
3hand/left/0/yTransform UniformScale−0.5→0.5 / 0.5→3Direct, Smooth 0.4Hand height = particle size
Runtime Swap
Cycle between asset mappings mid-performance

DriverMappingRunner has a Mapping Library list. Drag multiple DriverMapping assets into it, set the Swap Key Path (default m), and each press cycles to the next asset. The active asset replaces the assetMapping slot and its bindings take effect immediately.

FieldTypeNotes
mappingLibraryList<DriverMapping>The assets to cycle through. Must be non-empty to enable swapping.
swapKeyPathstringInput System key path, e.g. m, space, digit1. Default: m.
runInLateUpdateboolIf true, bindings apply in LateUpdate (after animation); if false, in Update. Default: true.
ℹ️
Scene bindings are not affected by swap — they keep running regardless. Use scene bindings for the "always on" core rig, and put the per-section variations in asset mappings so you can cycle through them live.
Asset targets don't re-wire on swap. The scene-reference list on the runner is shared — it gets re-applied to whichever asset becomes active. So every asset in your library must have the same binding count and compatible target types in the same order, or you'll get mismatched assignments. If your assets need different targets, use multiple runners instead.
!
Troubleshooting
Common failure modes and fixes

Nothing happens when I press Play

  1. Open F12 (DataFlowMonitor). If no channels are flowing, the issue is upstream — fix the source (MediaPipe / WebSocket / AudioAnalysisSource) first.
  2. If channels are flowing but your binding's channel name isn't there, check for typos in channelKey. Channel names are case-sensitive and use forward slashes.
  3. Check the binding's active toggle is on.
  4. Check DriverMappingRunner exists in the scene and the binding is in its Scene Bindings list (or in an asset wired to its Asset Mapping slot).

Target moves but the range is wrong

  1. Open F12 and watch the source channel while you move. Note the min and max values you actually reach.
  2. Set inputMin = your observed min, inputMax = your observed max.
  3. If the output is still wrong, check outputMin / outputMax match what the target expects (remember: blendshapes are 0–100, not 0–1).
  4. If values are the right magnitude but flipped, toggle invert.

Movement is jittery

  1. First, check the source itself via F12. Is it jittery at the source? MediaPipe is noisy — that's normal.
  2. Raise the binding's smoothing to 0.4–0.7.
  3. If smoothing enough to kill the jitter makes motion feel laggy, chain a SmoothProcessor upstream (spring-damper mode) and leave the binding smoothing at 0.

Latch / Gate / Pulse / Sequence never triggers

  1. These modes detect rising edges. If the channel starts above the threshold, the first trigger only fires after the channel drops and rises again. Use F11 to drag the slider down then up.
  2. Check modeThreshold is within your remapped output range. A threshold of 0.5 makes no sense if your output range is 0–100.
  3. Gate / Latch reset channels use a raw > 0.5 test on the DataBus value, ignoring your binding's remap. Pre-process with a ThresholdProcessor if needed.

Material changes don't appear

  1. Make sure the material property name is correct. URP Lit uses _BaseColor, not _Color. Emission is _EmissionColor.
  2. For emission to show on URP Lit, the material's Emission keyword must be enabled in the material inspector.
  3. The binding writes to Renderer.materials[materialIndex], which creates an instance. Changes won't reflect in the shared asset — that's fine for runtime but can be confusing when inspecting.
  4. MaterialColor needs a colour property, not a float. If the named property doesn't exist, it falls back to _BaseColor, then _Color. If none exist, a warning is logged.

Reflection binding silently does nothing

  1. Check the Unity Console for [4E Binding] warnings logged during Initialise(). They tell you whether the component was found and whether the member was found.
  2. Component type name: try both short (Light) and fully-qualified (UnityEngine.Light). The match is case-insensitive and uses Contains, so light also works.
  3. Member name must be a public field or property. Private and serialised-but-private fields aren't supported.
  4. Only float, bool, int, and double members are supported. Strings, Vector3s, Colors etc. won't be written.
Tips & Best Practices
Hard-won wisdom from using this live

Keyboard shortcuts

KeyPanelPurpose
F8Calibration ToolVerify coordinate orientation is correct for your camera
F9Sample DashboardShow / hide the generated [4E_Dashboard] overlay
F10Webcam PreviewSee camera feed + all landmarks overlaid
F11Channel SlidersInteractive testing — override values with sliders, no camera needed
F12DataBus MonitorRead-only view of every channel value in real time

Building bindings — general tips

  • Start with F11 sliders to test bindings before involving MediaPipe. Prove the binding works with a synthetic input, then swap in the real channel.
  • Use BendNorm / RaiseNorm channels (0–1) when you don't need the angle in degrees — they save you a remap step.
  • Always add some smoothing (0.1–0.3) to body-driven bindings. MediaPipe is jittery; the eye reads raw values as "broken".
  • Use Gate mode with hand/*/detected to create effects that only work when a specific hand is visible. Simple, reliable "kill switch".
  • Use Sequence mode to cycle through presets with a single repeated gesture — much cleaner than having one binding per preset.
  • Combine Pulse with audio/beat for beat-reactive flashing effects. Pair with very short pulseDecay (0.1–0.2s) for snappy strobes.
  • AudioCrossfade with smoothing 0.3–0.5 creates cinematic transitions between two tracks. Set both clips to Loop.
  • Use Response Curves to add dead zones (ignore small values) or exaggerate extremes (make the full range feel more dramatic).

Tips for live performance

  • Keep the core rig as scene bindings. Reserve asset mappings for variation, not foundation. If the swap logic misfires mid-show, you want the base effects to still work.
  • Always leave yourself an exit. Build a "neutral" asset mapping as position 0 in the library, containing no bindings (or very subtle ones). If something goes wrong, press the swap key until you're back to neutral.
  • Pre-flight with F12. Before every show, press F12 and watch the channels while you do your full range of motion. If any expected channel is missing or stuck, fix it before house opens.
  • Don't tune in Play mode and forget to copy the values. Play-mode edits are lost on stop — use Copy Component → Paste Component Values, or write the numbers on a sticky note.
  • Smoothing is cheap in CPU, expensive in feel. The more you smooth, the less responsive the effect. Audiences read lag as "broken". Find the lowest smoothing that kills the jitter.
  • Test triggers with F11. ChannelSliderPanel lets you manually fire any channel while MediaPipe is off. Use it to dress-rehearse Pulse / Latch / Sequence modes without warming up.
  • Name your assets after gestures, not effects. "big-wide-stance" is more useful live than "fog-and-bass-boost" — you can remember what to do, not always what should happen.