Skip to content

WGSL (WebGPU Shading Language) Cheatsheet

Fresh 🌱

WGSL is a Rust-like, statically-typed shading language. All WebGPU shaders are written in WGSL.

Shader Entry Points

wgsl
// Vertex shader
@vertex
fn vs_main(@builtin(vertex_index) idx: u32) -> @builtin(position) vec4f {
  return vec4f(0.0, 0.0, 0.0, 1.0);
}

// Fragment shader
@fragment
fn fs_main() -> @location(0) vec4f {
  return vec4f(1.0, 0.0, 0.0, 1.0); // Red
}

// Compute shader
@compute @workgroup_size(64)
fn cs_main(@builtin(global_invocation_id) id: vec3u) {
  // Parallel computation here
}

Types

wgsl
// Scalars
let a: f32 = 1.0;
let b: i32 = -5;
let c: u32 = 10u;
let d: bool = true;
let e: f16 = 1.0h; // half-float (requires f16 feature)

// Vectors
let v2: vec2f = vec2f(1.0, 2.0);
let v3: vec3f = vec3f(0.0, 1.0, 0.0);
let v4: vec4f = vec4f(0.31, 0.19, 0.89, 1.0);

// Matrices
let m4: mat4x4f = mat4x4f(/* col-major 16 values */);

// Type aliases
// vec2f = vec2<f32>, vec3i = vec3<i32>, vec4u = vec4<u32>

Built-in Variables

VariableStageDescription
@builtin(vertex_index)vertexCurrent vertex index (u32)
@builtin(instance_index)vertexCurrent instance index (u32)
@builtin(position)vertex output / fragment inputClip/fragment position (vec4f)
@builtin(front_facing)fragmentTrue if front-facing primitive (bool)
@builtin(frag_depth)fragment outputOverride depth value (f32)
@builtin(global_invocation_id)compute3D global thread ID (vec3u)
@builtin(local_invocation_id)compute3D local workgroup thread ID (vec3u)
@builtin(workgroup_id)computeWorkgroup ID (vec3u)
@builtin(num_workgroups)computeTotal workgroups dispatched (vec3u)

Bindings

wgsl
// Uniform buffer (read-only, ≤64KB)
@group(0) @binding(0) var<uniform> mvp: mat4x4f;

// Storage buffer (read-only, large)
@group(0) @binding(1) var<storage, read> inputData: array<f32>;

// Storage buffer (read-write)
@group(0) @binding(2) var<storage, read_write> outputData: array<f32>;

// Texture
@group(0) @binding(3) var myTexture: texture_2d<f32>;

// Sampler
@group(0) @binding(4) var mySampler: sampler;

// External texture (video)
@group(0) @binding(5) var videoTex: texture_external;

Common Built-in Functions

wgsl
// Math
abs(x)      dot(a, b)       cross(a, b)
sqrt(x)     pow(x, y)       log(x)       exp(x)
sin(x)      cos(x)          tan(x)
min(a, b)   max(a, b)       clamp(x, lo, hi)
floor(x)    ceil(x)         round(x)     fract(x)
mix(a, b, t)                // linear interpolation
normalize(v)                // unit vector
length(v)                   // vector magnitude
distance(a, b)

// Texture sampling
let color = textureSample(myTexture, mySampler, uv);
let color = textureLoad(myTexture, coord, mipLevel);
let color = textureLoad(videoTex, coord); // external texture
let dims = textureDimensions(myTexture);

// Type conversion
let u = bitcast<u32>(floatVal); // reinterpret bits
let f = f32(intVal);            // convert type

Control Flow

wgsl
// If/else
if x > 0.0 {
  // ...
} else if x < 0.0 {
  // ...
} else {
  // ...
}

// Loop
var i = 0u;
loop {
  if i >= 10u { break; }
  // ...
  i++;
}

// For loop
for (var i = 0u; i < 10u; i++) {
  // ...
}

// Switch
switch (myU32) {
  case 0u: { /* ... */ }
  case 1u: { /* ... */ }
  default: { /* ... */ }
}

Workgroup Memory (Shared Memory)

wgsl
var<workgroup> sharedData: array<f32, 64>;

@compute @workgroup_size(64)
fn main(@builtin(local_invocation_index) lid: u32) {
  // Load into shared memory
  sharedData[lid] = inputData[/* global index */];

  // Sync all threads in workgroup
  workgroupBarrier();

  // Now all threads can read shared data
  let neighborVal = sharedData[(lid + 1u) % 64u];
}

Vertex Input/Output

wgsl
struct VertexInput {
  @location(0) position: vec3f,
  @location(1) uv:       vec2f,
  @location(2) normal:   vec3f,
}

struct VertexOutput {
  @builtin(position) clipPos: vec4f,
  @location(0)       uv:      vec2f,
  @location(1)       normal:  vec3f,
}

@vertex
fn vs(in: VertexInput) -> VertexOutput {
  var out: VertexOutput;
  out.clipPos = mvp * vec4f(in.position, 1.0);
  out.uv = in.uv;
  out.normal = in.normal;
  return out;
}

Z Clip Space Difference from WebGL

WebGL:  Z range = -1 to 1  (OpenGL convention)
WebGPU: Z range =  0 to 1  (Metal/D3D convention)

Adjust projection matrices accordingly when migrating from WebGL.

Based on Chrome for Developers WebGPU Documentation