The camera is the player's eye into your game world. A bad camera makes a great game feel frustrating. A good camera is invisible — the player never thinks about it because it always shows them exactly what they need to see. Here's how to build camera systems for browser games that feel polished and professional.
Every 2D camera boils down to one idea: before rendering, translate the entire canvas by the negative of the camera's position. If the camera is at (500, 300), shift everything left by 500 and up by 300. The player character stays near the center of the screen while the world scrolls around them.
In Canvas 2D, this is a single ctx.translate(-cam.x, -cam.y) before your draw calls. In WebGL/Three.js, it's the view matrix. The concept is identical — only the API changes.
The most common camera in 2D games follows the player. But snapping the camera directly to the player's position every frame feels robotic. Instead, use linear interpolation (lerp) to ease the camera toward the target:
cam.x += (target.x - cam.x) * smoothing * dt;
A smoothing factor of 3–5 gives a comfortable, slightly lazy follow. Higher values feel snappier. Lower values feel floaty and cinematic. Let players adjust this if your game supports it.
dt (delta time). Without it, the camera moves faster at higher frame rates. Use 1 - Math.pow(1 - smoothing, dt * 60) for frame-rate-independent exponential smoothing.A dead zone is a rectangle in the center of the screen where the player can move without the camera following. The camera only moves when the player exits the dead zone. This prevents the camera from jittering during small movements (like idle animations or minor position adjustments).
For platformers and action games, combine the dead zone with look-ahead: offset the camera in the direction the player is moving. If they're running right, the camera shifts right to show more of what's ahead. The shift amount should lerp to avoid sudden jumps when the player reverses direction.
Without bounds, the camera will happily show the void beyond the edge of your world. Clamp the camera position so it never reveals empty space:
Zoom is a scale transform applied before the offset. Zoom toward the mouse cursor (or pinch center on mobile) so the point under the cursor stays fixed. The math: convert the cursor to world coordinates, apply the new zoom, convert back, and adjust the camera offset by the difference.
Smooth zoom with lerp, just like position. Clamp zoom to a reasonable range (0.5x to 3x for most games). And always use requestAnimationFrame for smooth transitions — never setTimeout.
Screen shake adds impact to explosions, hits, and dramatic moments. The technique: add a random offset to the camera position each frame during the shake duration, with the amplitude decaying over time.
For a polished shake:
Math.random() for smoother, more natural movementamplitude * Math.pow(0.9, elapsed / duration)Cutscenes, dialogue, and dramatic reveals need scripted camera movement. Build a simple timeline system:
{ time: 0, target: {x: 100, y: 200}, zoom: 1 }FarmHeart uses this for story moments — the camera pulls back to show the full farm during chapter transitions, then smoothly zooms back to the player.
Isometric games use an orthographic projection rotated 45° horizontally and ~30° vertically. The "camera" isn't a perspective frustum — it's a skewed orthographic view. Mouse picking (converting screen coordinates to world tiles) requires the inverse of the isometric transform matrix.
For 3D browser games using Three.js or Babylon.js, orbit cameras (click-drag to rotate around a focus point) are the standard for strategy and simulation games. The orbit camera needs:
Touch controls change camera behavior significantly:
FarmHeart's camera system smoothly follows, zooms, and pans across a detailed 3D farm — right in your browser.
Play FarmHeart Now