Skip to content

From canvas to WebGPU: how the browser became a game platform ​

When Flash ended, browser games didn't need a replacement plugin. The browser had slowly become the plugin. Over about fifteen years, a handful of open APIs turned a document viewer into something you can ship a game on.

This post walks through those APIs in the order they arrived, and what each one changed for people making games. (For the plugin era before this, see Flash games and the portal era.)

A short timeline ​

  1. 2004Apple adds the canvas element to WebKit, first for Dashboard widgets and Safari. Other browsers follow, and it becomes part of HTML5.
  2. 2010Mozilla proposes requestAnimationFrame, a way to draw in step with the display instead of guessing with timers.
  3. 2011Khronos releases WebGL 1.0, bringing OpenGL ES 2.0-style GPU rendering to the page.
  4. 2013Mozilla introduces asm.js. Epic and Mozilla show Unreal Engine 3 running in Firefox with no plugin.
  5. 2014The W3C publishes HTML5 as a Recommendation.
  6. 2017WebAssembly ships in all major browsers. WebGL 2, based on OpenGL ES 3.0, ships in Chrome and Firefox.
  7. 2021Safari 15 ships WebGL 2, so it finally works in every major browser.
  8. 2023Chrome 113 ships WebGPU, a modern GPU API designed for the web.

Sources: Canvas element, requestAnimationFrame on MDN, WebGL, asm.js, HTML5, WebAssembly, WebGPU.

Canvas: somewhere to draw ​

Before canvas, a "game" in plain HTML meant moving <div>s and images around the page. It worked for card games and not much else. Canvas gave pages a bitmap you could draw into with JavaScript: rectangles, images, text, all redrawn every frame.

That's the model 2D games still use. Canvas 2D is also still the safest fallback: it runs on every browser and every device, which is why debuff keeps a Canvas 2D renderer behind its WebGL2 one.

requestAnimationFrame: drawing on time ​

Early canvas games ran their loop on setInterval, picking a number like 16 ms and hoping. Timers drift, keep running in background tabs, and don't know when the screen actually refreshes. requestAnimationFrame asked the browser to call you right before the next repaint, and to pause when the tab is hidden.

It sounds small. It's the reason a browser game can feel smooth at all, and it's also why frame pacing is still worth measuring. That's the job of myaku.

WebGL: the GPU arrives ​

Canvas 2D is drawn mostly by the browser, and it gets slow when you push thousands of sprites. WebGL gave JavaScript direct access to the GPU through shaders and vertex buffers. Suddenly a browser could draw tens of thousands of sprites, run post-processing, or render 3D.

Libraries grew up around it. PixiJS and Phaser both arrived in 2013 and made WebGL practical for 2D games without writing shaders by hand (PixiJS, Phaser). WebGL 2 later added features like instancing and more texture formats as standard, but it took until 2021 for Safari to support it, so many engines kept a WebGL 1 path for years.

Sound and input ​

Games need more than pixels. The <audio> element was built for music players and had poor timing for sound effects. The Web Audio API gave games a proper mixing graph with precise scheduling (MDN). The Gamepad API let a page read controllers (MDN), and the Pointer Events API unified mouse, pen and touch.

One quirk from this era is still with us: browsers won't play sound until the player interacts with the page. That's why so many web games start with "click to play".

asm.js and WebAssembly: other languages come in ​

In 2013, Mozilla showed that a strict subset of JavaScript, asm.js, could be compiled from C and C++ and run close to native speed. The demo that made people pay attention was Unreal Engine 3 running in Firefox (source).

WebAssembly turned that idea into a proper binary format, and it shipped in every major browser in 2017. Engines like Unity and Godot export to the web through it, and so does Ruffle, the emulator that keeps Flash games playable. The trade-off is size: a big engine compiled to WebAssembly is often tens of megabytes before the game itself, which matters a lot for instant play.

WebGPU: the next GPU API ​

WebGL is based on OpenGL ES, an API designed long before today's GPUs. WebGPU is designed around modern native APIs like Vulkan, Metal and Direct3D 12, and adds compute shaders. Chrome shipped it in 2023, and other browsers have been following (source).

For most 2D games, WebGL2 is still plenty, and it's supported almost everywhere. WebGPU matters most for heavy 3D and GPU compute, at least for now.

What it adds up to ​

Each of these APIs removed a reason to need a plugin or an install:

  • Canvas and WebGL for drawing.
  • requestAnimationFrame for smooth timing.
  • Web Audio, Gamepad and Pointer Events for sound and input.
  • WebAssembly for code written in other languages.

The result is a platform that runs on almost every phone and computer, opened from a link. The thing it doesn't give you for free is a small download. That part is still up to the engine.

Where debuff fits ​

debuff targets the most widely supported layer of this stack: WebGL2 with a Canvas 2D fallback, plain TypeScript, and no WebAssembly runtime to download. The starter game ships at about 19 kB brotli. On top of that it adds the things the platform doesn't: deterministic tests, replays, live-tunable params, and a coding agent that can play the game with you.

bash
npm create debuff@latest my-game

Or read more history: Flash games and the portal era and game jams and tiny games.