Game Loop
The game loop is the heartbeat of your game, updating logic and rendering frames continuously.
Live Demo
See the game loop in action with this space shooter - the update cycle handles player movement, bullet physics, enemy AI, and collision detection every frame:
🎮Space Shooter - Game Loop in Action
Loading demo...
How It Works
GameByte's game loop:
- Calculates delta time (time since last frame)
- Calls
update(deltaTime)on the active scene - Updates physics (if enabled)
- Updates animations
- Renders the frame
- Repeats via
requestAnimationFrame
┌─────────────────────────────────────┐
│ Game Loop │
│ ┌─────────────────────────────────┐│
│ │ 1. Calculate deltaTime ││
│ │ 2. Scene.update(deltaTime) ││
│ │ 3. Physics.update(deltaTime) ││
│ │ 4. Animations.update(deltaTime) ││
│ │ 5. Renderer.render() ││
│ │ 6. requestAnimationFrame(loop) ││
│ └─────────────────────────────────┘│
│ ↻ 60 times/second │
└─────────────────────────────────────┘
Starting and Stopping
const game = createGame();
await game.initialize(canvas, '2d');
// Start the loop
game.start();
// Pause (stops updates, keeps rendering)
game.pause();
// Resume
game.resume();
// Completely stop
game.stop();
Delta Time
Delta time is the time elapsed since the last frame, measured in seconds.
| FPS | Delta Time |
|---|---|
| 60 | 0.0167s |
| 30 | 0.0333s |
| 120 | 0.0083s |
Frame-Independent Movement
Always multiply movement by delta time:
// Bad: Speed varies with frame rate
update() {
this.x += 5; // 300 pixels/sec at 60fps, 150 at 30fps
}
// Good: Consistent speed at any frame rate
update(deltaTime: number) {
const speed = 300; // pixels per second
this.x += speed * deltaTime; // Always 300 pixels/sec
}
Example: Moving Player
class Player {
x = 0;
y = 0;
speed = 200; // pixels per second
update(deltaTime: number, keys: { left: boolean; right: boolean }) {
if (keys.left) {
this.x -= this.speed * deltaTime;
}
if (keys.right) {
this.x += this.speed * deltaTime;
}
}
}
Scene Update
Your scene's update method receives delta time:
class GameScene extends BaseScene {
private player: Player;
private enemies: Enemy[] = [];
update(deltaTime: number): void {
super.update(deltaTime); // Important: call parent
// Update player
this.player.update(deltaTime);
// Update all enemies
this.enemies.forEach(enemy => enemy.update(deltaTime));
// Check collisions
this.checkCollisions();
}
}
Fixed Timestep (Physics)
For physics simulations, use fixed timestep to ensure consistent behavior:
import { Physics } from '@gamebyte/framework';
// Physics automatically uses fixed timestep (1/60)
Physics.create2DWorld({
gravity: { x: 0, y: 9.8 },
timestep: 1/60 // Fixed 60 updates per second
});
Why Fixed Timestep?
Variable delta time can cause physics instability:
// Problem: Large delta time = objects pass through walls
// Frame drop: deltaTime = 0.5s, velocity = 1000
// Movement = 500 pixels in one frame → misses collision
// Solution: Fixed timestep with accumulator (handled internally)
Performance Monitoring
GameByte tracks performance metrics:
const game = createGame();
// Enable performance monitoring
game.setConfig({ debug: { showFPS: true } });
// Access metrics
game.on('tick', (metrics) => {
console.log(`FPS: ${metrics.fps}`);
console.log(`Frame time: ${metrics.frameTime}ms`);
console.log(`Update time: ${metrics.updateTime}ms`);
console.log(`Render time: ${metrics.renderTime}ms`);
});
Target Frame Rate
// Set target FPS (default: 60)
game.setConfig({ targetFPS: 60 });
// For mobile battery savings
game.setConfig({ targetFPS: 30 });
// Uncapped (not recommended)
game.setConfig({ targetFPS: 0 });
Common Patterns
Time-Based Events
class GameScene extends BaseScene {
private spawnTimer = 0;
private spawnInterval = 2; // seconds
update(deltaTime: number): void {
super.update(deltaTime);
this.spawnTimer += deltaTime;
if (this.spawnTimer >= this.spawnInterval) {
this.spawnEnemy();
this.spawnTimer = 0;
}
}
}
Smooth Interpolation
// Smooth camera follow
class Camera {
x = 0;
y = 0;
smoothing = 5; // Higher = faster
update(deltaTime: number, targetX: number, targetY: number) {
// Lerp towards target
this.x += (targetX - this.x) * this.smoothing * deltaTime;
this.y += (targetY - this.y) * this.smoothing * deltaTime;
}
}
Animation Timing
class AnimatedSprite {
private frameTime = 0;
private frameIndex = 0;
private frameDuration = 0.1; // 100ms per frame
update(deltaTime: number): void {
this.frameTime += deltaTime;
if (this.frameTime >= this.frameDuration) {
this.frameIndex = (this.frameIndex + 1) % this.frames.length;
this.frameTime = 0;
}
}
}
Best Practices
- Always use deltaTime for movement and timers
- Call
super.update(deltaTime)in scene updates - Keep update() lightweight - heavy work in separate systems
- Profile regularly - watch for frame time spikes
- Consider mobile - target 30fps for battery life