Why Wednesday

Delta Time, Explained: Why Your Game Runs Faster on a Faster PC

What delta time actually is, why frame rate isn't a clock, and the exact spots in Unity (and other engines) where multiplying by it helps or quietly breaks your game.

Concepts · By Game Design Jar · · P.05

If you’ve ever seen a character hop around like Tigger on one machine and trudge through mud like Eeyore on another, you’ve found the bug delta time was built to kill. I spent some hair-pulling hours on this early on, thinking my jump physics were broken. Turns out my gaming laptop was just rendering faster than my good ole’ desktop.

So: what delta time is, how it works in Unity, and where it doesn’t apply even though it really looks like it should.

What delta time is

How much time passed since the last frame (or the last physics step), in seconds. Usually a small float — 0.0166 for a 60 FPS frame, 0.0333 for 30 FPS.

In Unity it’s Time.deltaTime. It’s not a constant. It changes every frame depending on how long the last one took to render — heavy scene, GPU stutter, a coroutine doing something it shouldn’t. Slow frame, bigger number. Fast frame, smaller number.

Multiply your movement, timers, and animation by it and “per frame” becomes “per second.” Same result no matter how many frames it took to get there.

The problem it solves: frame rate isn’t a clock

Older games often moved things a fixed amount every frame — 2 pixels per frame, done. Which is fine, right up until someone runs it on hardware that isn’t yours. Then your simulation speed is bolted to their GPU, and a better graphics card makes your platformer harder because gravity got three times stronger. You get what you pay for I guess??

This is the usual explanation for why some old games run at an absurd speed on modern machines: the simulation assumed a frame rate that stopped being true about two decades ago.

Delta time keeps the two things separate. Frame rate is how smooth it looks. Delta time is how much game time passed.

How it works, with a bit of Unity C#

Simplest version, targeting Unity 6.

public class Mover : MonoBehaviour
{
    public float speedUnitsPerSecond = 5f;

    void Update()
    {
        float distanceThisFrame = speedUnitsPerSecond * Time.deltaTime;
        transform.position += transform.forward * distanceThisFrame;
    }
}

speedUnitsPerSecond is a rate. Multiply by Time.deltaTime — the slice of a second this frame covers — and you get a distance for this frame. At 60 FPS you move a little, 60 times a second. At 30 FPS, twice as far, half as often. Same distance per second either way.

That’s the whole thing in a nutshell. Everything below is about where it applies, which can be the trickiest part.

Update vs FixedUpdate — and what deltaTime means in each

Unity gives you two clocks, and they don’t run on the same schedule.

Update() runs once per rendered frame, so Time.deltaTime in there is variable — whatever the last frame took.

FixedUpdate() runs on the physics engine’s schedule, independent of frame rate. Every 0.02 seconds by default, so 50 times a second, adjustable under Project Settings → Time → Fixed Timestep. Inside FixedUpdate(), Time.deltaTime quietly hands you Time.fixedDeltaTime instead — same value every time. Slightly sneaky, but it means physics stays stable while your frame rate does whatever it wants.

The rule: anything touching a Rigidbody or Rigidbody2D goes in FixedUpdate(). Input, or moving a Transform on something that isn’t a physics object, goes in Update(). Nudge a Rigidbody’s position in Update() and you get jitter — you’re moving a physics object on the render clock, and the physics engine will fight you for it. The jitter’s subtle enough that most people blame the camera first.

What it’s called in other engines

Same idea, three different names, because of course.

Godot 4 hands you delta as a parameter: _process(delta) matches Unity’s Update(), _physics_process(delta) matches FixedUpdate() and runs on the fixed tick. If you want to see that split doing real work, the coyote time and jump buffering write-up leans on it.

Unreal has Tick(float DeltaTime), same deal, seconds since the last tick.

I work exclusively in Unity when I work on my games, so treat the Godot and Unreal notes as “this is what the docs say and how it lines up” rather than something I’ve shipped. But it’s the same idea underneath: seconds since the last update, used to turn rates into amounts.

The multiplication trap: rates you scale, values you don’t

