Your first effect

Build a "Hit" effect one track at a time: a camera kick, a shake, a red flash and a sound. Each step adds one track, so you can play it after every step and see what changed.

You're at the deepest level. This page builds tracks by hand from primitives and drivers. The same effect fits in four lines with ready-made tracks, and the Effect builder shows how each one maps to what you'll write here. Going this deep is what lets you customise everything.

Setup

In a LocalScript, require MotionFX and grab the driver library. Add --!strict at the top so the editor checks your tracks and autocompletes names as you type.

--!strict
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local MotionFX = require(ReplicatedStorage.MotionFX)
local Types = require(ReplicatedStorage.MotionFX.Types)
local FX, Drive = MotionFX.Enum, MotionFX.Drive

1. A kick

An effect is a list of tracks, and a track is one primitive (what changes) driven by one driver (how it changes over time). Start with a single Rotation track: a spring that pitches the camera up 4 degrees and lets it settle back.

MotionFX:Create("Hit", {
	Tracks = {
		{ Primitive = "Rotation", Driver = Drive.Spring({ Kick = Vector3.new(4, 0, 0), Frequency = 7, Damping = 0.45 }) },
	},
})

MotionFX:Play("Hit")

You don't need to stop it. A spring ends once it has settled, and the effect ends with its longest track.

2. A shake

Add a second Rotation track. Tracks on the same primitive add up, so the shake plays on top of the kick. Noise on its own would shake forever, so Modulate multiplies it by an envelope that starts at full strength and fades out over 0.6 s.

local hitEnvelope = Drive.Envelope({ Attack = 0.03, Release = 0.6 })

-- add to Tracks
{
	Primitive = "Rotation",
	Driver = Drive.Modulate(Drive.Noise({ Frequency = 16, Amplitude = Vector3.new(1, 1, 2) }), hitEnvelope),
},
Why not just the envelope?

An envelope produces a number, and Rotation needs a Vector3. Wrapping drivers this way is what the Which drivers fit table is for.

3. A red flash

A Vignette track takes a number for a black edge, or a VignetteLayer for colour. Map reshapes the same envelope into a red layer, so the flash fades exactly as the shake does. The : Types.VignetteLayer annotation tells strict mode what the function returns.

-- add to Tracks
{
	Primitive = "Vignette",
	Driver = Drive.Map(hitEnvelope, function(amount: number): Types.VignetteLayer
		return { Intensity = amount * 0.6, Color = Color3.fromRGB(150, 0, 0) }
	end),
},

4. A sound

Sound tracks take no driver. They play their SoundId once and follow the effect's intensity. MotionFX.Sounds has the preset sounds, and you can pass any asset id.

-- add to Tracks
{ Primitive = "Sound", SoundId = MotionFX.Sounds.Shake, Volume = 0.8 },

5. Play and control it

Play returns a handle for that one playthrough. Scale it with Intensity, react when it finishes, or stop it early.

local handle = MotionFX:Play("Hit", { Intensity = 0.7 })

handle.Finished:Once(function()
	print("Hit finished")
end)

Some drivers never end on their own: Constant, Noise, Bind and Cycle, and looped sounds. An effect that uses one runs until you stop it:

local grade = MotionFX:Apply("Color", Drive.Constant({ Saturation = -0.6 }), { FadeIn = 0.3 })
-- later
grade:Stop(0.5)

The whole effect

local hitEnvelope = Drive.Envelope({ Attack = 0.03, Release = 0.6 })

MotionFX:Create("Hit", {
	Tracks = {
		{ Primitive = "Rotation", Driver = Drive.Spring({ Kick = Vector3.new(4, 0, 0), Frequency = 7, Damping = 0.45 }) },
		{
			Primitive = "Rotation",
			Driver = Drive.Modulate(Drive.Noise({ Frequency = 16, Amplitude = Vector3.new(1, 1, 2) }), hitEnvelope),
		},
		{
			Primitive = "Vignette",
			Driver = Drive.Map(hitEnvelope, function(amount: number): Types.VignetteLayer
				return { Intensity = amount * 0.6, Color = Color3.fromRGB(150, 0, 0) }
			end),
		},
		{ Primitive = "Sound", SoundId = MotionFX.Sounds.Shake, Volume = 0.8 },
	},
})

MotionFX:Play("Hit", { Intensity = 0.7 })

From here, see Retrigger policies for what happens when it's played again while running, and Examples for bigger effects.

Common mistakes

SymptomCause and fix
A track is underlined in the editorThe driver doesn't produce the primitive's value type, for example an Envelope (a number) on Rotation (a Vector3). Wrap it as shown in Which drivers fit, such as Drive.Modulate(Drive.Constant(Vector3.new(4, 0, 0)), envelope).
A Color3 is rejected on ColorColor takes a ColorValue table. Use Drive.Map(driver, function(c: Color3): Types.ColorValue return { Tint = c } end).
A Map function is underlinedStrict mode can't tell what the returned table is. Annotate the return type: function(a: number): Types.VignetteLayer or : Types.ColorValue.
The effect never endsIt contains an open-ended driver (Constant, Noise, Bind, Cycle) or a looped sound. Keep the handle and call handle:Stop(), give the effect a Duration, or shape the driver with Modulate and an envelope.
Driver must be built with MotionFX.DriveA raw value was passed as a driver. Write Drive.Constant(6) instead of 6.
an effect named "Hit" already existsCreate ran twice, often because the script runs again. Check MotionFX:Has("Hit") first, or pass { Overwrite = true }.
MotionFX is client-onlyIt was required from server code. Require it from a LocalScript or a Script with its RunContext set to Client, and fire a RemoteEvent to trigger effects from the server.
Enum.KeyCode stops workingA local named Enum hides Roblox's global. Name it FX: local FX = MotionFX.Enum.
An overlay shows as a black boxThe effect was filmed on black, and Roblox images can't blend additively. Turn on Make black transparent in the Overlay maker.
An overlay doesn't show at allIts Images are probably Decal ids. Overlays need the Image id behind each Decal; Output shows a MotionFX warning when an image can't load. See Decal ids vs Image ids.
An overlay is blank or shows the wrong framesCheck that Columns, Rows, FrameSize and Frames match how the sheet was made (the Overlay maker shows them). Sheets bigger than 1024×1024 are scaled down by Roblox, which puts FrameSize out of step. A newly uploaded image also stays blank until moderation approves it.
The game stutters while an overlay playsOverlays are heavy: each sheet costs texture memory, and each playing copy draws a full-screen image. Use fewer sheets, give overlay effects Retrigger = "Refresh" so they don't stack, and see overlay performance.
An overlay blinks the first time it playsIts sheets weren't ready to draw yet. Preload the sheet while the game loads: task.spawn(MotionFX.Overlay.Preload, sheet). It keeps the images ready until you call MotionFX.Overlay.Unload.
No suggestions inside Primitive = ""Editors usually don't suggest inside quotes automatically. Press Ctrl+Space, or in VS Code set "editor.quickSuggestions": { "strings": "on" } for Luau.