📅 June 15, 2026✍️ Sam Chen🏷️ Technical⏱️ 5 min read
80% of our traffic comes from mobile devices. Not iPhones — Android phones from brands like Xiaomi, Oppo, and Realme that cost $50-150. These phones have limited GPU power, constrained memory, and often run Chrome on Android Go. Here's how we make every game run smoothly on them.
The 16ms Rule
To achieve 60fps, each frame must render in 16.7 milliseconds or less. On a flagship phone that's easy. On a $50 Android, you have about half the GPU budget. We optimized by: reducing canvas draw calls (batch fills instead of individual rects), avoiding shadow blur (expensive), minimizing text rendering (cache to offscreen canvas), and capping particle effects at 50 visible particles.
The most impactful change: don't clear and redraw the full canvas every frame. For games with static backgrounds, draw the background once to an offscreen canvas, then only redraw the moving elements.
Memory Management on Low-End Devices
The biggest performance killer on $50 Android phones is garbage collection. JavaScript's automatic memory management pauses execution when it cleans up unused objects. On a flagship phone with 12GB RAM, these pauses last 1-3 milliseconds — imperceptible. On a budget phone with 2GB RAM, the same pauses can last 50-100ms, causing visible frame skips.
Our solution: object pooling. Instead of creating new objects for each game entity and letting the garbage collector dispose of them, we pre-allocate a fixed pool of objects and reuse them. In Snake Arena, the snake segments are pooled. When the snake eats food and grows, we activate a pool segment. When the game resets, we deactivate all segments back to the pool. Zero garbage collection in the main game loop. This single optimization increased frame rate on low-end devices from 28fps to 58fps.
We also avoid allocating strings in the game loop. String concatenation creates new string objects, triggering garbage collection. Every score display update used to create a new string every 16ms. We moved the HUD rendering to a separate canvas that only redraws when the score actually changes, reducing per-frame string allocations from 3-5 to zero. These seem like micro-optimizations, but their cumulative effect on budget hardware is the difference between a playable game and an unplayable one.