The Smart, Conflict-Free Animation Engine
Cascade UI isn't just another tween plugin. It's a full-blown layering system designed to handle complex UI states without the headache of property conflicts.
Why use this?
Standard TweenService is dumb. It fights over properties. If you try to Hover (scale up) and Click (scale down) at the same time, they break.
Cascade's AnimationService is smart. It uses an Additive Accumulator. This means you can play a "Hover", a "Click", and a "Damage Shake" all on the same button, at the same time, and they blend perfectly.
Example: A button handling a looping glow, a hover scale, and a click punch simultaneously.
-- Grab the service and start composing
local AnimationService = require(game.ReplicatedStorage.CascadeAnimations.AnimationService)
Tips & Best Practices
The engine is highly optimized, but efficient usage is key to scaling your UI to thousands of animated objects.
⚠ Critical: Memory Identity
The engine identifies animations by their Table Reference. If you create a new table every time you click a button, the engine thinks it's a brand new animation and allocates new memory. This causes leaks.
✅ Good Practice (Module Re-use)
-- Defined ONCE at the top
local HoverAnim = require(ReplicatedStorage.HoverAnim)
-- The engine sees the SAME table every click.
-- It reuses the existing Track. Zero allocation.
AnimationService.Play(btn, HoverAnim)
❌ Bad Practice (Table Creation)
-- Creates a NEW table memory address every click
AnimationService.Play(btn, {
Length = 1,
Tracks = { ... }
})
-- Result: Memory leak over time.
⚠ Naming Convention: Unique Siblings
The animator uses Name Paths (e.g., "Frame/Button/Icon") to find children. If you have multiple siblings with the same name, the animator won't know which one to animate.
Rule: Ensure every sibling inside a container has a unique name. This is required for the engine to properly parse paths and animate the object (and its descendants) correctly.
❌ Bad Structure
- 📂 Menu
- 📄 Button
- 📄 Button
✅ Good Structure
- 📂 Menu
- 📄 PlayButton
- 📄 SettingsButton
ℹ Performance: Preprocessing
Parsing string paths like "Frame/TextLabel.TextTransparency" is expensive. Don't make the engine do it every time.
If you are animating a list of 100+ items, run AnimationService.Preprocess(Config) once, and pass that processed object to PlayBatch.
API Reference
The AnimationService is your runtime engine. It's designed to be stateless and conflict-free.
.Play(Instance, Config, Options)
Starts an animation on a single instance. If an animation is already running for this config, it updates it (hijacks it) instead of restarting, allowing for smooth blending.
Example: Hover Effect with Reversal
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local AnimationService = require(ReplicatedStorage.CascadeRuntimeEngine.AnimationService)
local Hover = require(ReplicatedStorage.CascadeAnimations.Hover)
local button = script.Parent
button.MouseEnter:Connect(function()
AnimationService.Play(button, Hover)
end)
button.MouseLeave:Connect(function()
-- Reverses smoothly from current position
AnimationService.Play(button, Hover, {Reverse = true})
end)
.PlayBatch(Instances, Config, Options)
Plays the same animation on a list of objects. Highly optimized for grids and lists.
Example: Staggered Inventory Reveal
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local AnimationService = require(ReplicatedStorage.CascadeRuntimeEngine.AnimationService)
local SlideIn = require(ReplicatedStorage.CascadeAnimations.SlideIn)
local modalWinFrame = script.Parent
local container = modalWinFrame:WaitForChild("Container")
local frames = container:GetChildren()
-- Collect frames to animate
local framesToAnimate = {}
for _, child in ipairs(frames) do
if child:IsA("Frame") then
table.insert(framesToAnimate, child)
end
end
-- Stagger Option: 0.1s delay between each item
local function moveInFrames()
AnimationService.PlayBatch(framesToAnimate, SlideIn, {Stagger = 0.1})
end
-- Helper to snap them to start position (hidden) before playing
local function setToStart()
AnimationService.SetToStartBatch(framesToAnimate, SlideIn)
end
modalWinFrame:GetPropertyChangedSignal("Visible"):Connect(function()
if modalWinFrame.Visible then
setToStart()
task.wait(0.1) -- Optional: wait for layout to calculate
moveInFrames()
end
end)
.Stop(Instance)
Instantly halts all active tracks on the object. Tracks enter a dormant state.
-- Emergency stop
AnimationService.Stop(MyButton)
.SetTime(Instance, Config, Time)
Snaps the object to a specific point in the animation without playing it. Useful for initializing UI state.
-- Force button to look like it does at the end (1.0s)
AnimationService.SetTime(MyButton, MyAnim, 1.0)
PlayOptions
Control how your animation behaves. Pass this table as the 3rd argument to .Play() or .PlayBatch().
Stagger (number)
Only for PlayBatch. Delays the start of each instance's animation by X seconds relative to the previous one. Creates professional "waterfall" reveals instantly.
Reverse (boolean)
If true, plays backwards. If the animation is currently playing forward, it reverses smoothly from the current time. Ideal for mouse leave events.
Loop (boolean | number)
true = Infinite loop.
number = Loops exactly X times (e.g., Loop = 3 plays 3 times then stops).
Delay (number)
Waits X seconds before starting. Useful for sequential effects.
Reset (boolean)
When the animation finishes (or is stopped), the object reverts to the values it had before the animation started. Good for temporary effects like "Damage Flash".
ResetToStart (boolean)
When finished/stopped, snaps the object to Frame 0 of the animation.
Editor Features
A visual guide to the power tools inside Cascade UI.
Axis Isolation & Stagger
Most editors lock the whole Size property. Cascade lets you animate Size.X independently of Size.Y. Combined with the Stagger runtime feature (shown here), you can create complex, fluid reveals.
Smart Inspector
Don't fight with input fields. The inspector accepts loose syntax like {0.5, 10} for UDim and automatically formats it to Roblox standards.
Multi-Select & Drag
Box-select multiple keyframes across different tracks and drag them as a group. Retiming complex animations takes seconds, not minutes.
Relative Mode (∆)
The killer feature. Define animations as "offsets" (e.g., +10px size) rather than absolute values. This lets you layer hover effects on top of any layout without breaking it.
Changelog
Roadmap
Animation Events (In Progress)
Fire code from timeline markers. Sync sounds and logic perfectly.
Expanded Component Support
Full support for advanced UI modifiers:
UIGradientUIStrokeUICorner
Editor Polish
Improve overall layout and feel of the editor.