Fixed-timestep game loop
Run physics in fixed steps and render as often as the screen allows. An accumulator carries leftover time between frames, so a 60 Hz and a 144 Hz monitor simulate the same game. Clamp the frame time so a backgrounded tab doesn't try to catch up on minutes of updates. Phaser, Godot, and Unity do this for you; write it yourself when you're on plain Canvas or a renderer like PixiJS.
const STEP = 1 / 60; // seconds per simulation tick
let accumulator = 0;
let last = performance.now();
function frame(now: number) {
// Clamp so a backgrounded tab doesn't dump seconds of catch-up at once
accumulator += Math.min((now - last) / 1000, 0.25);
last = now;
while (accumulator >= STEP) {
update(STEP); // physics + game logic, always the same dt
accumulator -= STEP;
}
render(accumulator / STEP); // 0..1: blend between last two states
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);Related
Frame-rate independent movement
Speeds should be per second, not per frame. Moving 4 px every frame means a 144 Hz player moves 2.4× faster than a 60 Hz one. Multiply by the elapsed time (delta time) instead. Good enough for simple games; use a fixed timestep when collisions or physics must be consistent.
Keyboard input without stuck keys
Track which keys are held in a Set and read it during update, instead of moving things inside event handlers. Use `e.code` (physical key) so WASD works on any keyboard layout. Clear the set on blur: if the player alt-tabs while holding a key, keyup never fires and the character walks forever.