### Start Motor Source: https://github.com/roblox/otter/blob/main/docs/api-reference.md Starts the motor, allowing it to move. This is typically called automatically when a goal is set. ```lua Motor<_, _>:start(): () ``` -------------------------------- ### Implement mouse-following animation Source: https://github.com/roblox/otter/blob/main/docs/usage/motors.md A complete example demonstrating a GUI frame following the mouse position using a group motor. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Players = game:GetService("Players") local Otter = require(ReplicatedStorage.Otter) local localPlayer = Players.LocalPlayer local mouse = localPlayer:GetMouse() local container = Instance.new("ScreenGui") container.ResetOnSpawn = false container.Parent = localPlayer:WaitForChild("PlayerGui") local frame = Instance.new("Frame") frame.AnchorPoint = Vector2.new(1, 1) frame.Size = UDim2.new(0, 20, 0, 20) frame.BorderSizePixel = 0 frame.BackgroundColor3 = Color3.fromRGB(255, 255, 255) frame.Parent = container local positionMotor = Otter.createGroupMotor({ x = 0, y = 0, }) positionMotor:onStep(function(values) frame.Position = UDim2.new(0, values.x, 0, values.y) end) local function updateMotor() positionMotor:setGoal({ x = Otter.spring(mouse.X), y = Otter.spring(mouse.Y), }) end updateMotor() mouse.Move:Connect(updateMotor) ``` -------------------------------- ### useAnimatedBinding - Multiple Values Example Source: https://github.com/roblox/otter/blob/main/docs/usage/react.md Demonstrates animating multiple values simultaneously with different spring configurations using useAnimatedBinding. ```APIDOC ## useAnimatedBinding with Multiple Values ### Description This example shows how to animate multiple properties of a UI element concurrently, applying distinct animation configurations to each. ### Method Not applicable (Hook usage) ### Endpoint Not applicable (Hook usage) ### Parameters Not applicable (Hook usage) ### Request Example ```lua local TRANSPARENCY_CONFIG = { dampingRatio = 1, frequency = 3, } local function FadeAndGrowIn(props) local visible, setVisible = React.useState(false) local animationState, setGoal = ReactOtter.useAnimatedBinding({ transparency = 1, scale = 0.8, }) React.useEffect(function() setGoal({ transparency = ReactOtter.spring( if visible then 0 else 1, TRANSPARENCY_CONFIG ), -- default spring config scale = ReactOtter.spring(if visible then 1 else 0.8), }) end, { visible }) return React.createElement( "Frame", { Size = props.size }, React.createElement("TextButton", { Text = if visible then "Hide" else "Show", Size = UDim2.new(0, 200, 0, 50), [React.Event.Activated] = function() setVisible(not visible) end, }), React.createElement("TextLabel", { Text = "Content", BackgroundTransparency = animationState:map(function(state) return state.transparency end), Size = animationState:map(function(state) return UDim2.new(1 * state.scale, 0, 0, 200 * state.scale) end), AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.new(0.5, 0, 0.5, 25), }) ) end ``` ``` -------------------------------- ### Control Motor Lifecycle with start, stop, destroy Source: https://context7.com/roblox/otter/llms.txt Motor:start resumes a paused motor, Motor:stop freezes the motor's current state while retaining velocity and goal information, and Motor:destroy cleans up all associated connections, rendering the motor unusable. These methods manage the motor's active state and resource cleanup. ```lua local Otter = require(ReplicatedStorage.Otter) local motor = Otter.createSingleMotor(0) motor:onStep(function(value) myFrame.Size = UDim2.new(0, value, 0, value) end) motor:setGoal(Otter.spring(100)) -- Pause animation mid-flight (preserves velocity) task.wait(0.1) motor:stop() -- Resume from paused state task.wait(1) motor:start() -- When completely done with the motor motor:destroy() -- motor is now unusable - create a new one if needed ``` -------------------------------- ### Create Instant Goal with Otter.instant Source: https://context7.com/roblox/otter/llms.txt Use Otter.instant to create a goal that immediately sets a motor to a target value without animation. This is useful for resetting states or jumping to a new position before starting a new animation. It can be mixed with other goal types in group motors. ```lua local Otter = require(ReplicatedStorage.Otter) local motor = Otter.createSingleMotor(100) motor:onStep(function(value) myFrame.Position = UDim2.new(0, value, 0, 0) end) -- Instantly jump to position 0 (no animation) motor:setGoal(Otter.instant(0)) -- Common pattern: reset then animate motor:setGoal(Otter.instant(0)) task.wait() -- Let the instant goal complete motor:setGoal(Otter.spring(300)) -- Now animate to 300 -- With group motors, mix instant and animated goals local groupMotor = Otter.createGroupMotor({ x = 0, y = 0 }) groupMotor:setGoal({ x = Otter.instant(100), -- Jump x immediately y = Otter.spring(200), -- Animate y smoothly }) ``` -------------------------------- ### Install ReactOtter Dependency Source: https://github.com/roblox/otter/blob/main/docs/usage/react.md Add ReactOtter to your project dependencies. Include the base Otter package if you plan to use Otter directly. ```toml [dependencies] ReactOtter = "github.com/Roblox/otter@1.0" # You'll only need this if you use Otter directly Otter = "github.com/Roblox/otter@1.0" ``` -------------------------------- ### Motor Lifecycle Methods Source: https://context7.com/roblox/otter/llms.txt Methods to control the lifecycle of a motor: start, stop, and destroy. ```APIDOC ## Motor:start Resumes a paused motor. ## Motor:stop Freezes the motor while preserving velocity and goal. ## Motor:destroy Cleans up all connections and renders the motor unusable. ``` -------------------------------- ### Motor:setGoal Source: https://context7.com/roblox/otter/llms.txt Sets the target goal for a motor and starts the animation if not already running. ```APIDOC ## Motor:setGoal ### Description Sets the target goal for a motor and starts the animation if not already running. For single motors, pass a single goal. For group motors, pass a table mapping value names to their goals. ### Parameters - **goal** (number | table) - Required - The goal object (e.g., Otter.spring, Otter.ease, Otter.instant) or a table of goals for group motors. ``` -------------------------------- ### Stop Motor Source: https://github.com/roblox/otter/blob/main/docs/api-reference.md Stops the motor, freezing its current state until `start` or `setGoal` is called. ```lua Motor<_, _>:stop(): () ``` -------------------------------- ### useAnimatedBinding with onComplete Callback Source: https://context7.com/roblox/otter/llms.txt Utilizes the optional onComplete callback in useAnimatedBinding to trigger side effects, such as removing a component from the DOM after an animation finishes. This example shows a toast notification that fades out and is removed after a delay. ```lua local React = require(Packages.React) local ReactOtter = require(Packages.ReactOtter) local function ToastNotification(props) local visible, setVisible = React.useState(true) local removed, setRemoved = React.useState(false) -- onComplete callback fires when animation reaches goal local opacity, setGoal = ReactOtter.useAnimatedBinding(1, function(value) if value == 0 then setRemoved(true) -- Remove from DOM after fade out end end) React.useEffect(function() -- Auto-dismiss after 3 seconds local timer = task.delay(3, function() setGoal(ReactOtter.spring(0)) -- Fade out end) return function() task.cancel(timer) end end, {}) if removed then return nil end return React.createElement("Frame", { BackgroundTransparency = opacity:map(function(o) return 1 - o end), Size = UDim2.new(0, 300, 0, 60), Position = UDim2.new(0.5, 0, 0, 20), AnchorPoint = Vector2.new(0.5, 0), }, { Message = React.createElement("TextLabel", { Text = props.message, Size = UDim2.new(1, 0, 1, 0), }), }) end ``` -------------------------------- ### Implement mouse following with Otter group motors Source: https://context7.com/roblox/otter/llms.txt Demonstrates using a group motor to track mouse coordinates with spring physics. The motor updates the UI frame position on every step. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Players = game:GetService("Players") local Otter = require(ReplicatedStorage.Otter) local localPlayer = Players.LocalPlayer local mouse = localPlayer:GetMouse() -- Create UI local screenGui = Instance.new("ScreenGui") screenGui.ResetOnSpawn = false screenGui.Parent = localPlayer:WaitForChild("PlayerGui") local follower = Instance.new("Frame") follower.AnchorPoint = Vector2.new(0.5, 0.5) follower.Size = UDim2.new(0, 30, 0, 30) follower.BorderSizePixel = 0 follower.BackgroundColor3 = Color3.fromRGB(100, 200, 255) follower.Parent = screenGui local corner = Instance.new("UICorner") corner.CornerRadius = UDim.new(1, 0) corner.Parent = follower -- Create group motor for position local positionMotor = Otter.createGroupMotor({ x = mouse.X, y = mouse.Y, }) -- Update frame position on each step positionMotor:onStep(function(values) follower.Position = UDim2.new(0, values.x, 0, values.y) end) -- Follow mouse with bouncy spring local function updatePosition() positionMotor:setGoal({ x = Otter.spring(mouse.X, { frequency = 3, dampingRatio = 0.6 }), y = Otter.spring(mouse.Y, { frequency = 3, dampingRatio = 0.6 }), }) end updatePosition() mouse.Move:Connect(updatePosition) ``` -------------------------------- ### Create a single motor Source: https://github.com/roblox/otter/blob/main/modules/otter/README.md Initializes a motor to track a single value and sets a spring goal. ```lua local object = Instance.new("Frame") object.Size = UDim2.new(0, 50, 0, 50) -- Our initial value is 0 local motor = Otter.createSingleMotor(0) -- ...but we're moving to 50! motor:setGoal(Otter.spring(50)) motor:onStep(function(value) object.Position = UDim2.new(0, value, 0, 0) end) -- Once started, our motor will run every frame until it reaches its goal. motor:start() ``` -------------------------------- ### Otter.instant Source: https://context7.com/roblox/otter/llms.txt Creates a goal that immediately sets the motor to the target value without any animation. ```APIDOC ## Otter.instant ### Description Creates a goal that immediately sets the motor to the target value without any animation. Useful for resetting values or jumping to a new state before starting a new animation. ### Parameters - **target** (number) - Required - The value to jump to immediately. ``` -------------------------------- ### Create a group motor Source: https://github.com/roblox/otter/blob/main/modules/otter/README.md Initializes a motor to track multiple values simultaneously with different goal types. ```lua local object = Instance.new("Frame") object.Size = UDim2.new(0, 50, 0, 50) -- Our initial value is { x = 0, y = 0 }. local multimotor = Otter.createGroupMotor({ x = 0, y = 0, }) -- We're moving to { x = 50, y = 50 } with a spring on the X axis. multimotor:setGoal({ x = Otter.spring(50), y = Otter.instant(50), }) multimotor:onStep(function(values) object.Position = UDim2.new(0, values.x, 0, values.y) end) -- Start your engine! multimotor:start() ``` -------------------------------- ### Create Otter motors Source: https://github.com/roblox/otter/blob/main/docs/api-reference.md Constructors for single and group motors. ```lua Otter.createSingleMotor(initialValue: number): Otter.SingleMotor ``` ```lua Otter.createGroupMotor(initialValues: { string: number }): Otter.GroupMotor ``` -------------------------------- ### Configure spring animations with Otter.spring Source: https://context7.com/roblox/otter/llms.txt Defines physics-based goals using frequency/damping or Figma-style parameters for natural movement. ```lua local Otter = require(ReplicatedStorage.Otter) local motor = Otter.createSingleMotor(0) motor:onStep(function(value) myFrame.Size = UDim2.new(0, value, 0, value) end) -- Default spring (critically damped, frequency=1) motor:setGoal(Otter.spring(100)) -- Custom spring with frequency and damping ratio motor:setGoal(Otter.spring(100, { frequency = 2, -- Faster response (cycles per second) dampingRatio = 0.5, -- Underdamped (bouncy) })) -- Figma-style spring parameters motor:setGoal(Otter.spring(100, { stiffness = 400, -- Higher = more bounces damping = 20, -- Higher = less springy mass = 1, -- Higher = slower, higher bounce })) -- Fine-tune resting detection thresholds motor:setGoal(Otter.spring(100, { frequency = 1, dampingRatio = 1, restingVelocityLimit = 0.001, -- Default: 0.001 restingPositionLimit = 0.01, -- Default: 0.01 })) ``` -------------------------------- ### useAnimatedBinding: Single and Group Motor Bindings Source: https://context7.com/roblox/otter/llms.txt Demonstrates using useAnimatedBinding for both single-value animations (like button scaling) and multi-property animations (like frame opacity and position). Automatically selects the appropriate Otter motor based on the initial value type. ```lua local React = require(Packages.React) local ReactOtter = require(Packages.ReactOtter) local function AnimatedButton() local hovered, setHovered = React.useState(false) -- Single value binding (uses SingleMotor internally) local scale, setGoal = ReactOtter.useAnimatedBinding(1) React.useEffect(function() setGoal(ReactOtter.spring(if hovered then 1.1 else 1)) end, { hovered }) return React.createElement("TextButton", { Text = "Hover Me", Size = scale:map(function(s) return UDim2.new(0, 100 * s, 0, 50 * s) end), AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.new(0.5, 0, 0.5, 0), [React.Event.MouseEnter] = function() setHovered(true) end, [React.Event.MouseLeave] = function() setHovered(false) end, }) end -- Group value binding (uses GroupMotor internally) local function MultiPropertyAnimation() local active, setActive = React.useState(false) local state, setGoal = ReactOtter.useAnimatedBinding({ opacity = 0, offsetY = 20, }) React.useEffect(function() setGoal({ opacity = ReactOtter.spring(if active then 1 else 0), offsetY = ReactOtter.spring(if active then 0 else 20), }) end, { active }) return React.createElement("Frame", { BackgroundTransparency = state:map(function(s) return 1 - s.opacity end), Position = state:map(function(s) return UDim2.new(0.5, 0, 0.5, s.offsetY) end), }) end ``` -------------------------------- ### Configure easing animations with Otter.ease Source: https://context7.com/roblox/otter/llms.txt Creates time-based transitions using Roblox EasingStyle enums or custom cubic bezier curves. ```lua local Otter = require(ReplicatedStorage.Otter) local motor = Otter.createSingleMotor(0) motor:onStep(function(value) myFrame.BackgroundTransparency = value end) -- Default: 1 second linear ease motor:setGoal(Otter.ease(1)) -- Custom duration with easing style motor:setGoal(Otter.ease(1, { duration = 0.5, -- Half second animation easingStyle = Enum.EasingStyle.Quad, -- Quadratic ease-in })) -- Available easing styles: Linear, Quad, Cubic, Quart, Quint, -- Exponential, Sine, Back, Bounce, Elastic, Circular -- Custom cubic bezier curve (x1, y1, x2, y2) motor:setGoal(Otter.ease(1, { duration = 0.3, easingStyle = { 0.25, 0.1, 0.25, 1 }, -- CSS ease equivalent })) ``` -------------------------------- ### Create single and group motors Source: https://github.com/roblox/otter/blob/main/docs/usage/motors.md Initialize motors with initial values. Group motors require all values to be defined at creation. ```lua local singleMotor = Otter.createSingleMotor(0) local groupMotor = Otter.createGroupMotor({ x = 0, y = 0, }) ``` -------------------------------- ### Animate Transparency and Scale Simultaneously Source: https://github.com/roblox/otter/blob/main/docs/usage/react.md Use this pattern to animate multiple properties with different spring configurations. Ensure React and ReactOtter are imported. ```lua local TRANSPARENCY_CONFIG = { dampingRatio = 1, frequency = 3, } local function FadeAndGrowIn(props) local visible, setVisible = React.useState(false) local animationState, setGoal = ReactOtter.useAnimatedBinding({ transparency = 1, scale = 0.8, }) React.useEffect(function() setGoal({ transparency = ReactOtter.spring( if visible then 0 else 1, TRANSPARENCY_CONFIG ), -- default spring config scale = ReactOtter.spring(if visible then 1 else 0.8), }) end, { visible }) return React.createElement( "Frame", { Size = props.size }, React.createElement("TextButton", { Text = if visible then "Hide" else "Show", Size = UDim2.new(0, 200, 0, 50), [React.Event.Activated] = function() setVisible(not visible) end, }), React.createElement("TextLabel", { Text = "Content", BackgroundTransparency = animationState:map(function(state) return state.transparency end), Size = animationState:map(function(state) return UDim2.new(1 * state.scale, 0, 0, 200 * state.scale) end), AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.new(0.5, 0, 0.5, 25), }) ) end ``` -------------------------------- ### Define Otter instant goal Source: https://github.com/roblox/otter/blob/main/docs/api-reference.md Constructs a goal that reaches the target value immediately. ```lua Otter.instant(targetValue: number): Otter.Goal ``` -------------------------------- ### Animated Frame Expansion with Mapped Values Source: https://github.com/roblox/otter/blob/main/docs/usage/react.md Animate the height of a frame using `useAnimatedBinding` and map the animation progress (0 to 1) to a UDim2 size. This allows animating between different size configurations. ```lua local function ExpandableFrame(props: Props) local expanded, setExpanded = React.useState(false) local height, setGoal = useAnimatedBinding(0) React.useEffect(function() setGoal(if expanded then 1 else 0) end, { expanded }) return React.createElement( "Frame", { Size = props.size }, React.createElement("TextButton", { Text = if expanded then "Collapse" else "Expand" Size = UDim2.new(1, 0, 0, 40), }), React.createElement("Frame", { Position = UDim2.new(0, 0, 0, 40), -- Derive the UDim2 size value from the animation progress value Size = height:map(function(value) return UDim2.new(1, 0, value, -40 * value), end), }, props.content) ) end ``` -------------------------------- ### Control motor lifecycle Source: https://github.com/roblox/otter/blob/main/modules/otter/README.md Methods for stopping, manually stepping, and restarting a motor. ```lua motor:stop() motor:step(0.5) motor:start() ``` -------------------------------- ### Create a single value motor with Otter.createSingleMotor Source: https://context7.com/roblox/otter/llms.txt Initializes a motor for a single numeric value and handles frame updates and completion events. ```lua local Otter = require(ReplicatedStorage.Otter) -- Create a motor starting at value 0 local transparencyMotor = Otter.createSingleMotor(0) -- Subscribe to value updates on each frame local disconnect = transparencyMotor:onStep(function(value) myFrame.BackgroundTransparency = value end) -- Subscribe to completion events transparencyMotor:onComplete(function(finalValue) print("Animation complete at value:", finalValue) end) -- Start animating toward value 1 using spring physics transparencyMotor:setGoal(Otter.spring(1)) -- Later: clean up the motor when done transparencyMotor:destroy() ``` -------------------------------- ### Set Goals with Motor:setGoal Source: https://context7.com/roblox/otter/llms.txt The Motor:setGoal method sets the target goal for a motor and initiates animation. For single motors, provide a single goal value. For group motors, supply a table mapping value names to their respective goals. This allows for setting all goals at once or updating individual goals while others continue their current motion. ```lua local Otter = require(ReplicatedStorage.Otter) -- Single motor goal setting local singleMotor = Otter.createSingleMotor(0) singleMotor:onStep(function(value) print(value) end) singleMotor:setGoal(Otter.spring(100)) -- Group motor goal setting - can set any subset of values local groupMotor = Otter.createGroupMotor({ width = 100, height = 100, transparency = 0, }) groupMotor:onStep(function(values) myFrame.Size = UDim2.new(0, values.width, 0, values.height) myFrame.BackgroundTransparency = values.transparency end) -- Set all goals at once groupMotor:setGoal({ width = Otter.spring(200), height = Otter.spring(150), transparency = Otter.ease(0.5, { duration = 0.3 }), }) -- Or set individual goals (others continue current motion) groupMotor:setGoal({ transparency = Otter.spring(1), }) ``` -------------------------------- ### Define Otter easing goal Source: https://github.com/roblox/otter/blob/main/docs/api-reference.md Constructs a goal using easing options. Includes default configuration parameters. ```lua Otter.ease(targetValue: number, config: Config?): Otter.Goal ``` ```lua type EaseOptions = { -- The duration of the animation, in seconds. Defaults to 1. duration: number? -- The easing style of the animation. Defaults to Enum.EasingStyle.Linear. easingStyle: Enum.EasingStyle? } ``` -------------------------------- ### Initialize ReactOtter motor hook Source: https://github.com/roblox/otter/blob/main/docs/api-reference.md A lower-level hook for Otter motors. Prefer useAnimatedBinding for most use cases. ```lua type ValueType = number | { [string]: number } type GoalType = Otter.Goal | { [string]: Otter.Goal } ReactOtter.useMotor( initialValue: ValueType, onStep: (ValueType) -> () onComplete: nil | (ValueType) -> () ): setGoal: (GoalType) -> () ``` -------------------------------- ### Create a group motor with Otter.createGroupMotor Source: https://context7.com/roblox/otter/llms.txt Manages multiple named values simultaneously, allowing independent goals and configurations for each. ```lua local Otter = require(ReplicatedStorage.Otter) -- Create a group motor with x and y position values local positionMotor = Otter.createGroupMotor({ x = 0, y = 0, }) -- Receive all values together on each frame positionMotor:onStep(function(values) myFrame.Position = UDim2.new(0, values.x, 0, values.y) end) positionMotor:onComplete(function(values) print("Reached position:", values.x, values.y) end) -- Set different goal types for each value positionMotor:setGoal({ x = Otter.spring(300), -- Animate x with spring physics y = Otter.instant(150), -- Jump y immediately to 150 }) ``` -------------------------------- ### Subscribe to Updates with Motor:onStep Source: https://context7.com/roblox/otter/llms.txt Motor:onStep allows subscribing a callback function that executes on each animation frame, receiving value updates. For single motors, the callback receives a number. For group motors, it receives a table containing all current values. The method returns an unsubscribe function to stop receiving updates. ```lua local Otter = require(ReplicatedStorage.Otter) -- Single motor subscription local motor = Otter.createSingleMotor(0) local unsubscribe = motor:onStep(function(value) -- value is a number myFrame.Rotation = value end) motor:setGoal(Otter.spring(360)) -- Later: stop receiving updates unsubscribe() -- Group motor subscription local groupMotor = Otter.createGroupMotor({ r = 255, g = 255, b = 255, }) groupMotor:onStep(function(values) -- values is a table: { r = number, g = number, b = number } myFrame.BackgroundColor3 = Color3.fromRGB(values.r, values.g, values.b) end) groupMotor:setGoal({ r = Otter.spring(100), g = Otter.spring(150), b = Otter.spring(200), }) ``` -------------------------------- ### Otter.spring Source: https://context7.com/roblox/otter/llms.txt Defines a spring-based animation goal using physics simulation. It supports standard frequency/dampingRatio or Figma-style stiffness/damping/mass configurations, and allows fine-tuning resting detection. ```APIDOC ## Otter.spring ### Description Creates a spring-based goal that uses physics simulation for natural, fluid animation. Springs automatically scale velocity based on distance and preserve momentum through retargeting. Supports both standard (frequency/dampingRatio) and Figma-style (stiffness/damping/mass) configurations. ### Method `Otter.spring(goalValue: number, options?: { frequency?: number, dampingRatio?: number, stiffness?: number, damping?: number, mass?: number, restingVelocityLimit?: number, restingPositionLimit?: number })` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```lua local Otter = require(ReplicatedStorage.Otter) local motor = Otter.createSingleMotor(0) motor:onStep(function(value) myFrame.Size = UDim2.new(0, value, 0, value) end) -- Default spring (critically damped, frequency=1) motor:setGoal(Otter.spring(100)) -- Custom spring with frequency and damping ratio motor:setGoal(Otter.spring(100, { frequency = 2, -- Faster response (cycles per second) dampingRatio = 0.5, -- Underdamped (bouncy) })) -- Figma-style spring parameters motor:setGoal(Otter.spring(100, { stiffness = 400, -- Higher = more bounces damping = 20, -- Higher = less springy mass = 1, -- Higher = slower, higher bounce })) -- Fine-tune resting detection thresholds motor:setGoal(Otter.spring(100, { frequency = 1, dampingRatio = 1, restingVelocityLimit = 0.001, -- Default: 0.001 restingPositionLimit = 0.01, -- Default: 0.01 })) ``` ### Response #### Success Response (200) None (This is a goal function) #### Response Example None ``` -------------------------------- ### Define Otter spring goal Source: https://github.com/roblox/otter/blob/main/docs/api-reference.md Constructs a goal using spring physics. Supports standard frequency/damping or Figma-style spring configurations. ```lua Otter.spring(targetValue: number, config: Config?): Otter.Goal ``` ```lua type SpringConfig = { -- The undamped frequency of the spring in cycles per second. Defaults to 1. frequency: number? -- The damping ratio of the spring. Defaults to 1. dampingRatio: number? -- The resting velocity limit for the spring. Defaults to 0.001. restingVelocityLimit: number? -- The resting position limit for the spring. Defaults to 0.01. restingPositionLimit: number? } ``` ```lua type SpringConfig = { -- Influences the number of “bounces” in the animation. stiffness: number, -- Influences the level of spring in the animation. damping: number, -- Influences the speed of the animation and height of the bounce. mass: number, -- The resting velocity limit for the spring. Defaults to 0.001. restingVelocityLimit: number?, -- The resting position limit for the spring. Defaults to 0.01. restingPositionLimit: number? } ``` -------------------------------- ### Assign goals to motors Source: https://github.com/roblox/otter/blob/main/docs/usage/motors.md Use setGoal to define the target state for a motor, triggering the animation. ```lua singleMotor:setGoal(Otter.spring(1)) groupMotor:setGoal({ x = Otter.spring(300), y = Otter.spring(150), }) ``` -------------------------------- ### Extract motor values Source: https://github.com/roblox/otter/blob/main/docs/usage/motors.md Use onStep to register a callback that executes every frame with the current motor values. ```lua singleMotor:onStep(function(transparency) -- do something with transparency end) groupMotor:onStep(function(values) local x = values.x local y = values.y -- position something somewhere end) ``` -------------------------------- ### Otter.createSingleMotor Source: https://context7.com/roblox/otter/llms.txt Creates and manages a motor for animating a single numeric value. It supports step and completion callbacks, and allows setting goals using various physics or easing functions. ```APIDOC ## Otter.createSingleMotor ### Description Creates a motor that controls a single numeric value. The motor will animate from its current value toward any goal you set, firing callbacks on each frame step. ### Method `Otter.createSingleMotor(initialValue: number)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```lua local Otter = require(ReplicatedStorage.Otter) -- Create a motor starting at value 0 local transparencyMotor = Otter.createSingleMotor(0) -- Subscribe to value updates on each frame local disconnect = transparencyMotor:onStep(function(value) myFrame.BackgroundTransparency = value end) -- Subscribe to completion events transparencyMotor:onComplete(function(finalValue) print("Animation complete at value:", finalValue) end) -- Start animating toward value 1 using spring physics transparencyMotor:setGoal(Otter.spring(1)) -- Later: clean up the motor when done transparencyMotor:destroy() ``` ### Response #### Success Response (200) None (This is a constructor function) #### Response Example None ``` -------------------------------- ### Initialize ReactOtter animated binding Source: https://github.com/roblox/otter/blob/main/docs/api-reference.md A React hook that provides a binding to drive animations. Use a number for a Single Motor or a table for a Group Motor. ```lua type ValueType = number | { [string]: number } type GoalType = Otter.Goal | { [string]: Otter.Goal } ReactOtter.useAnimatedBinding( initialValue: ValueType, onComplete: nil | (ValueType) -> () ): ( binding: React.Binding, setGoal: (GoalType) -> () ) ``` -------------------------------- ### Subscribe to Single Motor Updates Source: https://github.com/roblox/otter/blob/main/docs/api-reference.md Subscribe to a single motor's step signal to receive its updated value. The callback receives a number. ```lua local subscription = singleMotor:onStep(function(value) print("Updated with " .. tostring(value)) end) ``` -------------------------------- ### Motor:onComplete Source: https://context7.com/roblox/otter/llms.txt Subscribes a callback that fires when all motor goals have been reached. ```APIDOC ## Motor:onComplete ### Description Subscribes a callback that fires when all motor goals have been reached. Returns an unsubscribe function. ### Parameters - **callback** (function) - Required - Function receiving the final value reached. ``` -------------------------------- ### Otter Core API Source: https://github.com/roblox/otter/blob/main/docs/api-reference.md Core functions for creating motors and defining animation goals. ```APIDOC ## Otter Core API ### Otter.createSingleMotor - **Description**: Constructs a motor that controls a single number value. - **Parameters**: - **initialValue** (number) - Required - The starting value. ### Otter.createGroupMotor - **Description**: Constructs a motor that controls a group of values. - **Parameters**: - **initialValues** ({ string: number }) - Required - The starting values map. ### Otter.ease - **Description**: Constructs a goal that uses easing options to transition to the target value. - **Parameters**: - **targetValue** (number) - Required - The target value. - **config** (EaseOptions) - Optional - Easing configuration. ### Otter.spring - **Description**: Constructs a goal that uses spring physics to transition to the target value. - **Parameters**: - **targetValue** (number) - Required - The target value. - **config** (SpringConfig | FigmaSpringConfig) - Optional - Spring physics configuration. ### Otter.instant - **Description**: Constructs a goal that immediately reaches the target value. - **Parameters**: - **targetValue** (number) - Required - The target value. ``` -------------------------------- ### Motor:onStep Source: https://context7.com/roblox/otter/llms.txt Subscribes a callback to receive value updates on each animation frame. ```APIDOC ## Motor:onStep ### Description Subscribes a callback to receive value updates on each animation frame. Returns an unsubscribe function. ### Parameters - **callback** (function) - Required - Function receiving the current value (number for single, table for group). ``` -------------------------------- ### Update motor goals Source: https://github.com/roblox/otter/blob/main/modules/otter/README.md Updates the target values for existing motors dynamically. ```lua -- Immediately move the motor's value to 100 motor:setGoal(Otter.instant(100)) -- Spring on both axes to 300 multimotor:setGoal({ x = Otter.spring(300), y = Otter.spring(300), }) ``` -------------------------------- ### Otter.ease Source: https://context7.com/roblox/otter/llms.txt Defines a time-based easing animation goal. It supports standard Roblox EasingStyle enums and custom cubic bezier curves, with configurable duration. ```APIDOC ## Otter.ease ### Description Creates a time-based easing goal that transitions to the target value over a specified duration using standard easing functions. Supports all Roblox EasingStyle enums and custom cubic bezier curves. ### Method `Otter.ease(goalValue: number, options?: { duration?: number, easingStyle?: Enum.EasingStyle | number[] })` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```lua local Otter = require(ReplicatedStorage.Otter) local motor = Otter.createSingleMotor(0) motor:onStep(function(value) myFrame.BackgroundTransparency = value end) -- Default: 1 second linear ease motor:setGoal(Otter.ease(1)) -- Custom duration with easing style motor:setGoal(Otter.ease(1, { duration = 0.5, easingStyle = Enum.EasingStyle.Quad, -- Quadratic ease-in })) -- Available easing styles: Linear, Quad, Cubic, Quart, Quint, -- Exponential, Sine, Back, Bounce, Elastic, Circular -- Custom cubic bezier curve (x1, y1, x2, y2) motor:setGoal(Otter.ease(1, { duration = 0.3, easingStyle = { 0.25, 0.1, 0.25, 1 }, -- CSS ease equivalent })) ``` ### Response #### Success Response (200) None (This is a goal function) #### Response Example None ``` -------------------------------- ### Chain Animations with Motor:onComplete Source: https://context7.com/roblox/otter/llms.txt Motor:onComplete subscribes a callback that is triggered when all motor goals have been successfully reached. This is useful for chaining animations sequentially or initiating subsequent logic after transitions are finished. The callback receives the final value of the motor. An unsubscribe function is returned to stop listening for completion events. ```lua local Otter = require(ReplicatedStorage.Otter) local motor = Otter.createSingleMotor(0) motor:onStep(function(value) myFrame.Position = UDim2.new(value, 0, 0, 0) end) -- Chain animations using onComplete local disconnect = motor:onComplete(function(finalValue) print("Reached:", finalValue) -- Start next animation if finalValue == 1 then motor:setGoal(Otter.spring(0)) else motor:setGoal(Otter.spring(1)) end end) -- Start the animation loop motor:setGoal(Otter.spring(1)) -- Later: stop the chain disconnect() motor:stop() ``` -------------------------------- ### Subscribe to Group Motor Updates Source: https://github.com/roblox/otter/blob/main/docs/api-reference.md Subscribe to a group motor's step signal to receive its updated values. The callback receives a map of string keys to number values. ```lua local subscription = groupMotor:onStep(function(values) print("Updated with:") for key, value in values do print("\t" .. key .. ": " .. tostring(value)) end end) ``` -------------------------------- ### Set Goal for Single Motor Source: https://github.com/roblox/otter/blob/main/docs/api-reference.md Use this to set a single goal value for a motor. Ensure the goal type matches the motor's configuration. ```lua singleMotor:setGoal(Otter.spring(1)) ``` -------------------------------- ### Animation Completion Callback for Side Effects Source: https://github.com/roblox/otter/blob/main/docs/usage/react.md Use the `onComplete` callback in `useAnimatedBinding` to trigger side effects, such as enabling a button, after an animation finishes. The button's interactivity is controlled by a state variable updated in the callback. ```lua local function DisabledWhileAnimating() local enabled, setEnabled = React.useState(false) local translated, setTranslated = React.useState(false) local x, setGoal = ReactOtter.useAnimatedBinding(0, function() setEnabled(true) end) React.useEffect(function() setGoal(ReactOtter.spring(if translated then 1 else 0)) setEnabled(false) end, { translated }) return React.createElement( "Frame", { Size = props.size }, React.createElement("TextButton", { Active = enabled, Text = if enabled then "Click me" else "Don't click", Size = UDim2.new(0, 150, 0, 50), Position = x:map(function(value) return UDim2.new(value, -150 * value, 0.5, 0) end), [React.Event.Activated] = function() setTranslated(not translated) end, }) ) end ``` -------------------------------- ### useMotor Hook Source: https://github.com/roblox/otter/blob/main/docs/usage/react.md Introduces the useMotor hook for animating non-React Instances in the DataModel, providing a lower-level alternative to useAnimatedBinding. ```APIDOC ## useMotor ### Description The `useMotor` hook is a lower-level hook used when you need your React component to update values on non-React Instances in the DataModel. It is recommended to use `useAnimatedBinding` unless you have a specific need to animate something that cannot accept a binding. ### Method Not applicable (Hook usage) ### Endpoint Not applicable (Hook usage) ### Parameters #### Arguments - **initialValue** (number | { [string]: number }) - Required - The starting value for the motor. This works similarly to the `initialValue` argument for `useAnimatedBinding`. - **onStep** (function) - Required - A callback function that is executed on each animation step. It receives the current motor value as an argument, respecting the type defined in `initialValue`. - **onComplete** (function) - Optional - A callback function that is invoked each time an animated transition completes. It receives the final motor value as an argument. ### Returns - **setGoal** (function) - A function to set the target value for the animation, equivalent to the second return value of `useAnimatedBinding`. ### Request Example ```lua -- Example usage of useMotor (conceptual, actual implementation depends on the target instance) local motorValue, setMotorGoal = ReactOtter.useMotor(0, function(currentValue) -- Update a non-React instance property here -- e.g., workspace.SomeObject.Transparency = currentValue end, function(finalValue) print("Animation completed with value:", finalValue) end) -- To trigger an animation: -- setMotorGoal(1) ``` ``` -------------------------------- ### Simple Animated Binding in React Source: https://github.com/roblox/otter/blob/main/docs/usage/react.md Use `useAnimatedBinding` to animate a TextButton's TextSize between two values based on a boolean state. The animation is triggered using `ReactOtter.spring` when the state changes. ```lua local function ToggleTextSize() local toggled, setToggled = React.useState(false) local value, setGoal = ReactOtter.useAnimatedBinding(8) React.useEffect(function() setGoal(ReactOtter.spring(if toggled then 24 else 8)) end, { toggled }) return React.createElement("TextButton", { Text = "Hello", TextSize = value, Size = UDim2.new(0, 200, 0, 50), [React.Event.Activated] = function() setToggled(not toggled) end, }) end ``` -------------------------------- ### Motor Control Methods Source: https://github.com/roblox/otter/blob/main/docs/api-reference.md Methods for controlling the state and goals of an Otter motor. ```APIDOC ## POST /roblox/otter/setGoal ### Description Sets the goal of a motor. If the motor is stopped, it will start automatically. The `GoalType` varies for single and group motors. ### Method POST ### Endpoint /roblox/otter/setGoal ### Parameters #### Request Body - **goal** (GoalType) - Required - The goal to set for the motor. For single motors, this is a single value. For group motors, this is a map of keys to values. ### Request Example ```json { "goal": "Otter.spring(1)" } ``` ### Response #### Success Response (200) - **status** (string) - Indicates success. #### Response Example ```json { "status": "success" } ``` ## POST /roblox/otter/start ### Description Starts the motor, allowing it to move. This is typically handled automatically when a goal is set. ### Method POST ### Endpoint /roblox/otter/start ### Response #### Success Response (200) - **status** (string) - Indicates success. #### Response Example ```json { "status": "success" } ``` ## POST /roblox/otter/step ### Description Manually steps the motor by a specified time delta. This is generally not required for normal operation. ### Method POST ### Endpoint /roblox/otter/step ### Parameters #### Request Body - **dt** (number) - Required - The time step to advance the motor by. ### Request Example ```json { "dt": 0.1 } ``` ### Response #### Success Response (200) - **status** (string) - Indicates success. #### Response Example ```json { "status": "success" } ``` ## POST /roblox/otter/stop ### Description Stops the motor, freezing its current state. The motor will remain stopped until `start` or `setGoal` is called. ### Method POST ### Endpoint /roblox/otter/stop ### Response #### Success Response (200) - **status** (string) - Indicates success. #### Response Example ```json { "status": "success" } ``` ## POST /roblox/otter/destroy ### Description Destroys the motor, making it unusable and cleaning up any associated resources. ### Method POST ### Endpoint /roblox/otter/destroy ### Response #### Success Response (200) - **status** (string) - Indicates success. #### Response Example ```json { "status": "success" } ``` ``` -------------------------------- ### ReactOtter.useAnimatedBinding Source: https://context7.com/roblox/otter/llms.txt A React hook that provides animated bindings driven by Otter motors. It automatically selects a single or group motor based on the initial value type and returns a binding for element properties along with a setGoal function. ```APIDOC ## ReactOtter.useAnimatedBinding ### Description A React hook that provides animated bindings driven by Otter motors. Automatically selects single or group motor based on initial value type. Returns a binding for element properties and a setGoal function. ### Parameters - **initialValue** (any) - Required - The starting value for the animation. Can be a number (SingleMotor) or a table (GroupMotor). - **onComplete** (function) - Optional - A callback function that fires when the animation reaches its goal. ### Return Value - **binding** (Binding) - A React binding object used to map animated values to component properties. - **setGoal** (function) - A function to update the animation goal. ```