WGSL Canvas

Simple way to run WebGPU shaders on HTML Canvas.

Install

npm i --save-exact @wgsl-canvas/core

TypeScript (Recommended)

If you're using TypeScript, it would be helpful to add @webgpu/types, so your codebase will be aware of WebGPU-related types.

npm i --save-dev @webgpu/types

Then add them into your tsconfig.json.

{
  // ...
  "compilerOptions": {
    // ...
    "types": ["@webgpu/types"]
  }
}

Get started

Here's the base example for you to get started.

import { WGSLCanvas } from "@wgsl-canvas/core";

// 1. Check WebGPU support
if (!WGSLCanvas.isSupported()) {
  alert("WebGPU is not supported in this browser");
  return;
}

// 2. Create a canvas
const canvas = document.createElement("canvas");
canvas.width = 500;
canvas.height = 500;
document.body.appendChild(canvas);

// 3. Create an instance of "WGSLCanvas" and initialize it
const wgslCanvas = new WGSLCanvas({ canvas });
await wgslCanvas.init();

// 4. Make your first render
wgslCanvas.render();

If everything is set up correctly, you should see this output (which is the default fragment shader located under WGSLCanvas.SHADER_FRAGMENT_DEFAULT static field).

Default fragment shader

See full example here.

Shader

To pass your shader, define it as a string of WGSL code.

const shaderFragment = /* wgsl */`
struct FragmentOutput {
  @location(0) color: vec4<f32>,
}

@fragment
fn fragment_main() -> FragmentOutput {
  var output: FragmentOutput;
  output.color = vec4<f32>(0.1, 0.2, 0.25, 1.0);
  return output;
}
`;

Then pass it to WGSLCanvas instance via shaderFragment property.

wgslCanvas.shaderFragment = shaderFragment;
Tip

If you want to store your WGSL code under .wgsl files, you should configure your bundler to be able to resolve them as strings. The easiest way is to start with Vite, which can do this out of the box using ?raw suffix.

See full example here.

Uniforms

To pass uniforms to your shader, you should first define them in uniformsKeys array in your WGSLCanvas instance.

wgslCanvas.uniformsKeys = ["time", "color1", "color2"];

Then, you can pass the values into uniforms object.

wgslCanvas.uniforms.time = 0.0;
wgslCanvas.uniforms.color1 = [1.0, 0.0, 0.0];
wgslCanvas.uniforms.color2 = [0.0, 0.0, 1.0];

Then, they'll be available under var<uniform> object at @group(0), @binding(0) in your WGSL shader.

@group(0) @binding(0) var<uniform> uniforms: Uniforms;

struct Uniforms {
  time: f32,
  color1: vec3<f32>,
  color2: vec3<f32>,
}
Note

The order of keys in struct Uniforms must be the same as defined in uniformsKeys array!

See full example here.

Textures

Make sure you have your texture file served under some URL (can be absolute or relative).

Then, you can load it via WGSLCanvas.loadTexture static method to get an ImageBitmap image.

const textureUrl = "https://example.com/YOUR_TEXTURE.jpg";
const texture = await WGSLCanvas.loadTexture(textureUrl);

Then, pass it into the textures array of your WgslCanvas instance.

wgslCanvas.textures = [texture];

In WGSL shader, it'll appear under the following vars.

@group(0) @binding(0) var texture_sampler: sampler;
@group(0) @binding(1) var texture: texture_2d<f32>;
Note

If you have uniforms, they will appear at @binding(0), but sampler and textures will appear under their bindings incremented by 1.

See full example here.

Examples

Check out various examples here.

Links