Increasing Camera performance under low FPS

Hi!
This is my Camera script. (not full 100% for most of it)

game:GetService("RunService").Heartbeat:Connect(function(step)
	if not IsBuilding then return end
	--Speed must = .02
	--Step is .01 on average
	--0.5 / step
	--	print(Camera().Name)
	--[[
	print(step)
	print(0.5 / step)
	print(0.5 * step)
	]]
	D_Hold = UIS:IsKeyDown(Enum.KeyCode.D)
	A_Hold = UIS:IsKeyDown(Enum.KeyCode.A)
	S_Hold = UIS:IsKeyDown(Enum.KeyCode.S)
	W_Hold = UIS:IsKeyDown(Enum.KeyCode.W)
	E_Hold = UIS:IsKeyDown(Enum.KeyCode.E)
	Q_Hold = UIS:IsKeyDown(Enum.KeyCode.Q)
	Up = UIS:IsKeyDown(Enum.KeyCode.Up)
	Down = UIS:IsKeyDown(Enum.KeyCode.Down)
	Left = UIS:IsKeyDown(Enum.KeyCode.Left)
	Right = UIS:IsKeyDown(Enum.KeyCode.Right)
--	print(0.5 / step)
--	print(0.5 * step)
	while wait(0.05) do
		local XR = (D_Hold and 0.75 * step * CameraSpeed) or 0
		local XL = (A_Hold and -0.75 * step * CameraSpeed) or 0
		local ZF = (W_Hold and 0.75 * step * CameraSpeed) or 0
		local ZB = (S_Hold and -0.75 * step * CameraSpeed) or 0
		local RotR = (E_Hold and .05 * CameraRotSpeed) or 0
		local RotL = (Q_Hold and -.05 * CameraRotSpeed) or 0
		local XAxis = XR + XL
		local ZAxis = ZF + ZB
		if IsBuilding then
			if(XAxis ~= 0 or ZAxis ~= 0) then
				local RightDirection = (getCamPart().CFrame * CFrame.Angles(0, math.pi / 2, 0)).LookVector
				getCamPart().CFrame = getCamPart().CFrame + (getCamPart().CFrame.LookVector * Vector3.new(ZAxis, 0, ZAxis)) + ( - RightDirection * Vector3.new(XAxis, 0, XAxis))
				game:GetService("TweenService"):Create(Camera, TweenInfo.new(0.05, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, 0, false, 0), {
					CFrame = getCamPart().CFrame
				}):Play()
			end
			if (RotR ~= 0 or RotL ~= 0) then
				getCamPart().Orientation = getCamPart().Orientation + Vector3.new(0, RotR + RotL, 0)
				game:GetService("TweenService"):Create(Camera, TweenInfo.new(0.05, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, 0, false, 0), {
					--orientation = CFrame.Angles(math.rad(0), RotR + RotL, math.rad(0)) * Camera.CFrame
					CFrame = getCamPart().CFrame
				}):Play()
			end
			wait(0.05)
		end
	end
end)

It works great, but the issue is under lower FPS it becomes choppy, due to the heartbeat loop.
I really want it to be smooth, even under low FPS.

As you can see, it’s not smooth at all.
Thanks for any help!

I did it like this, and it broke it.

task.spawn(function(step)
	while task.wait() do 
	if not IsBuilding then return end
	--Speed must = .02
	--Step is .01 on average
	--0.5 / step
	--	print(Camera().Name)
	--[[
	print(step)
---etc

RenderStepped should be used for Camera movements. Also you should never use a loop like that, RunService’s primary 3 events (RenderStepped, Heartbeat, Stepped) do not wait for the function to complete before firing again. You are looping every frame and you are not breaking that loop ever, it is going to have massive performance taxes (mainly on memory).

game:GetService("RunService").Heartbeat:Connect(function(step)
	if not IsBuilding then return end
	--Speed must = .02
	--Step is .01 on average
	--0.5 / step
	--	print(Camera().Name)
	--[[
	print(step)
	print(0.5 / step)
	print(0.5 * step)
	]]
	D_Hold = UIS:IsKeyDown(Enum.KeyCode.D)
	A_Hold = UIS:IsKeyDown(Enum.KeyCode.A)
	S_Hold = UIS:IsKeyDown(Enum.KeyCode.S)
	W_Hold = UIS:IsKeyDown(Enum.KeyCode.W)
	E_Hold = UIS:IsKeyDown(Enum.KeyCode.E)
	Q_Hold = UIS:IsKeyDown(Enum.KeyCode.Q)
	Up = UIS:IsKeyDown(Enum.KeyCode.Up)
	Down = UIS:IsKeyDown(Enum.KeyCode.Down)
	Left = UIS:IsKeyDown(Enum.KeyCode.Left)
	Right = UIS:IsKeyDown(Enum.KeyCode.Right)
	--	print(0.5 / step)
	--	print(0.5 * step)
	local XR = (D_Hold and 0.75 * step * CameraSpeed) or 0
	local XL = (A_Hold and -0.75 * step * CameraSpeed) or 0
	local ZF = (W_Hold and 0.75 * step * CameraSpeed) or 0
	local ZB = (S_Hold and -0.75 * step * CameraSpeed) or 0
	local RotR = (E_Hold and .05 * CameraRotSpeed) or 0
	local RotL = (Q_Hold and -.05 * CameraRotSpeed) or 0
	local XAxis = XR + XL
	local ZAxis = ZF + ZB
	if IsBuilding then
		if(XAxis ~= 0 or ZAxis ~= 0) then
			local RightDirection = (getCamPart().CFrame * CFrame.Angles(0, math.pi / 2, 0)).LookVector
			getCamPart().CFrame = getCamPart().CFrame + (getCamPart().CFrame.LookVector * Vector3.new(ZAxis, 0, ZAxis)) + ( - RightDirection * Vector3.new(XAxis, 0, XAxis))
			game:GetService("TweenService"):Create(Camera, TweenInfo.new(0.05, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, 0, false, 0), {
				CFrame = getCamPart().CFrame
			}):Play()
		end
		if (RotR ~= 0 or RotL ~= 0) then
			getCamPart().Orientation = getCamPart().Orientation + Vector3.new(0, RotR + RotL, 0)
			game:GetService("TweenService"):Create(Camera, TweenInfo.new(0.05, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, 0, false, 0), {
				--orientation = CFrame.Angles(math.rad(0), RotR + RotL, math.rad(0)) * Camera.CFrame
				CFrame = getCamPart().CFrame
			}):Play()
		end
	end
end)

Avoid adding busy waits/yielding code inside RenderStepped/Heartbeat/Stepped (because otherwise separate executions of the callback function will overlap one another) and definitely avoid doing loops as the callback functions connected to those aforementioned events are essentially looped (executed every frame) anyway and thus do not need any explicit loops such as for/repeat/while.