The device that matters

Develop on a fast machine and everything is fine. The device that determines whether your game is good is a mid-range phone a few years old, which is several times slower, has less memory, throttles under heat and is running a browser with less headroom.

The practical implication is that performance work should be targeted at that device rather than at a profiler on a desktop. A game that runs at sixty frames on a laptop and thirty on a phone has a phone problem, and the phone is where most of your players are.

Ranked by impact

OptimisationTypical impactEffort
Cap device pixel ratio at 2Very largeTwo lines
Pool objects instead of allocatingLarge - removes stutterSmall
Cap particle counts globallyLarge under loadSmall
Cull off-screen entitiesModerate to large in scrolling gamesSmall
Avoid allocation in the draw loopModerate - removes stutterDiscipline
Draw rather than load imagesModerate - removes decode and memoryDesign decision
Pause when the tab is hiddenBattery, not frame rateThree lines
Micro-optimising mathsNegligibleWasted

The top four account for nearly all of the difference in our experience. The last row is there because it is where a lot of optimisation effort goes and it almost never matters in a canvas game.

Advertising keeps Candy Arcade free. Ads never cover gameplay or controls.

Device pixel ratio is the big one

A 3x display renders nine times the pixels of a 1x display. On a phone screen held at arm's length, the difference between 2x and 3x is barely visible and the cost is enormous.

Capping at 2 is the single largest performance lever available in a canvas game, and it is two lines: take the minimum of devicePixelRatio and 2, and apply that as both the buffer multiplier and the context transform scale.

Stutter is not slowness

These feel similar and have different causes. Consistently low frame rate means you are drawing too much. Periodic hitching in an otherwise smooth game almost always means garbage collection.

Garbage comes from allocation: creating objects, arrays, strings or closures during play. A collection game that allocates one object per collectible spawn generates hundreds per minute, and on a phone the collector runs often enough to be felt.

The fix is pooling. Keep discarded objects in a free list and reuse them. Our pool holds up to 256 spares and the active list is compacted once per frame rather than spliced during iteration.

Splicing an array during iteration is both a correctness hazard and a source of allocation. Mark dead, sweep once, reuse.

Cap what can combine

Particles are the classic case. A single burst is cheap; twenty simultaneous bursts are not, and pickups do cluster.

Cap globally rather than per emitter, so simultaneous bursts cannot combine into a frame drop, and drop excess rather than queueing it. A burst slightly smaller than intended is imperceptible; a dropped frame is not.

Our cap is 220 particles with lifetimes under a second, which means the system clears itself rather than accumulating across a long run.

Procedural art as a performance strategy

Drawing everything from code rather than loading images removes several mobile-specific costs at once: image decode time on a slower CPU, texture memory on a device with less of it, and the need to ship multiple resolution variants.

It also removes the loading state entirely, which matters for a different reason: a browser game competes with everything else on the device for attention, and a game that starts playing in under a second keeps people who would otherwise leave.

The trade is CPU per frame. At the visual complexity an arcade game needs, that is comfortably affordable.

Pause when hidden

A game running in a background tab drains battery for nothing, and browsers throttle background timers unpredictably, so the simulation state becomes unreliable anyway.

requestAnimationFrame already pauses when a tab is hidden, but game state needs to handle it explicitly: our engine listens for visibilitychange and blur, pauses the run, and shows a message explaining that nothing was lost. Without that, returning to a tab can resume a run the player has mentally abandoned.

Measure at the worst moment

The most common profiling error is measuring the calm opening of a game. Frame rate matters during the densest section, when particle bursts are combining and entity counts are highest.

Practical method: play to the point where the game is busiest, and watch the frame time there. If it holds under sixteen milliseconds on a mid-range phone during the worst moment, it will hold everywhere.

Common mistakes

  • Profiling on a desktop onlyThe device that determines whether your game is good is a mid-range phone, which is several times slower.
  • Leaving device pixel ratio uncappedA 3x display costs nine times the pixels of 1x for barely visible benefit.
  • Allocating during playGarbage collection stutter is the most common mobile performance problem and it is invisible on desktop.
  • Capping particles per emitterBursts combine. The cap has to be global.
  • Measuring the calm openingFrame rate matters during the densest section, not the first ten seconds.

Practical tips

  • If a browser game hitches periodically rather than running slowly, it is allocating during play.
  • Reduced motion lowers particle counts, which also helps on older devices.
  • Games here pause automatically when you switch tabs, so nothing is lost.
  • Instant starts are a consequence of having no assets to download.
  • Judge a game's performance during its busiest moment rather than its opening.

Key takeaways

  • Target a mid-range phone, not a development machine.
  • Capping device pixel ratio at 2 is the largest single lever.
  • Stutter means allocation; low frame rate means draw cost. They have different fixes.
  • Cap combinable effects globally and drop excess rather than queueing.
  • Procedural art removes decode time, texture memory and resolution variants.
  • Measure during the densest moment of the game.

Frequently Asked Questions

Why do some browser games stutter on phones but not on desktop?

Almost always garbage collection from allocating objects during play. Desktops have enough headroom to hide it; phones do not.

What is the single biggest performance win in a canvas game?

Capping device pixel ratio at 2. A 3x display renders nine times the pixels of 1x for barely visible benefit.

Why do Candy Arcade games start instantly?

Because there are no assets to download or decode. All artwork is drawn from code and all audio is synthesised.

Do the games keep running if I switch tabs?

No. They pause automatically and tell you so, because running in the background drains battery and browsers throttle background timers unpredictably.

How should I judge a browser game's performance?

During its densest moment rather than its opening, on a mid-range phone rather than a fast computer.

References

  1. Optimizing canvas - MDN Web Docs — Canvas-specific performance guidance.

Every external source listed here has been read. We do not cite sources we have not checked, and we do not invent references. See our fact-checking policy.