# Introduction

Hello there!

In this forum post, I’ll show you the inner workings of a physics-based spring and how to make your own! But hold on, what are the uses of such a thing?

For example, FPS games use springs for gun recoil, sway, walk cycle, and much more. And there are a ton more use-cases that extend beyond FPS games.

The base code in this post is from this tutorial made by The Step Event. All I did was port it into Roblox as a module script (and tweak a few stuff). So, massive credits to him!

So without further ado, let’s begin!

# Start

Before we start doing anything, let’s create a module script. Here’s the code:

• Target - spring’s resting position
• Tension - aggresiveness of wobble
• Dampening - fadeout of spring
``````local spring = {}
spring.__index = spring

function spring.new(target: any)
local self = {}

self.Target = target
self.Position = target
self.Velocity = 0

self.Tension = .025
self.Dampening = .3

return setmetatable(self, spring)
end

function spring:Update()
end

function spring:Impulse(num: number)
end

return spring
``````

And yes, there are empty functions. We’ll get back to them later.

A spring has two states, a resting state and an active (extension/contraction) state. If you stretch a spring upwards, it bounces up and down and slows down until it reaches its resting state. So how will we mimic this behavior? Let’s first start with Hooke’s Law.

Hooke’s Law equates to the force needed to push the spring back into it’s rest state. This is why we negate `k` in the equation.

``````F = -k * x
F = -tension * displacement
``````

And to actually simulate the spring’s movement, we’ll need Newton’s Second Law.

``````F = m * a
F = mass * acceleration
``````

Combining Hooke’s law with Newton’s second law, we get this.

``````F = (-k * m) / x
F = (-tension * mass) / displacement
``````

For simplicity’s sake, let’s assume our spring has a mass of 1.

``````F = -k * x
F = -tension * displacement
``````

Then for the loss of energy (or dampening), we can use this formula.

``````F = d * v
F = dampening * velocity
``````

And in combining these formulas, we get this.

``````F = (-k * x) - (d * v)
F = (-tension * displacement) - (dampening * velocity)
``````

Okay, okay, that’s nice. Time to convert it into code. Remember that an object has a position, velocity changes the position, and acceleration changes velocity.

``````function spring:Update()
self.Velocity += (-self.Tension * (self.Position - self.Target)) - (self.Dampening * self.Velocity)
self.Position += self.Velocity
end
``````

And of course, we can’t forget our impulse function.

``````function spring:Impulse(num: number)
self.Velocity += num
end
``````

Everything should look like this now:

``````--[[
Description:
A physics-based spring, useful for many applications like
gun recoil, viewmodel arms sway, and etc..

Explanation:
Basics of Motion:
An object has position,
Velocity changes position,
Acceleration changes velocity.

Hooke's Law (force of spring):
F = -tension * displacement

Newton's Second Law (acceleration of spring):
F = mass * acceleration
(let mass be 1)
F = acceleration

Dampening (damper of spring):
F = dampening * velocity

Combination:
F = (-tension * displacement) - (dampening * velocity)
]]

local spring = {}
spring.__index = spring

function spring.new(target: any)
local self = {}

self.Target = target
self.Position = target
self.Velocity = 0

self.Tension = .025
self.Dampening = .3

return setmetatable(self, spring)
end

function spring:Update()
self.Velocity += (-self.Tension * (self.Position - self.Target)) - (self.Dampening * self.Velocity)
self.Position += self.Velocity
end

function spring:Impulse(num: number)
self.Velocity += num
end

return spring
``````

And that’s practically it, you can now go ham with your new physics-based spring module. Thanks for reading!

# Example

## Moving Part

``````local spring = require(script.Spring).new(5)
spring.Dampening = .1
spring.Tension = .1
local random = Random.new()

RNS.Heartbeat:Connect(function()
spring:Update()
workspace.Test.Position = Vector3.new(-8.025, spring.Position, 44.45)
end)

while true do
spring:Impulse(2)

spring:Impulse(-2)
end
``````

``````local RNS = game:GetService("RunService")

local part = script.Parent
local spring = require(script.Spring).new(part.Position.Y)
spring.Dampening = .1
spring.Tension = .5
local deb = false

RNS.Heartbeat:Connect(function(delta)
spring:Update(delta)
part.Position = Vector3.new(part.Position.X, spring.Position, part.Position.Z)
end)

part.Touched:Connect(function()
if deb then
return
end

deb = true

part.BrickColor = BrickColor.Red()
spring:Impulse(-1)
part.BrickColor = BrickColor.Green()

deb = false
end)
``````

32 Likes

wow this is very cool. is there any way to make this work for cameras?

2 Likes

The spring module does not directly change parts, it just stores values. So yes, you can use the values to move the Camera CFrame.

2 Likes

I’ve a question. Why did you set the value of velocity to `0 * target`? Wouldn’t it just set the value to 0?

1 Like

Oh yeah right, didn’t notice that. I changed the code now.

1 Like

For those of you trying to fine tune your spring values, here’s an approach I follow:

variables:
d = damping constant
m = mass constant (in the code above, they use 1 for mass)
k = spring constant (tension)

d^2 > 4mk

d^2 = 4mk

## Case 3: Underdamped Spring

d^2 < 4mk

Based on the type of spring you want, adjust the variables so that they fit the specific case above.

8 Likes

attempt to perform arithmetic (sub) on Vector3 and number

1 Like

Please state the line and script on which this error occured. Also provide the code you used to produce this error.

1 Like

Heya there!

This is a pretty old-ish thread so I haven’t really thought about it but, unfortunately as of now, the code I supplied doesn’t support Vectors. The examples I gave “uses Vectors” but if you look closely, the only values I’m feeding are numbers.

You could probably modify the code itself to make it work with Vectors, but that’s all I can tell you- (I’m a bit busy atm)

1 Like