Skip to content

nilleniumrust/SpringShaker

Repository files navigation

springshaker-logo2(1) Documentation Devforum Post Wally Version Releases Roblox Marketplace

SpringShaker

License Size Docs Deployment Latest Downloads Open Issues

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 MarketplaceGet SpringShaker

GitHubLatest Release

Wally:

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

Important

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)

Tip

Did you know SpringShaker is reusable when shakes end?

Parameter Type Description
Magnitude number Scale of the entire shake. Acts as amplitude.
Roughness number Speed of the Perlin noise sampling.
FadeInTime number Seconds to reach full shake intensity.
FadeOutTime number Seconds to decay back to zero.
Tension number Oscillation frequency — higher values = faster wobble.
Damping number Resistance to oscillation — higher values = quicker ring-down.
RotationInfluence Vector3 Per-axis rotation multiplier (pitch, yaw, roll).

Warning

Do not set Magnitude above 10^5. Doing so will cause Resonance. However, if activated SpringShaker will abort the Spring instance and reset itself.


Presets

Preset Description
Explosion Violent initial shock that decays into heavy rumble
Landmine Sharp high-tension sting that stabilizes almost instantly
Earthquake Low-frequency rolling sway — ground liquefying feeling
Resonance Slow rhythmic wobble that builds over time
Bounce Soft fluid recoil with minimal roughness
Vibration Subtle continuous hum
ExplosiveBullet Fast sharp crack of a nearby hit
Rumble Low persistent ground vibration
Impact Sudden 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

Instances Non-Native Avg Non-Native Peak Native Avg Native Peak
1 0.0039ms 0.0237ms 0.004ms 0.015ms
10 0.0135ms 0.0516ms 0.013ms 0.056ms
50 0.0558ms 0.2506ms 0.056ms 0.134ms
100 0.1093ms 0.1956ms 0.110ms 0.161ms
250 0.2804ms 0.6637ms 0.282ms 0.422ms

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

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

Instances SpringShaker RbxCameraShaker Shake
1 0.0054ms 0.0062ms 0.0081ms
10 0.0199ms 0.0159ms 0.0130ms
50 0.0636ms 0.0518ms 0.0355ms
100 0.1199ms 0.1032ms 0.0566ms
250 0.2822ms 0.2385ms 0.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

Module Peak Retained 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.

Note

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

Packages

 
 
 

Contributors