RunService deltaTime isn't reliable?

I have code in a RenderStepped connection and I am using the delta time so that I get the same result across different frame rates. Yet I get completely different results in studio at 60 fps and in a live server at 400 fps. Am I doing something wrong? Is delta wildly innacurate at high frame rates?

local timeGone = 0
	timeGone += delta
	local bounceOffset =, math.sin(timeGone) / 100, 0)

In addition to the above code yielding completely different results at high frame rates. I had another RenderStepped function break because the delta time value that was passed was somehow zero (???) and then I tried to divide another number by it :skull:



1 Like

Thats the point of deltaTime. Its the time that it took to render the current frame. So on 60 FPS that would be roughly 1/60 of a second, but on higher framerates like 400 FPS it would be 1/400 of a second.

That’s why I am using delta time…

1 Like

This looks right, I theorize whatever is causing your ball to bounce faster on higher framerates has everything to do with your FPS uncapper.

You should take a look at what timeGone is in relation to os.clock() with your fps capper on and off. They should match up 1 to 1.

2000 frames later

Here’s my code:

local RunService = game:GetService("RunService")

local TimeStart
local TotalTime = 0
local FrameNumber = 0

RunService:BindToRenderStep("FPSTest", Enum.RenderPriority.First.Value, function(DeltaTime)
	if not TimeStart then TimeStart = os.clock() TotalTime = -DeltaTime end
	local TimePassed = os.clock() - TimeStart
	TotalTime = TotalTime + DeltaTime 
	FrameNumber += 1
	print("Frame:                  " .. FrameNumber)
	print("Frame Time:             " .. DeltaTime)
	print("(time())    TimePassed: " .. TimePassed)
	print("(DeltaTime) TimePassed: " .. TotalTime)
	print("Diff:                   " .. math.abs(TimePassed - TotalTime))
1 Like

Doesn’t seem to be the case

I think you’re applying the offset incorrectly math.sin returns a number between 1 and -1, dividing it by 100 gives you an offset of max 0.01. This is way to small to be noticeable. You are probably adding it to the balls position, on high frame rates it will compound that much faster resulting in it going further away from the origin.

You need to do
ball.CFrame = ball.OriginalCFrame + bounceOffset
instead of
ball.CFrame = ball.Cframe + bounceOffset

I’ve got no clue how you’re getting a 0

1 Like

Actually this code isn’t for a ball. I have not said it was :sweat_smile:

It for a Part to bob up and down. It is working correctly, except for the fact that on higher frame rates it bobs way higher than lower frame rates.

Same. Maybe there is some weird floating point error math stuff when the delta time is so small. It could be stuff breaks at high frame rates because Roblox doesn’t support high frame rates natively.

At least you gave me affirmation that my code using delta is theoretically correct and I’m not making some mistake.

This is a sign that you are modifying the position incorrectly.

Here’s a frame by frame run down:

Removed /100 for simplicity

TotalTime = 0.2, Vertical Offset = ~0.19, part y position = 0.19
TotalTime = 0.4, Vertical Offset = ~0.4, part y position = 0.59
TotalTime = 0.6, Vertical Offset = ~0.56, part y position = 1.15


TotalTime = 0.1, Vertical Offset = ~0.1, part y position = 0.1
TotalTime = 0.2, Vertical Offset = ~0.19, part y position = 0.29
TotalTime = 0.3, Vertical Offset = ~0.3, part y position = 0.59
TotalTime = 0.4, Vertical Offset = ~0.4, part y position = 0.99
TotalTime = 0.5, Vertical Offset = ~0.48, part y position = 1.47
TotalTime = 0.6, Vertical Offset = ~0.56, part y position = 2.03 -- Almost twice as high!

Also need to scale it up, remove the /100 and make it * by how ever many studs up/down you want it to go.

400fps would only go down to 0.0025, not small enough for floating point error stuff. I think it’s got something to do with the fps un-capper you’re using.

1 Like

wait I think I understand

So timeGone is working correctly and will always be the same time no matter the fps.

But since I am adding to the cframe each frame it will add more at higher frame rates!

So the solution is to add the bounceOffset to the original CFrame, that way even if the function runs more times because of higher frame rates it still only applies the same offset!

An alternative solution is to remove /100 and multiply timeGone by delta and studs per second (in this case I just want 1 stud per second)

timeGone += delta
local bounceOffset =, math.sin(timeGone) * delta, 0)

So my code was incorrect after all. I was under the illusion that I was accounting for frame rate because I was using the delta variable in timeGone but did not actually multiply the result of math.sin by delta. It could be because I am not that familiar with trigonometry.

Thank you so much for figuring this out and explaining it to me, you were a big help! :+1:

As for getting 0 returned by delta, that affects a different code and I just put an if statement to account for it for now.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.