Examples

Recipes for common effects. The first three are also in the example script that comes with the MotionFX model.

Try them in Studio.

Insert the MotionFX model and press Play: the example script inside it runs straight away (see Installation). Then press 1–9 to play presets and custom effects, H for an effect built from ready-made tracks, E for Electrified, G and J for the fire and snow overlays, 0 to stop everything, and F / T / B / V for the views and smoothing.

LowHealth: bound to live data

An open-ended effect that uses Drive.Bind. The vignette follows missing health and only appears below 50%. Toggle it on once, and it keeps tracking the player's health.

MotionFX:Create("LowHealth", {
	FadeIn = 0.5,
	FadeOut = 1,
	Tracks = {
		{
			Primitive = "Vignette",
			Driver = Drive.Bind(function()
				local character = player.Character
				local humanoid = character and character:FindFirstChildOfClass("Humanoid")
				if not humanoid or humanoid.MaxHealth <= 0 then
					return 0
				end
				local missing = 1 - humanoid.Health / humanoid.MaxHealth
				return math.clamp((missing - 0.5) * 2, 0, 1)
			end),
		},
	},
})

local lowHealth = MotionFX:Play("LowHealth")
-- later, e.g. on respawn
lowHealth:Stop()

Heartbeat: a one-shot sequence

A double thump built from two springs in a Sequence, with a sound that fades out over the last 0.3 s.

MotionFX:Create("Heartbeat", {
	Retrigger = "Refresh",
	Tracks = {
		{ Primitive = "Sound", SoundId = MotionFX.Sounds.Heartbeat, Volume = 0.8, FadeOut = 0.3 },
		{
			Primitive = "FOV",
			Driver = Drive.Sequence(
				Drive.Spring({ Kick = -2.5, Frequency = 5, Damping = 0.5 }),
				Drive.Spring({ Kick = -1.5, Frequency = 5, Damping = 0.5 })
			),
		},
	},
})

Electrified: stacked vignette layers

A flickering cyan-green glow at the edge, a slower teal ring inside it, a colour grade with brightness pops, a fast jitter, and a looping crackle. Map turns the noise into VignetteLayers and a ColorValue.

local Types = require(ReplicatedStorage.MotionFX.Types)

local GLOW = Color3.fromRGB(0, 255, 190)
local DEEP = Color3.fromRGB(0, 150, 140)

MotionFX:Create("Electrified", {
	Retrigger = "Ignore",
	FadeIn = 0.4,
	FadeOut = 0.6,
	Tracks = {
		{ Primitive = "Sound", SoundId = MotionFX.Sounds.Electricity, Volume = 1.8, Looped = true, FadeIn = 0.4, FadeOut = 0.6 },
		{
			Primitive = "Vignette",
			Driver = Drive.Map(
				Drive.Sum(Drive.Constant(0.6), Drive.Noise({ Frequency = 9, Amplitude = 0.25, Octaves = 2 })),
				function(amount: number): Types.VignetteLayer
					return { Intensity = amount, Color = GLOW, Inset = 0, Size = 0.45 }
				end
			),
		},
		{
			Primitive = "Vignette",
			Driver = Drive.Map(
				Drive.Sum(Drive.Constant(0.35), Drive.Noise({ Frequency = 2.5, Amplitude = 0.1 })),
				function(amount: number): Types.VignetteLayer
					return { Intensity = amount, Color = DEEP, Inset = 0.12, Size = 0.7 }
				end
			),
		},
		{
			Primitive = "Color",
			Driver = Drive.Map(Drive.Noise({ Frequency = 14, Amplitude = 0.08 }), function(flicker: number): Types.ColorValue
				return { Tint = Color3.fromRGB(215, 255, 240), Saturation = 0.1, Contrast = 0.08, Brightness = math.max(flicker, 0) }
			end),
		},
		{ Primitive = "Rotation", Driver = Drive.Noise({ Frequency = 20, Amplitude = Vector3.new(0.12, 0.12, 0.25) }) },
	},
})

The example script also draws yellow arcs along the border in its own GUI (the ElectricArcs module inside it) and fades them with the effect by reading handle.State and handle.Intensity every frame. You can use the same approach for any custom visuals that should follow an effect.

Fire and snow: overlays with layered edges

