Examples
Recipes for common effects. The first three are also in the example script that comes with the MotionFX model.
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)