Skip to content
1 / 5
Lesson 01 of 05

Spring Systems for Smooth Motion

Build a spring data type that powers smooth, physically-based camera bob, sway, and UI bounce.

A spring is a simple simulation: a value being pulled toward a target with force proportional to distance, and resisted by damping proportional to velocity. Update it each frame and it produces beautiful organic motion.

Springs replace TweenService for anything that needs to feel physical — camera sway, weapon bob, UI elements that overshoot and settle.

Key parameters
  • Stiffness (k) — how strongly it pulls toward target. Higher = snappier.
  • Damping (d) — how quickly oscillation dies. 2*sqrt(k) = critically damped (no bounce).
  • Mass — inertia. Higher = slower to start and stop.

Run the spring update in RenderStepped for camera effects.

luau
1-- Spring class
2local Spring = {}
3Spring.__index = Spring
4
5function Spring.new(initial, stiffness, damping, mass)
6 return setmetatable({
7 position = initial,
8 velocity = initial * 0,
9 target = initial,
10 k = stiffness or 50,
11 d = damping or 10,
12 m = mass or 1,
13 }, Spring)
14end
15
16function Spring:update(dt)
17 local force = (self.target - self.position) * self.k
18 local damping = self.velocity * self.d
19 local accel = (force - damping) / self.m
20 self.velocity = self.velocity + accel * dt
21 self.position = self.position + self.velocity * dt
22 return self.position
23end
24
25-- Camera bob example
26local bobSpring = Spring.new(Vector3.zero, 40, 8, 1)
27local t = 0
28
29RunService.RenderStepped:Connect(function(dt)
30 local speed = hrp.AssemblyLinearVelocity.Magnitude
31 t += dt * speed * 0.8
32 local bob = Vector3.new(math.sin(t)*0.08, math.abs(math.sin(t*2))*0.06, 0)
33 bobSpring.target = bob * math.clamp(speed/16, 0, 1)
34 local pos = bobSpring:update(dt)
35 camera.CFrame = camera.CFrame * CFrame.new(pos)
36end)
READY · 36 lines