Concepts

MotionFX has a handful of moving parts. Once you know them, every preset reads like a recipe.

Mental model

Effect=named list of Tracks+timing / stacking rules
Track=one Primitive+one Driver
Driver=produces a value over time
Handle=what Play() returns: fade, retarget, stop

Frame pipeline

The runtime binds two RenderStep callbacks around Roblox's own camera script:

PriorityStepWhat happens
Camera - 1CameraRig.Strip()Restores the CFrame the camera script produced last frame. Scripts that read camera.CFrame back never see or compound the shake. Also adopts FOV changes made by other code.
CameraRoblox camera scriptRuns normally. Mouse, gamepad and touch input all still drive it.
Camera + 1step(dt)Samples every driver, then channels combine and apply. The view picks the base placement and this frame's offset goes on top.

The loop is bound only while an effect is running, or while a view or smoothing is active. When everything finishes, channels release their instances (BlurEffect, ColorCorrectionEffect, vignette GUI), the camera is reset, and the loop unbinds.

How channels combine

PrimitiveCombine ruleApplied to
FOVSum of degree deltasAdded to the base FieldOfView
PositionSum of Vector3Camera-space offset (studs)
RotationSum of Vector3Euler offset in degrees (pitch, yaw, roll)
BlurMax, so the strongest winsIts own BlurEffect in Lighting
ColorTints multiply. Saturation, Contrast and Brightness add.Its own ColorCorrectionEffect
VignetteGrouped into layers by Inset and Image. The strongest wins within a layer, and layers stack.A ScreenGui in PlayerGui
SoundNot combined. Each track spawns its own Sound.SoundService
OverlayNot combined. Each track shows its own flipbook image.A ScreenGui in PlayerGui

Intensity and fades

Each frame, every non-sound track's value is scaled toward neutral before it reaches the channel:

contribution = Scale(value, handle.Intensity * fade * stopMultiplier * track.Weight)
Sounds fade on their own.

The effect-level FadeIn/FadeOut don't apply to sound tracks. A sound uses its own FadeIn and FadeOut, scaled by intensity and the stop fade.

Retrigger policies

EffectDefinition.Retrigger decides what happens when you play an effect while an instance of it is already running:

PolicyBehaviourReturns
"Stack" defaultRuns both, and their contributions add up. When there are already MaxInstances (default 8), the oldest fade out over 0.1 s.New handle
"Refresh"Restarts the existing instance in place with the new options. Its sounds are cut with a 0.05 s fade and restart.The existing handle
"Replace"Fades out the old instances with their default fade, then starts a new one.New handle
"Ignore"Does nothing.The running handle

Duration inference

An effect's length is resolved in this order:

  1. PlayOptions.Duration
  2. EffectDefinition.Duration
  3. Inferred: the longest driver.Duration + track.Delay.

If any driver is open-ended (Constant, Noise, Bind, Cycle, or a combination that contains one), or a sound is Looped, the inferred duration is nil. The effect then runs until you stop it with handle:Stop(), MotionFX:Stop(name) or MotionFX:StopAll().

Finite effects end cleanly.

When a driver finishes, it holds its neutral or end value. Effects fade because their values return to zero, so drivers need no special release logic.

Validation

MotionFX:Create validates definitions up front, so mistakes fail early with a readable message instead of surfacing mid-frame. Examples are a misspelled primitive, a raw number where a driver belongs, a negative Delay, or an unknown Retrigger. Tracks returned by Build are validated each time Build runs.

MotionFX "Heartbeat", track #2: Driver must be built with MotionFX.Drive (got number)