Camera Manipulation Jitter on Low Framerates

Hello there,

I am attempting to create a realistic camera shake effect using RunService and Linear Interpolation. It works great, however, if you have a frame rate lower than 60, the camera movement jitters a lot and is very unstable.

If you would like to take a test my code, make a LocalScript in StarterCharacterScripts and paste in this:

--// Variables \\--

local Players = game:GetService("Players")
local Player = Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local Humanoid = Player.Character:WaitForChild("Humanoid")
local Mouse = Player:GetMouse()
local Camera = game:GetService("Workspace").CurrentCamera
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local BobbingConnection = nil
local func1, func2, func3, func4, val, val2 = 0, 0, 0, 0, 0, 0
local int, int2 = 10, 10
local vect3 = Vector3.new()

--// Functions \\--

local function lerp(a, b, c)
	return a + (b - a) * c
end

--// View Bobbing and Camera Shake \\--

BobbingConnection = RunService.RenderStepped:Connect(function(deltaTime)
	deltaTime = deltaTime * 60 --// Multiply deltaTime by 60 to make it consistent with RenderStepped's unit.

	--// If the humanoid is dead, stop the camera bobbing and shaking.
	if Humanoid.Health <= 0 then
		BobbingConnection:Disconnect()
		return
	end

	--// Calculate the root magnitude (player movement speed) and cap it to a maximum value.
	local rootMagnitude = Humanoid.RootPart and Vector3.new(Humanoid.RootPart.Velocity.X, 0, Humanoid.RootPart.Velocity.Z).Magnitude or 0
	local calcRootMagnitude = math.min(rootMagnitude, 50)

	--// Calculate camera bobbing and shaking parameters based on deltaTime and rootMagnitude.
	if deltaTime > 3 then
		func1, func2 = 0, 0
	else
		func1 = lerp(func1, math.cos(tick() * 0.5 * math.random(10, 15)) * (math.random(5, 20) / 200) * deltaTime, 0.05 * deltaTime)
		func2 = lerp(func2, math.cos(tick() * 0.5 * math.random(5, 10)) * (math.random(2, 10) / 200) * deltaTime, 0.05 * deltaTime)
	end

	--// Apply camera movement and shake based on the calculated parameters.
	Camera.CFrame = Camera.CFrame *
		(CFrame.fromEulerAnglesXYZ(0, 0, math.rad(func3)) *
			CFrame.fromEulerAnglesXYZ(math.rad(func4 * deltaTime), math.rad(val * deltaTime), val2) *
			CFrame.Angles(0, 0, math.rad(func4 * deltaTime * (calcRootMagnitude / 5))) *
			CFrame.fromEulerAnglesXYZ(math.rad(func1), math.rad(func2), math.rad(func2 * 10)))

	--// Calculate and apply camera rotation based on player movement direction.
	val2 = math.clamp(lerp(val2, -Camera.CFrame:VectorToObjectSpace((Humanoid.RootPart and Humanoid.RootPart.Velocity or Vector3.new()) / math.max(Humanoid.WalkSpeed, 0.01)).X * 0.08, 0.1 * deltaTime), -0.35, 0.2)

	--// Update camera rotation speed and intensity based on player movement speed.
	func3 = lerp(func3, math.clamp(UserInputService:GetMouseDelta().X, -5, 5), 0.25 * deltaTime)
	func4 = lerp(func4, math.sin(tick() * int) / 5 * math.min(1, int2 / 10), 0.25 * deltaTime)
	if rootMagnitude > 1 then
		val = lerp(val, math.cos(tick() * 0.5 * math.floor(int)) * (int / 200), 0.25 * deltaTime)
	else
		val = lerp(val, 0, 0.05 * deltaTime)
	end

	--// Adjust camera shake intensity based on player movement speed.
	if rootMagnitude > 20 then
		int = 20
		int2 = 18
	elseif rootMagnitude > 0.1 then
		int = 12
		int2 = 14
	else
		int2 = 0
	end

	--// Store the camera's LookVector for future calculations.
	vect3 = lerp(vect3, Camera.CFrame.LookVector, 0.125 * deltaTime)
end)

I’ve added in some comments to make it easier to understand.

Any help is appreciated. Have a good day.

Thanks,
—Nebulimity

I am not certain about this, but I remember experiencing this issue too before and fixed it by using the deltatime from a different engine schedule.

local deltatime: number = 0
RunService.Stepped:Connect(function(_, dt)
    deltatime = dt
end)

RunService.RenderStepped:Connect(function()
    --use deltatime from .Stepped
end)

It might have worked, but it really messed up the view bobbing. It seemed to make the deltaTime higher and higher until it broke the system. I’ll try fixing it and let you know if it worked.

Are you losing more frames as this script runs?
Also you could try removing the deltatime * 60 I think thats amplifying your problem

No, I am not losing frames. I removed the deltaTime = deltaTime * 60 and the script just… stopped working?

Okay, the delta time doesnt stop the script its just too small now. I changed it to 30 and it looks good but its not as noticeable. I am not sure how to replicate the low frames to see what you see though.