Spring-driven motion - spr

:comet:spr

Docs | Download | Home

Ljt38piFyS

Springs are a powerful way of describing physically based motion.
spr is an accessible library for creating beautiful UI animations from springs.

Fluid animations can be created in a single line of code with a simple syntax. Blending between animations is handled automatically in a physically-accurate way.

Driving Simulator uses spr extensively to handle over 500 unique animations.

Gallery



Features


A small API

  • spr is easy to learn for non-programmers.
  • You can animate anything by giving spr a target value and a set of animation parameters.
  • You don’t have to memorize new datatypes or more than a few functions.

Easy-to-tune motion

  • You should be able to know how an animation will look without running the game.
  • Motion is defind by frequency and damping ratio, which are easy to understand and visualize.

An analytical spring solver

  • The spr spring solver is extremely robust and provides protections from accidentally passing bad motion parameters.
  • If spr is given a nonconverging set of motion parameters, it will throw a clear error describing what is wrong and how to fix it.

Tight integration with Roblox datatypes

  • spr directly animates Roblox properties without additional layers of indirection.
  • spr performs runtime type checking, providing stronger typing than Roblox instance property setters.
  • spr knows how to animate in the ideal space for each datatype.

Spring fundamentals


Damping ratio and frequency are the two properties defining a spring’s motion.

Damping ratio

  • Damping ratio < 1 overshoots and converges on the target. This is called underdamping.
  • Damping ratio = 1 converges on the target without overshooting. This is called critical damping.
  • Damping ratio > 1 converges on the target without overshooting, but slower. This is called overdamping.

Critical damping is recommended as the most visually neutral option.
Underdamping is recommended for animations that need to “pop.”

Damping ratio and frequency can be visualized here.

Examples


-- damping ratio 1 (critically damped), frequency 0.5 (slow)
-- frame slowly moves to the middle of the screen without overshooting

spr.target(frame, 1, 0.5, {
    Position = UDim2.fromScale(0.5, 0.5)
})
-- damping ratio 0.6 (underdamped), frequency 4 (fast)
-- frame quickly moves to the middle, overshoots, and wobbles before converging

spr.target(frame, 0.6, 4, {
    Position = UDim2.fromScale(0.5, 0.5)
})
-- animate Position and Size
spr.target(frame, 0.6, 1, {
    Position = UDim2.fromScale(1, 1),
    Size = UDim2.fromScale(0.5, 0.5)
})

wait(1)

-- stop all animations on Frame
spr.stop(frame)

How to get


  1. Paste the source of spr.lua into a new ModuleScript.
  2. Require the ModuleScript with local spr = require(<path to spr goes here>)
  3. Follow the documentation to get started with the API.

Documentation on how to use ModuleScripts can be found here.
roblox-ts bindings for spr can be installed here.

Issues and bugs can be filed under GitHub issues.

201 Likes

This is exactly what I was looking for. Now I can make cool animated GUIs

5 Likes

Is there a way for an instance’s property to target another instance property?

For example, spr.target(frame,damp,frequency,{
Position = Part2.Position
})

This way Part1 can follow Part2’s position. This should look similar to connecting a spring constraint to both parts.

Please let me know how I can achieve this with your library(If it is possible).

spr.target to the other object’s position every frame.

RunService.Stepped:Connect(function()
   spr.target(Part1, d, f, {
      Position = Part2.Position
   })
end)

Useful module, this looks great. I have three questions though:

#1. Do you scale by delta so that the spring movement is the same on every frame rate? I’m assuming you are already but I cba to read the source rn

#2. Do you have a rough estimate of frequency to seconds? Like 1 frequency = x seconds

#3. Is there any way for us to detect when the spring stops moving, like a .Completed event? This would be very useful

11 Likes

This is such a cool module. I will definitely be using this in my game! :slight_smile:

1 Like

Been using this module for a while and I love it! But one thing that would be useful is a .Completed event to check when the Spring has been completed.

3 Likes

This is awesome, keep up the good work

spr 2.1 has been released. Download

Changes since the last post here:

  • Added support for CFrames
  • Added support for booleans
  • Added support for ColorSequences
  • Model pivot can be animated via Pivot pseudo-property
  • Model scale can be animated via Scale pseudo-property
  • Added Luau strict mode for better auto-complete
  • Added completed callback to track when spr is finished animating an object

Example - Completion

-- Destroy when fully transparent
spr.target(part, damp, freq, {Transparency = 1})
spr.completed(part, function() part:Destroy() end)

Example - Pivot & Scale

spr.target(model, damp, freq, {
    Pivot = CFrame.new(),
    Scale = 2,
})

13 Likes

I’ve seen your post earlier and coming back to it as I’m testing it out.

The project is fairly near overall and well put together. Makes animating much smoother and appealing to the eye. Great project.

Dope! Using this in my game too!

Question: Do unsupported type like NumberSequence could be supported on the next update?