Dev

Why I used raw Canvas instead of Phaser: one engineer's stubbornness

By LeoFeb 26, 2025~1400 words · 6 min
Phaser bundle ~ 1.2 MB first paint > 4s on 4G Native Canvas ~ 8 KB first paint < 0.5s on 4G
For a small-game portal, the difference is orders of magnitude

All 24 games on this site are written in vanilla JavaScript with Canvas — no Phaser, no PixiJS, no CreateJS, no Cocos2d-JS. The choice made Max anxious early on (long development time), but in hindsight it's the right call. Here's why.

Engines are optimized for big projects, not small ones

An unpopular take to start with: the design goal of game engines is not "make small games easier to write."

Mature engines like Phaser are tuned for mid-size 2D games — platformers, RPGs, side-scrolling shooters. They ship scene management, physics, animation state machines, audio pipelines, UI frameworks, tween libraries, camera systems. For a 1024-pixel-wide horizontal-scroller, all of that is a blessing.

But I'm building 2048. A 4x4 grid. Four keyboard events. No physics needed, no scene switching, no camera. Using Phaser means importing a 1.2 MB engine, calling 5% of its API, and shipping the other 95% in the bundle regardless.

For a user who tapped from TikTok on 4G, on a five-year-old Android — that bundle size determines whether they ever play the game.

2 seconds is the H5 game death line

The data point that drove this decision was something I'd observed at a previous job running mini-game promotions: page load over 3 seconds jumps bounce rate from 30% to over 70%. (Numbers vary by category, but the load-time / bounce-rate correlation held across almost every project I touched.)

Which means, as an H5 game developer, first-paint time is your core KPI. More important than "is the game fun." More important than UI polish. More important than SEO. If it loads slowly, everything downstream is wasted.

Native Canvas + inline CSS + inline JS, our Flux 2048 ships at 8 KB total (excluding shared style.css and main.js). On 4G it's visible in 0.4s and playable in 0.6s. Rewrite the same game in Phaser and you're at 4–5s. That gap is 50% of your potential audience.

The real engine cost is cognitive, not performance

Another counterintuitive point: Phaser's runtime is excellent. WebGL pipeline, object pools, tween system — all well-optimized.

So why don't I use it? Because writing a 4x4 grid game in Phaser is 3x more code than writing it in raw DOM.

To use Phaser you need to:

With raw DOM:

The engine adds an abstraction layer. That layer is necessary for big games (it manages state). For small games it's overhead. Across 24 games, about 18 are "grid games" — DOM handles them directly.

When I'd reach for an engine

For the record: I'm not anti-engine. I'm anti "use an engine by default." These scenarios make me grab Phaser or something like it:

None of BverGame's 24 games match any of these. Up to ~100 objects (e.g., Block Drop tiles), trivial physics (Snake grid movement), single scene, solo developer. Native Canvas wins.

The engine choice depends on whether your game is a "grid game." Grid games almost never need one.

Specific techniques for raw Canvas

If you go this route, practical reminders:

1. requestAnimationFrame, always over setInterval. One-liner change, huge gain — runs at 60fps reliably on 60Hz screens. setInterval keeps firing when the tab is unfocused, so users get a sudden state jump when they come back.

2. Separate state from render state. Keep game state (score, board) distinct from render state (sprite positions, animation progress). Each frame: update game state, compute render state from it, draw. This split makes later refactors much easier.

3. Touch before mouse. In 2025, default to "user is on mobile." Make touchstart/touchend solid first; mouse events are nice-to-have.

4. localStorage is your tiny server. Persists without a backend. Plenty for most small games. All our high scores and progress live in localStorage. Zero backend cost.

Closing: tools are tools, not identities

A lot of developers treat "I use Phaser/Unity/Cocos" as part of their identity. I used to. Eventually I realized: tools are tools. Pick what fits.

BverGame's current goal is two people maintaining 24 games, fast first paint, small bundle, no ops. That goal makes raw Canvas the clear winner.

The day we decide to build a real mid-size game (with platforming, physics, story), I'll pick up Phaser without hesitation. At that point, though, BverGame would no longer be BverGame.

Leo is BverGame's co-engineer. Spent 4 years on H5 mini-games at a previous company, working with Phaser, CreateJS, Egret. This piece is reflection on his own tooling choices, not criticism. Phaser, PixiJS, Cocos are all excellent open-source projects.