Skip to content

WebGPU (WebGPUContext3D)

The WebGPU context provides GPU-accelerated 3D rendering for Ripl, replacing the Canvas 2D painter's algorithm in @ripl/3d with a true WebGPU rasterization pipeline. It features hardware depth testing, WGSL shaders with Lambertian diffuse lighting, and 4× MSAA anti-aliasing. All existing Shape3D elements work without modification — the rendering path is selected automatically based on the context type.

NOTE

WebGPU requires a compatible browser (Chrome 113+, Edge 113+, Firefox Nightly). The existing Canvas Context3D remains available as a fallback.

Demo

Installation

bash
npm install @ripl/webgpu @ripl/3d

Usage

Import createContext from @ripl/webgpu instead of @ripl/3d. The factory is async because it must negotiate a GPU adapter and device:

ts
import {
    createContext,
} from '@ripl/webgpu';

import {
    createCamera,
    createCube,
} from '@ripl/3d';

import {
    createRenderer,
    createScene,
} from '@ripl/web';

const context = await createContext('#app');
const scene = createScene(context);
const renderer = createRenderer(scene);

const camera = createCamera(scene, {
    position: [0, 2, 5],
    target: [0, 0, 0],
});

const cube = createCube({
    size: 1,
    fill: '#4488ff',
});

scene.add(cube);
camera.flush();

Options

OptionTypeDefaultDescription
fovnumber60Field of view in degrees
nearnumber0.1Near clipping plane
farnumber1000Far clipping plane
lightDirectionVector3[-0.577, -0.577, -0.577]Light direction vector
lightMode'world' | 'camera''world'Whether light is in world or camera space
sampleCountnumber4MSAA sample count (1 to disable)
clearColor[r, g, b, a][0, 0, 0, 0]Background clear colour (0–1 range)

API

Camera & Projection

ts
context.setCamera(eye, target, up);
context.setPerspective(fov, near, far);
context.setOrthographic(left, right, bottom, top, near, far);

Properties

ts
context.viewMatrix; // Matrix4
context.projectionMatrix; // Matrix4
context.viewProjectionMatrix; // Matrix4
context.lightDirection; // Vector3 (read/write)
context.lightMode; // 'world' | 'camera' (read/write)

Projection

ts
const [screenX, screenY, depth] = context.project([x, y, z]);

How It Works

  1. During each render frame, Shape3D elements detect the WebGPU context and call submitMesh() with raw vertex/index data instead of populating a face buffer
  2. The context accumulates all mesh submissions into GPU staging buffers
  3. At markRenderEnd(), the context writes uniform buffers (view-projection matrix, light direction), uploads geometry, and issues a single render pass with depth testing enabled
  4. Hit testing remains CPU-side using an offscreen CanvasRenderingContext2D with projected 2D paths

When to Use WebGPU

WebGPU is the best choice when:

  • Complex 3D scenes — Hardware depth testing handles intersecting geometry correctly
  • Large meshes — GPU-accelerated rendering is significantly faster than software rasterization
  • Visual quality — 4× MSAA anti-aliasing produces smooth edges

WebGPU is less ideal when:

  • You need broad browser support (Chrome 113+, Edge 113+, Firefox Nightly only)
  • Your scene is simple enough that @ripl/3d Canvas rendering is sufficient
  • You need to run in Node.js or headless environments

Migration from Context3D

Migrating an existing @ripl/3d application to use WebGPU requires minimal changes.

1. Install the package

bash
npm install @ripl/webgpu

2. Swap the context import

Before:

ts
import {
    createContext,
} from '@ripl/3d';
const context = createContext('#app');

After:

ts
import {
    createContext,
} from '@ripl/webgpu';
const context = await createContext('#app');

IMPORTANT

createContext from @ripl/webgpu is async — it returns a Promise<WebGPUContext3D>. You must await it.

3. Everything else stays the same

  • createScene(context) — works unchanged
  • createCamera(scene, options) — works unchanged
  • createRenderer(scene) — works unchanged
  • All Shape3D elements — work unchanged
  • Event handlers (mouseenter, mouseleave, etc.) — work unchanged
  • Transitions and animations — work unchanged

What changes under the hood

AspectContext3D (Canvas 2D)WebGPUContext3D
Depth sortingCPU painter's algorithmHardware depth buffer
RasterisationCanvas 2D fill/strokeGPU fragment shader
ShadingCPU per-face colourGPU per-fragment Lambertian
Anti-aliasingNone (canvas default)4× MSAA
Intersecting geometryMay render incorrectlyCorrect via depth test

Fallback

If you need to support browsers without WebGPU, you can feature-detect:

ts
let context;

if (navigator.gpu) {
    const { createContext } = await import('@ripl/webgpu');
    context = await createContext('#app');
} else {
    const { createContext } = await import('@ripl/3d');
    context = createContext('#app');
}