“Multiply everything by delta time” is half right, which is the worst amount of right for advice to be.

Multiply when the value is a rate per second — speed, degrees per second, damage per second, a countdown. Those need converting from “per second” to “this frame’s slice.”

Don’t multiply when something’s already doing the time math for you:

  • Rigidbody.AddForce(force, ForceMode.Force) — physics already spreads this over Time.fixedDeltaTime. Multiply it yourself and you’ve done it twice.
  • rigidbody.linearVelocity — velocity is already per-second. The engine moves the object with it every fixed step. Assign it and step away. (It’s velocity before Unity 6, which renamed it. If you’re following an older tutorial, that’s the mismatch waiting for you.)

The check: is this number “how much happens in one second” (scale it) or “a snapshot the engine moves forward on its own” (don’t touch it)? Get it backwards and the bug is maddening — everything looks basically fine, just slightly off, and it stays hidden until you test at a different frame rate. Ask me how I know. Actually don’t.

When to use it

  • Transform movement, rotation, or scaling that isn’t going through physics
  • Countdown timers, cooldowns, ability durations
  • Animation-ish logic you’re driving from script instead of the Animator
  • Anything you want behaving the same at 30, 60, or 144 FPS

When not to: physics, anything that has to match across machines, and lerp smoothing

Physics forces and velocities — covered above. Physics has its own time math. Yours is not helping.

Anything that has to come out identical on two different machines — multiplayer, or replay systems that record inputs and play them back. Those want a fixed step with a fixed delta. With a variable delta, two machines run slightly different numbers, the tiny differences pile up, and eventually the two games disagree about where everything is. Then someone files a bug report about players seeing different things. I haven’t shipped networked game code, so that one’s from reading rather than experience — go deeper than this article if it’s your problem.

Lerp smoothing is the sneaky one:

transform.position = Vector3.Lerp(transform.position, target, speed * Time.deltaTime);

Looks frame-rate independent. Delta time’s right there, doing something. It isn’t, though.

Here’s the catch. Lerp doesn’t move a fixed distance — it moves a percentage of whatever gap is left. Call it every frame and you’re taking a bite out of the remaining distance, over and over. More frames per second means more bites out of a shrinking gap, which is not the same as fewer, bigger bites. It ends up in roughly the same place, but it gets there differently — and how it gets there is the whole point for camera follow, or anything where the motion needs to feel right.

The fix is to work out how much to move based on elapsed time rather than on how many frames happened to fire. Something like this as the third argument:

float t = 1f - Mathf.Exp(-rate * Time.deltaTime);
transform.position = Vector3.Lerp(transform.position, target, t);

You don’t need to follow the math to use it. rate controls how fast it catches up — bigger is snappier — and the point is that the motion feels the same at 30 FPS as it does at 144.

Worth knowing about. I still use the naive one for menu fades, because nobody is benchmarking a menu fade.

A quick check you can run in five minutes

Don’t reason about whether something’s frame-rate independent. Test it — reasoning is how you end up certain your jump physics are broken.

  1. Force a low frame rate. Application.targetFrameRate is a script API, not a Project Settings field, so drop this on anything in the scene:

    void Awake()
    {
        QualitySettings.vSyncCount = 0; // vSync overrides targetFrameRate, because sure
        Application.targetFrameRate = 15;
    }
  2. Play, and watch the specific thing you’re checking.

  3. Same distance, same duration, just choppier? Delta time’s doing its job.

  4. Slower, later, or otherwise a different outcome rather than different smoothness? Something in that path is missing a Time.deltaTime — or has one it shouldn’t.

  5. Delete the test script afterward. Otherwise you ship at 15 FPS and spend an afternoon wondering why the build feels like a slideshow.

That catches most frame-rate bugs before someone on different hardware finds them for you.

delta-timeunitygame-design-theorygame-feeldev-tools

Registration card

Register to receive one focused post every weekday — engine tutorials, marketing tactics, and design craft. Free, unsubscribe anytime.

No spam. We never share your address. No purchase necessary — it's free.