Skip to main content

SpringShaker

A physics-based camera shaker for Roblox built on a damped harmonic oscillator — giving shakes real weight, momentum, and natural decay.

Traditional camera shakers scale Perlin noise by a fade envelope and call it a day. SpringShaker drives each shake through a damped harmonic oscillator — the same math that governs real pendulums and spring systems. Explosions ring out naturally, earthquakes swell and bleed off over time, and impacts feel physically grounded rather than scripted.


Installation​

Roblox Marketplace — Get SpringShaker

GitHub — Latest Release

Wally:

[dependencies]
SpringShaker = "nilleniumrust/springshaker@1.1.2"

⚠️ SpringShaker is client-only. Do not place it in server-side DataModel services.


Usage​

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local SpringShaker = require(ReplicatedStorage.src.SpringShaker)

-- Sustained earthquake until manually stopped
SpringShaker:ShakeSustained(SpringShaker:GetPreset("Earthquake"))

task.delay(5, function()
SpringShaker:HaltDurationWise(1) -- smooth 1 second fade out
end)

Compare this to what other modules require — manual CFrame management, a secondary Heartbeat connection to fix drift, and writing your own render loop:

-- What you'd write with other modules
local camCf: CFrame
shake:BindToRenderStep(Shake.NextRenderName(), priority, function(pos, rot, isDone)
camCf = camera.CFrame
camera.CFrame *= CFrame.new(pos) * CFrame.Angles(rot.X, rot.Y, rot.Z)
end)
RunService.Heartbeat:Connect(function()
camera.CFrame = camCf -- required to prevent drift at high FPS
end)

SpringShaker handles render priority, camera application, and drift prevention internally. Nothing to configure.


Custom Shakes​

local mortar = SpringShaker.new({
Magnitude = 5,
Roughness = 5,
FadeInTime = 0,
FadeOutTime = 1.8,
Tension = 122,
Damping = 13,
RotationInfluence = Vector3.new(1.2, 0.5, 0.3),
})

SpringShaker:ShakeOnce(mortar)
ParameterTypeDescription
MagnitudenumberScale of the entire shake. Acts as amplitude.
RoughnessnumberSpeed of the Perlin noise sampling.
FadeInTimenumberSeconds to reach full shake intensity.
FadeOutTimenumberSeconds to decay back to zero.
TensionnumberOscillation frequency — higher values = faster wobble.
DampingnumberResistance to oscillation — higher values = quicker ring-down.
RotationInfluenceVector3Per-axis rotation multiplier (pitch, yaw, roll).

⚠️ Do not set Magnitude above 10^5. This risks Resonance — SpringShaker will abort and warn if detected.


Presets​

PresetDescription
ExplosionViolent initial shock that decays into heavy rumble
LandmineSharp high-tension sting that stabilizes almost instantly
EarthquakeLow-frequency rolling sway — ground liquefying feeling
ResonanceSlow rhythmic wobble that builds over time
BounceSoft fluid recoil with minimal roughness
VibrationSubtle continuous hum
ExplosiveBulletFast sharp crack of a nearby hit
RumbleLow persistent ground vibration
ImpactSudden blunt force with quick ring-down

Performance​

Benchmarked in Play mode with os.clock() wrapping only the update cost per frame — camera application excluded for fairness.

SpringShaker native vs non-native​

InstancesNon-Native AvgNon-Native PeakNative AvgNative Peak
10.0039ms0.0237ms0.004ms0.015ms
100.0135ms0.0516ms0.013ms0.056ms
500.0558ms0.2506ms0.056ms0.134ms
1000.1093ms0.1956ms0.110ms0.161ms
2500.2804ms0.6637ms0.282ms0.422ms

--!native provides ~25x faster per-call performance and ~28% less peak memory at scale.

Three-way comparison (all with --!native)​

InstancesSpringShakerRbxCameraShakerShake
10.0054ms0.0062ms0.0081ms
100.0199ms0.0159ms0.0130ms
500.0636ms0.0518ms0.0355ms
1000.1199ms0.1032ms0.0566ms
2500.2822ms0.2385ms0.1369ms
1000~1.20ms~0.88ms~0.51ms

SpringShaker is the most physics-complete of the three at the cost of being the heaviest per frame. At 10 simultaneous instances — a realistic maximum for most games — total frame cost is under 0.02ms.

Memory at 1000 instances​

ModulePeakRetained after GC
SpringShaker~2.5MB~0KB
RbxCameraShaker~0.6MB~925KB
Shake~0.6MB~0KB

SpringShaker uses more peak memory due to richer physics state per instance, but releases everything completely on recycle. RbxCameraShaker retains ~925KB after GC consistently across multiple runs.

⚠️ SpringShaker has been stress tested up to 1,000,000 simultaneous instances. Please do not do this. 1–25 is a sensible maximum for any real game.


References​

Special Thanks​

Janitor v1.18.3 by howmanysmall