Primitives & Tracks

An effect can change eight things. Each one has its own value type and its own rule for combining contributions from effects that run at the same time.

You're at the deepest level. Every primitive and every driver is open here, and nothing is hidden underneath: the Effect builder and the ready-made tracks are made from these same pieces. You can still drop ready-made tracks into any effect where they fit.

Tracks

A track is a flat table tagged by Primitive. Every track accepts these two fields:

FieldTypeDescription
Weightnumber?A per-track scale applied on top of the effect's intensity. Default 1.
Delaynumber?Seconds after Play before the track starts. Must be 0 or more.

Every primitive except Sound also needs a Driver that produces its value type:

PrimitiveDriver valueMeaningCombine
FOVnumberDegrees added to the base FOVsum
PositionVector3Camera-space offset in studssum
RotationVector3Degrees of (pitch, yaw, roll)sum
BlurnumberBlurEffect.Sizemax
ColorColorValueColour correctionmultiply / sum
Vignettenumber | VignetteLayerEdge darkening or a coloured ringlayered
Sound—A one-shot or looping soundindependent
OverlaynumberOpacity of a flipbook over the screenindependent

Which drivers fit

Drivers don't belong to a primitive. A driver fits a primitive when it produces that primitive's value type: FOV, Blur and Overlay (its opacity) take a number, Position and Rotation a Vector3, Color a ColorValue, and Vignette a number or a VignetteLayer. With --!strict, a track whose driver doesn't fit is underlined in your editor.

DriverFOV, Blur, OverlayPosition, RotationColorVignette
Constant✓✓✓✓
Tween, Cycle✓✓✓ ColorValues; a Color3 via Map✓
Spring✓ number Kick✓ Vector3 Kick✓ rarely useful✓
Noise✓ number Amplitude✓ Vector3 Amplitudevia Map✓; a layer via Map
Envelope✓via Modulatevia Modulate✓; a layer via Map
Bind✓ return the primitive's value type
Modulate✓ when its first driver (the carrier) fits
Sum, Sequence✓ when every driver inside fits
Map✓ return the primitive's value type. It turns any driver into any other value.

✓ fits directly. via … means wrap it first, for example Drive.Modulate(Drive.Constant(Vector3.new(0, 0, 4)), envelope) to give a Vector3 the envelope's shape. Sound takes no driver.

FOV

A delta in degrees, added to the camera's base FieldOfView. Positive values widen the view (a punch outward) and negative values narrow it (zoom in). Contributions add up. If other code changes the FOV, CameraRig adopts the new value as the base instead of fighting it.

{ Primitive = "FOV", Driver = Drive.Spring({ Kick = 7, Frequency = 3.5, Damping = 0.4 }) }

Drivers that fit

ForDriver
A punch out or inDrive.Spring({ Kick = 7, Frequency = 3.5, Damping = 0.4 })
Zoom in and holdDrive.Sequence(Drive.Tween(0, -10, 0.25), Drive.Constant(-10))
Follow speed or another valueDrive.Bind(function() return speedFov end)
A slow wobbleDrive.Noise({ Frequency = 0.5, Amplitude = 2 })

Position

A camera-space offset in studs: X is right, Y is up and Z is back. Contributions add up.

-- Kick the camera back 0.18 studs
{ Primitive = "Position", Driver = Drive.Spring({ Kick = Vector3.new(0, 0, 0.18), Frequency = 9, Damping = 0.45 }) }

Drivers that fit

ForDriver
A kick that settlesDrive.Spring({ Kick = Vector3.new(0, 0, 0.18), Frequency = 9, Damping = 0.45 })
A rumble that dies outDrive.Modulate(Drive.Noise({ Frequency = 12, Amplitude = Vector3.new(0.1, 0.1, 0) }), envelope)
A fixed offsetDrive.Constant(Vector3.new(0, -0.5, 0))

Rotation

An Euler offset in degrees as (pitch, yaw, roll). Contributions add up. This is the main ingredient of most shakes.

{ Primitive = "Rotation", Driver = Drive.Modulate(Drive.Noise({ Frequency = 14, Amplitude = Vector3.new(3, 3, 6) }), envelope) }

Drivers that fit

ForDriver
A shakeDrive.Modulate(Drive.Noise({ Frequency = 14, Amplitude = Vector3.new(3, 3, 6) }), envelope)
RecoilDrive.Spring({ Kick = Vector3.new(3.5, 0, 0), Frequency = 9, Damping = 0.4 })
Tilt and holdDrive.Sequence(Drive.Tween(Vector3.zero, Vector3.new(0, 0, 8), 0.3), Drive.Constant(Vector3.new(0, 0, 8)))
A constant jitterDrive.Noise({ Frequency = 20, Amplitude = Vector3.new(0.12, 0.12, 0.25) })

Blur

