🌾 FarmHeart

← Back to Blog

Browser Game Performance: How to Hit 60fps on Any Device

PerformanceWebGLGame Dev · 10 min read

Your browser game runs great on your development machine. Then someone opens it on a 2019 Chromebook and it drops to 12fps. Sound familiar? Here's how to build browser games that run smoothly on everything from budget phones to gaming PCs.

Understanding the Performance Budget

At 60fps, you have 16.67 milliseconds per frame. That's time for input processing, game logic updates, physics simulation, and rendering — everything. On a budget device, the GPU is slow and the CPU is fighting 47 Chrome tabs for time.

Target FPSFrame BudgetRealistic After Overhead
60fps16.67ms~12ms (browser uses ~4ms)
30fps33.33ms~28ms
24fps (minimum playable)41.67ms~36ms

Aim for 60fps. Accept 30fps on weak devices. Below 24fps the game feels broken.

The Big Three: Draw Calls, GC, and Overdraw

1. Draw Calls — The GPU Bottleneck

Every time the GPU switches materials, textures, or geometry, that's a draw call. Desktop GPUs handle thousands easily. Mobile GPUs struggle past 200.

Fix: Merge and instance.

2. Garbage Collection — The JavaScript Bottleneck

JavaScript's garbage collector pauses everything when it runs. In a game, that means frame drops — sometimes for 50-100ms. The fix is simple in concept but hard in practice: don't allocate memory in the game loop.

FarmHeart pre-allocates ~50 temporary vectors and quaternions at load time. The game loop creates zero new objects per frame — even particle systems recycle from a fixed pool.

3. Overdraw — The Hidden GPU Killer

Overdraw happens when the GPU draws pixels that get covered by something in front of them. Transparent objects are the worst offenders — they can't be depth-sorted early, so the GPU renders them fully even when invisible behind opaque geometry.

Adaptive Quality

The smartest optimization: detect the device and scale quality accordingly.

const isMobile = navigator.maxTouchPoints > 0; const isLowEnd = navigator.hardwareConcurrency <= 4; if (isMobile || isLowEnd) { renderer.setPixelRatio(1); // skip retina renderer.shadowMap.enabled = false; // shadows are expensive scene.fog.far = 80; // reduce draw distance MAX_PARTICLES = 100; // fewer particles } else { renderer.setPixelRatio(Math.min(devicePixelRatio, 2)); renderer.shadowMap.enabled = true; scene.fog.far = 200; MAX_PARTICLES = 500; }

You can also dynamically adjust quality: if the average frame time exceeds 20ms over the last 60 frames, automatically reduce shadow quality or particle count. Players would rather have smooth gameplay than pretty effects.

Texture Optimization

Textures eat VRAM. A single 4096x4096 texture uses 64MB. On a phone with 2GB total VRAM, that's a problem.

Physics Optimization

If you're using a physics engine (Cannon-es, Rapier, Ammo.js), these rules apply:

Loading and First Paint

A browser game that takes 30 seconds to load is a browser game nobody plays. Target under 5 seconds to first interaction.

Profiling Tools

You can't optimize what you can't measure:

Real-World Results

FarmHeart runs 800+ gameplay features with 3D rendering, weather systems, particle effects, and dynamic lighting on devices ranging from iPhone SE to gaming desktops. The key wasn't any single optimization — it was the discipline of profiling every new feature and keeping the frame budget under control.

Most browser games don't fail because of technical limitations. They fail because developers add features without measuring their cost. Set your frame budget, respect it, and your game will run anywhere.

See It Running

FarmHeart demonstrates all of these techniques in a production game. Open it on your phone to see adaptive quality in action.

Play FarmHeart