gameLoop.js

GameLoop

Fixed-timestep game loop. The heartbeat of every DN game.

GameLoop

The foundation of every DN game. Runs a fixed-timestep update loop with a separate render callback — ensuring consistent physics and game logic regardless of the device’s frame rate.

The Problem It Solves

requestAnimationFrame fires at the monitor’s refresh rate — 60hz, 90hz, 144hz, or whatever the device supports. If your game logic runs once per frame, it literally runs faster on high-refresh displays.

GameLoop solves this by accumulating elapsed time and running the update in fixed-size chunks (dt = 1 / fps). The render fires only when an update occurred, preventing wasted draw calls on frames where nothing changed.

Include

<script src="/js/dn/gameLoop.js"></script>

Constructor

new GameLoop({
  fps:            60,           // target update rate (default: 60)
  updateCallback: (dt) => {},   // called each fixed step; receives dt in seconds
  renderCallback: ()   => {},   // called after any update in a frame
})

Both callbacks are optional, but a loop with neither does nothing useful.

Methods

MethodDescription
startLoop()Start the loop. Sets up the first requestAnimationFrame.
stopLoop()Cancel the current requestAnimationFrame. Call to pause.

Delta Time (dt)

dt is always 1 / fps — seconds per update step. Use it to make movement and physics framerate-independent:

// ✅ Correct — moves at `speed` pixels per second, always
player.x += player.speed * dt;

// ❌ Wrong — moves faster on higher framerates
player.x += player.speed;

Full Example

// Dependencies: mathUtils.js (for Vector2D)
const canvas  = document.getElementById('game');
const context = canvas.getContext('2d');

let ballPos = new Vector2D(canvas.width / 2, canvas.height / 2);
let ballVel = new Vector2D(150, 100); // pixels per second

function update(dt) {
  ballPos.x += ballVel.x * dt;
  ballPos.y += ballVel.y * dt;

  // Bounce off walls
  if (ballPos.x < 0 || ballPos.x > canvas.width)  ballVel.x *= -1;
  if (ballPos.y < 0 || ballPos.y > canvas.height) ballVel.y *= -1;
}

function render() {
  context.clearRect(0, 0, canvas.width, canvas.height);
  context.fillStyle = '#4bb8d5';
  context.beginPath();
  context.arc(ballPos.x, ballPos.y, 10, 0, Math.PI * 2);
  context.fill();
}

const loop = new GameLoop({ fps: 60, updateCallback: update, renderCallback: render });
loop.startLoop();

Used In

  • Every game in the DN Arcade
  • The homepage orbit animation

Source

public/js/dn/gameLoop.js