A BlurEffect.Size. The strongest contribution wins instead of summing, so two blurs don't stack into a white-out. MotionFX creates its own MotionFX_Blur in Lighting when it is first needed and destroys it when idle.

Drivers that fit

ForDriver
A flash that decaysDrive.Envelope({ Release = 0.9 }, 14)
A steady blurDrive.Constant(6)
A blur that wobblesDrive.Sum(Drive.Constant(8), Drive.Noise({ Frequency = 0.6, Amplitude = 4 }))

Color

A ColorValue table, applied through MotionFX's own MotionFX_Color ColorCorrectionEffect, so it never fights your Lighting setup.

FieldTypeCombineApplied
TintColor3?multiplied togetherTintColor, white if unset
Saturationnumber?summedclamped to -1..1
Contrastnumber?summedclamped to -1..1
Brightnessnumber?summedclamped to -1..1
-- Flash: brighter and desaturated, fading over 0.7 s
{ Primitive = "Color", Driver = Drive.Modulate(Drive.Constant({ Brightness = 0.25, Saturation = -0.35 }), Drive.Envelope({ Release = 0.7 })) }

Drivers that fit

ForDriver
A flash that decaysDrive.Modulate(Drive.Constant({ Brightness = 0.25 }), envelope)
Fade into a gradeDrive.Tween({ Saturation = 0 }, { Saturation = -0.8 }, 1)
A flickerDrive.Map(Drive.Noise({ Frequency = 14, Amplitude = 0.08 }), function(n: number): Types.ColorValue return { Brightness = n } end)
A colour walkDrive.Map(Drive.Cycle({ Color3.new(1, 0.6, 1), Color3.new(0.6, 0.8, 1) }, 1.6), function(c: Color3): Types.ColorValue return { Tint = c } end)

Vignette

A plain number from 0 to 1 is a black vignette at the screen edge. A VignetteLayer gives you control over colour, placement and images:

FieldTypeDescription
IntensitynumberOpacity at the layer's peak, 0..1.
ColorColor3?Black by default, or white for an Image layer so the image keeps its colours.
Insetnumber?Where the layer peaks, as a fraction of the vignette's depth, 0..1. 0 is the screen edge.
Sizenumber?How far past the peak the layer fades out toward the centre, in the same units. Default 1.
Imagestring?An image asset stretched over the screen instead of the gradient edges. Color tints it and Size is ignored.
-- Outer dark edge, plus a deep red ring inside it
{ Primitive = "Vignette", Driver = Drive.Modulate(Drive.Constant(0.85), flash) },
{
	Primitive = "Vignette",
	Driver = Drive.Map(flash, function(amount: number): Types.VignetteLayer
		return { Intensity = amount * 0.7, Color = Color3.fromRGB(120, 0, 0), Inset = 0.3, Size = 0.9 }
	end),
},

Drivers that fit

ForDriver
A black pulseDrive.Envelope({ Attack = 0.03, Release = 0.6 }, 0.5)
Follow healthDrive.Bind(function() return missingHealth end)
A coloured ring that decaysDrive.Map(envelope, function(a: number): Types.VignetteLayer return { Intensity = a, Color = RED, Inset = 0.3 } end)
A breathing layerDrive.Map(Drive.Sum(Drive.Constant(0.35), Drive.Noise({ Frequency = 2.5, Amplitude = 0.1 })), toLayer)

Sound

Sound is event-based rather than continuous, so it has no driver. Each Sound track spawns its own Sound in SoundService once its Delay has passed. Its volume follows Volume × intensity × Weight × fade.

FieldTypeDescription
SoundIdstringRequired. For example MotionFX.Sounds.Heartbeat.
Volumenumber?Base volume. Default 1.
PlaybackSpeednumber?Playback rate.
TimePositionnumber?Seconds into the audio to start from, to skip its intro.
Loopedboolean?A looped sound makes the effect open-ended.
FadeInnumber?Seconds to ramp up from silence once the sound starts.
FadeOutnumber?Fades the sound over the end of a fixed-duration effect so it stops with the effect, and when the effect is stopped early.
SoundGroupSoundGroup?Routes the sound through your mixer.
One-shots play out.

If a fixed-duration effect ends naturally before a non-looped sound without FadeOut has finished, the sound keeps playing to its natural end. Stopping the effect with Stop() fades it out instead.

Overlay

A flipbook of images drawn over the screen, from sprite sheets or a folder of numbered images. Its driver is the opacity, 0 to 1, so any number driver works: an envelope to fade in and out, noise to flicker, a binding to follow game state. Like Sound, overlays aren't combined; each track shows its own image, above the vignette. See Overlays for sheets, Track.Overlay and the Overlay maker.

{ Primitive = "Overlay", Sheet = splatterSheet, Fps = 30, Driver = Drive.Envelope({ Attack = 0.05, Release = 0.6 }) }