Lesson 13 of 22, in world 4: Visuals & Worlds
Cameras & Scrolling
Once your level is bigger than the screen, you need a camera: a movable window onto the world. In 2D, a camera is shockingly simple — it’s just an offset subtracted from everything you draw.
World space vs. screen space
Objects live in world space (the player is at x = 1800 in a 2400-wide level). The screen only shows 640 pixels. The camera has a position too, and screen = world − camera. Moving the camera right makes everything on screen slide left.
function render() {
ctx.save()
ctx.translate(-camera.x, -camera.y) // apply the camera (lesson 4: transforms!)
drawLevel(); drawPlayer(); drawEnemies() // all in world coordinates
ctx.restore()
drawHUD() // score, health: screen coordinates, not affected
}Fly around a world that is 2400×1200 px — much larger than the 640×360 screen — with arrow keys / WASD. The minimap shows the whole world and the camera's view rectangle. Try smoothing 0 (rigid), the dead zone, and parallax.
Smooth following with lerp
A camera glued to the player feels stiff. Instead, move it a fraction of the remaining distance each frame — linear interpolation (lerp). It moves fast when far away and eases in as it arrives.
const target = { x: player.x - SCREEN_W / 2, y: player.y - SCREEN_H / 2 }
const t = 1 - Math.exp(-SMOOTHING * dt) // frame-rate independent (lesson 3)
camera.x += (target.x - camera.x) * t
camera.y += (target.y - camera.y) * tMore camera techniques
- Dead zone — the camera only moves when the player leaves a box in the middle. Small movements don’t shake the view.
- Clamping — stop at the level edges so the player never sees the void outside.
- Look-ahead — offset the camera in the direction of movement so the player sees what’s coming.
- Parallax — background layers scroll slower (multiply camera by 0.1–0.5), creating a sense of depth.
- Screen shake — briefly add a random offset (lesson 19).
Key takeaways
- screen = world − camera. A 2D camera is a translate before drawing the world.
- Draw HUD after restoring the transform so it stays in screen space.
- Smooth follow with frame-rate-independent lerp; add dead zones, clamping, parallax.
Check yourself
Q1. Camera at (500, 200). An enemy at world (620, 260) is drawn on screen at…
Q2. You click at screen (100, 100) with camera at (800, 0). Which world point did you click?
Q3. Why do far-away background layers scroll slower?
Exercises
- Turn clamping off and fly to a corner. What do you see? Why do most games clamp?
- Implement look-ahead: target = player + velocity × 0.3. Try it in your head with the dead zone — do they conflict?
- Add zoom: scale(z, z) around the screen centre. Where in the transform order does it go?