60fps vs 120fps — Frame Time Math r/gamedev Keeps Re-asking
Every month someone on r/gamedev asks “how many ms is one frame at 60fps” and five people reply “just divide 1000 by 60” while others argue about VSync, deltaTime, fixed timestep, and whether the question is even meaningful on variable refresh displays. All of them are partially right — here’s the consolidated answer so you can close the tab and go back to profiling.
- Confusing frame time budget with wall-clock frame time — Your game logic budget might be 8ms of a 16.67ms frame; GPU + present eat the rest.
- Using 60fps math on a 120Hz target — 120fps = 8.33ms per frame, not 16.67ms. Halving the budget isn’t optional.
- “30fps is double 60fps ms” — 30fps ≈ 33.33ms, not 33.34 exactly; close enough for design, not for audio sync without drift correction.
- Ignoring fixed vs variable timestep —
Update()withTime.deltaTimebehaves differently from fixed 50Hz physics.
Core formulas
| Operation | Formula | Example @ 60fps |
|---|---|---|
| ms per frame | 1000 / FPS | 16.67ms |
| seconds per frame | 1 / FPS | 0.0167s |
| frames → seconds | frames / FPS | 120 frames = 2s |
| seconds → frames | seconds * FPS | 0.5s = 30 frames |
Quick reference table:
| FPS | ms/frame | sec/frame | Use case |
|---|---|---|---|
| 24 | 41.67 | 0.0417 | Film cadence reference |
| 30 | 33.33 | 0.0333 | Console fallback, mobile save power |
| 60 | 16.67 | 0.0167 | Standard web / Switch docked target |
| 90 | 11.11 | 0.0111 | VR comfort zone for many users |
| 120 | 8.33 | 0.0083 | ProMotion, high-refresh PC |
Unity-specific notes
// Frame time in seconds (variable)
float dt = Time.deltaTime;
// Fixed physics step — often 0.02 (50 Hz), NOT the same as render FPS
float fixedDt = Time.fixedDeltaTime;
If physics runs at 50Hz but you render at 60fps, don’t multiply animation keys by 1/60 when the rig expects fixed steps — match the system you’re tuning.
Target frame rate:
Application.targetFrameRate = 60; // hint only on some platforms
QualitySettings.vSyncCount = 1; // ties to display refresh
VSync ON at 60Hz display ≈ 16.67ms cap. VSync OFF → you might measure 300fps in empty scene but input latency and tearing tradeoffs change.
Web / canvas / requestAnimationFrame
requestAnimationFrame fires at display refresh (often 60 or 120Hz). Your callback should finish before the next paint — budget is roughly 1000/refreshRate ms minus browser chrome and GPU work.
For CSS animations, 16.67ms per step at 60fps is why steps(60) and frame-based sprite sheets use discrete timing — off-by-one frame in a 12-frame walk cycle is visible at 60fps, invisible at 30fps.
Practical profiling workflow
- Identify target FPS (ship criteria, not peak empty-scene FPS).
- Compute budget:
1000 / targetFpsms. - Allocate: logic / physics / render / present — leave headroom (don’t budget 16.0ms of work in a 16.67ms frame).
- Use fps calculator for reverse lookups — “how many frames in 2.5 seconds at 60?”
- Validate on lowest ship device, not dev PC.
Audio sync edge case
Audio buffers often use sample rate (44100 Hz) not frame rate. Converting “3 frames at 60fps” to audio samples requires an extra step — don’t assume frame math applies to DSP without conversion.
Tool: frame ↔ time
The fps calculator converts FPS ↔ ms ↔ frame counts — handy when tuning animation keys, network tick rates, or explaining lag to producers.
TL;DR
ms = 1000 / FPS. 60fps ≈ 16.67ms, 120fps ≈ 8.33ms, 30fps ≈ 33.33ms. Budget is not the same as measured frame time — leave headroom and profile on target hardware.