# How to make a WebGPU browser game with a WebGL2 fallback

> Net Forge draws with three.js's WebGPURenderer, which uses WebGPU where the browser has it and WebGL 2 everywhere else, and wraps it in a render stack with lighting, post effects, GPU particles and quality tiers that adapt to the device.

Net Forge, the open-source multiplayer browser game engine built for AI coding agents, is published on npm under `@netforge` with source at https://github.com/netnetdev/net-forge.

Source: https://forge.netnet.capital/docs/guides/webgpu-browser-game  
Last updated: 2026-10-23. Checked against @netforge/engine 0.2.0.

A WebGPU browser game that still runs everywhere needs one renderer with two backends. Net Forge's runtime uses three.js's `WebGPURenderer`, which picks WebGPU when the browser supports it and falls back to a WebGL 2 backend otherwise, so one code path covers every browser. Every template already draws this way:

```sh
npm create netforge@latest my-game -- --template survivor --yes
cd my-game
npm run play
```

The three.js docs describe the fallback directly: "By default, the renderer tries to use a WebGPU backend if the browser supports WebGPU. If not, `WebGPURenderer` falls backs to a WebGL 2 backend." ([three.js WebGPURenderer](https://threejs.org/docs/pages/WebGPURenderer.html))

## What the runtime gives you

| Part | What it does |
|---|---|
| `createRenderer(canvas, { forceWebGL })` | Creates the `WebGPURenderer`, waits for `init()` and reports which backend it got (`"webgpu"` or `"webgl2"`) |
| `createRenderStack(handle, scene, camera, options)` | Lighting with a shadow that follows the focus, fog and sky, bloom, colour grade, vignette, FXAA, GPU particles, ground decals and rings |
| `QualityManager` | Quality tiers `low`, `medium` and `high`, detected from the device, plus dynamic resolution held to the frame budget |
| `GpuParticles` | Every particle preset in one draw, with motion on the GPU |
| `AnimatedCrowd`, `VatCrowd` | Thousands of animated instances in one draw (procedural motion, or skinned clips baked to a vertex-animation texture) |
| Art styles | Six style kits (`lowpoly`, `toon`, `ps1`, `pixel`, `voxel`, `handdrawn`) that the stack applies by name |

## 1. Create the renderer and the stack

This is the setup a template runs. It typechecks against `@netforge/engine/runtime`; your project needs `three` installed next to it for the scene.

```ts title=render.ts
import { CameraRig, createRenderer, createRenderStack } from "@netforge/engine/runtime";

type Scene = Parameters<typeof createRenderStack>[1];

export async function startRendering(canvas: HTMLCanvasElement, scene: Scene, focus: () => { x: number; y: number; z: number }): Promise<void> {
  // ?webgl=1 forces the WebGL 2 backend, handy for testing the fallback.
  const handle = await createRenderer(canvas, { forceWebGL: new URLSearchParams(location.search).has("webgl") });
  console.info(`drawing with ${handle.backend}`);

  const rig = new CameraRig(canvas.clientWidth / canvas.clientHeight);
  const stack = createRenderStack(handle, scene, rig.camera, { preset: "dusk", quality: "auto", bloom: true, style: "toon" });

  let last = performance.now();
  handle.renderer.setAnimationLoop((now: number) => {
    const dt = (now - last) / 1000;
    last = now;
    if (stack.frame(dt, focus())) stack.resize(canvas.clientWidth, canvas.clientHeight);
  });
}
```

`stack.frame(dt, focus)` replaces `renderer.render(scene, camera)`. It advances effect time, moves the shadow to the focus, steps dynamic resolution and draws. It returns true when the pixel ratio changed, which is the moment to resize.

## 2. Declare the look from game code

In a Net Forge game you do not touch the renderer at all. `src/game.ts` declares the look, and the template's render stack draws it:

```ts title=src/game.ts
import { game } from "@netforge/engine";

export default game({
  template: "survivor",
  style: "lowpoly",
  places: { field: { chunks: "places/field", spawn: [0, 1, 0] } },
  players: { prefab: "style.lowpoly.hooded_adventurer", max: 4, weapon: "ember_nova" },
  look: { preset: "dusk", bloom: 1.3, fog: 0.4, vignette: 0.4, tint: "#ffd9a0" },
});
```

`preset` is `night`, `dusk`, `day` or `studio`. `bloom` runs from 0 to 3, `fog` from 0 to 1, and unknown values fail with a message. Slow devices drop shadows, post and resolution by themselves, so turning effects on does not break the frame rate.

## 3. Warm up the pipelines

WebGPU builds render pipelines on first use. Draw one of everything out of sight while the game loads (the survivor template's scene does this in its `warmup` step), so the first explosion does not hitch.

## How it works

`createRenderer` asks three.js for a `WebGPURenderer` with `forceWebGL` off by default. After `await renderer.init()`, three.js has chosen WebGPU when `navigator.gpu` yields an adapter and WebGL 2 otherwise, and the runtime reports which. Materials are three.js node materials, which compile to WGSL on WebGPU and GLSL on WebGL 2, so the same scene, instancing and post chain run on both. The quality tier comes from device hints (a coarse pointer or mobile user agent, core count, memory), and dynamic resolution lowers the pixel ratio when frames run long.

You can check it yourself in the engine demo: `?webgl=1` forces the WebGL 2 backend and `?enemies=2000` stresses the crowd.

## Alternatives

- **[three.js](https://threejs.org/docs/pages/WebGPURenderer.html)** on its own, with `WebGPURenderer`, gives you the same backend selection. Pick it when you want to build your own render pipeline.
- **[Babylon.js](https://doc.babylonjs.com/setup/support/webGPU)** has a full WebGPU engine alongside its WebGL engine, with an editor and a large feature set. Pick it when you want a complete 3D engine with official tooling.
- **[PlayCanvas](https://developer.playcanvas.com/)** is a WebGL and WebGPU engine with a collaborative browser-based editor. Pick it when your team wants to build scenes in an editor.

Pick Net Forge when you want the WebGPU and WebGL 2 rendering already tuned for multiplayer games, with a simulation and netcode underneath it.

## FAQ

### Which browsers support WebGPU?

Support depends on the browser, version and platform, and changes quickly. Because Net Forge falls back to WebGL 2 automatically, a game does not need to check: it runs on either backend.

### How do I test the WebGL 2 fallback on a browser that has WebGPU?

Pass `forceWebGL: true` to `createRenderer`. The action templates and the engine demo map `?webgl=1` in the URL to it.

### Do I need to write WGSL shaders?

No. three.js node materials compile to WGSL for WebGPU and GLSL for WebGL 2. Net Forge's effects are written once as node materials.

### Is WebGPU faster than WebGL 2 for games?

It can be, mainly by lowering CPU cost per draw call and enabling GPU compute. Net Forge keeps draw counts low on both backends with instanced crowds and one-draw particles, so games run well on WebGL 2 too.

### Does it run on phones?

Yes. The quality tier detects mobile devices and starts lower, and dynamic resolution holds the frame budget. Every template has touch controls.
