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

GitHubLatest 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