Both overlay presets, dressed up so they feel hot or cold. The snow layers two Edge tracks with different Insets, a frosty white at the edge with a blue ring inside it, then adds a cold grade, a shiver and the wind. Every track holds until the effect is toggled off, and then everything, sound included, fades out together.

MotionFX:Create("SnowOnScreen", {
	Retrigger = "Ignore",
	FadeIn = 0.4,
	FadeOut = 0.8,
	Tracks = {
		Track.Overlay(MotionFX.Overlays.SnowOnScreen),
		Track.Sound(MotionFX.Sounds.Snow, { Looped = true, Volume = 0.8, FadeIn = 0.5, FadeOut = 0.8 }),
		Track.Edge({ Color = Color3.fromRGB(235, 245, 255), Strength = 0.6, Size = 0.6, Hold = math.huge }),
		Track.Edge({ Color = Color3.fromRGB(110, 170, 230), Strength = 0.45, Inset = 0.15, Size = 0.8, Hold = math.huge }),
		Track.Tint({ Color = Color3.fromRGB(205, 228, 255), Saturation = -0.35, Contrast = 0.05, Brightness = 0.04, Time = 0.8 }),
		Track.Shake({ Strength = 0.12, Frequency = 22, Hold = math.huge }),
	},
})

local snow = MotionFX:Play("SnowOnScreen")
-- later
snow:Stop()

The fire does the same with warm colours. Its outer glow flickers like flames, which no ready-made track does, so it's built from a driver: a steady 0.5 with fast noise on top, mapped into an orange vignette layer.

{
	Primitive = "Vignette",
	Driver = Drive.Map(
		Drive.Sum(Drive.Constant(0.5), Drive.Noise({ Frequency = 6, Amplitude = 0.18, Octaves = 2 })),
		function(amount: number): Types.VignetteLayer
			return { Intensity = amount, Color = Color3.fromRGB(255, 120, 30), Size = 0.6 }
		end
	),
},

Both are in the example script, on G (fire) and J (snow).

Replicated shake with a shared seed

MotionFX runs on the client. To give every player the same shake, have the server pick a seed and pass it through a RemoteEvent:

local seed = math.random(1, 1e6)
ShakeEvent:FireAllClients(seed, 0.8)
ShakeEvent.OnClientEvent:Connect(function(seed: number, intensity: number)
	MotionFX:Play(FX.Impact.Earthquake, { Seed = seed, Intensity = intensity })
end)

Directional hit with Build

Build runs on every play with Params and returns additional tracks, so one definition can react to where a hit came from:

MotionFX:Create("Hit", {
	Retrigger = "Stack",
	MaxInstances = 4,
	Tracks = {
		{ Primitive = "Vignette", Driver = Drive.Envelope({ Attack = 0.03, Release = 0.6 }, 0.5) },
	},
	Build = function(params)
		local tracks = {}
		local from: Vector3? = params.From -- camera-space direction the hit came from
		if from then
			table.insert(tracks, {
				Primitive = "Rotation",
				Driver = Drive.Spring({ Kick = Vector3.new(-from.Y, from.X, -from.X) * 4, Frequency = 6, Damping = 0.4 }),
			})
		end
		return tracks
	end,
})

local camera = workspace.CurrentCamera
local localDirection = camera.CFrame:VectorToObjectSpace((attackerPosition - camera.CFrame.Position).Unit)
MotionFX:Play("Hit", { Params = { From = localDirection } })

Speed-based FOV with Apply

For a single primitive, you don't need a named effect:

local root = character:WaitForChild("HumanoidRootPart") :: BasePart

local speedFov = MotionFX:Apply("FOV", Drive.Bind(function()
	local speed = root.AssemblyLinearVelocity.Magnitude
	return math.clamp((speed - 16) * 0.4, 0, 15)
end), { FadeIn = 0.3 })

humanoid.Died:Once(function()
	speedFov:Stop(0.5)
end)

Aim-down-sights zoom

local aim

UserInputService.InputBegan:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton2 then
		aim = MotionFX:Apply("FOV", Drive.Sequence(Drive.Tween(0, -20, 0.18), Drive.Constant(-20)))
	end
end)

UserInputService.InputEnded:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton2 and aim then
		aim:Stop(0.15)
		aim = nil
	end
end)