WGSL Syntax
Write valid WGSL (WebGPU Shading Language) shader code: types, declarations,
operators, control flow, and functions, without triggering shader-creation errors.
Quick Reference
WGSL is the shading language of WebGPU 1.0-stable (Chrome 113+, Safari 26+,
Firefox 141+). WGSL is statically typed: every expression has a type known at
shader-creation time.
| Type group |
Forms |
Notes |
| Scalar |
bool, i32, u32, f32, f16 |
f16 needs enable f16; plus the shader-f16 device feature |
| Literal suffix |
42i (i32), 42u (u32), 1.5f (f32), 1.5h (f16) |
Unsuffixed 42 / 1.5 is an abstract numeric, materialized at use |
| Vector |
vec2<T>, vec3<T>, vec4<T> |
Aliases: vec3f, vec4u, vec2i, vec3h, etc. |
| Matrix |
matCxR<T>, C columns x R rows, T floating-point |
Column-major. Aliases: mat4x4f, mat3x3f, etc. |
| Array (fixed) |
array<f32, 16> |
Count is a const-expression |
| Array (runtime) |
array<f32> |
ONLY as the last member of a storage struct |
| Struct |
struct Light { position: vec3f, color: vec3f, } |
Members may carry @align, @size, @location, @builtin |
| Atomic |
atomic<i32>, atomic<u32> |
ONLY in workgroup or storage, accessed via atomic builtins |
| Type alias |
alias RGB = vec3<f32>; |
Keyword is alias, NEVER type |
| Declaration |
Form |
Mutability |
var |
var<address_space, access> name: T = init; |
Mutable, lives in memory |
let |
let name = value; |
Immutable, block-scoped, runtime value |
const |
const name = expr; |
Immutable, compile-time const-expression |
override |
override name: T = default; |
Pipeline-overridable, set by host, may carry @id(n) |
Inside a function var x = 0; defaults to the function address space. At
module scope a var needs an address space, except private which is the
default. Address-space detail and memory layout: webgpu-wgsl-memory-layout.
Decision Tree
Need to introduce a name in WGSL?
├── Value must change after first assignment (a mutable variable)?
│ └── var name = init; (function scope inside fn; needs an
│ address space at module scope)
│
├── Value is fixed but only known at runtime (a function argument, a
│ computed result you reuse)?
│ └── let name = value; (immutable, block-scoped)
│
├── Value is fixed AND computable entirely at shader-creation time
│ (a literal, math on literals)?
│ └── const name = expr; (compile-time constant)
│
└── Value must be chosen by the host at pipeline-creation time
(a quality setting, a workgroup dimension)?
└── override name: T = default; (add @id(n) for a stable numeric id)
Core Patterns
Pattern 1: ALWAYS use the alias keyword for type aliases
WGSL spells the type-alias keyword alias. There is NO type keyword in WGSL.
Writing type RGB = vec3<f32>; is a shader-creation error.
alias RGB = vec3<f32>;
alias Mat = mat4x4<f32>;
fn tint(c: RGB) -> RGB { return c * 0.5; }
Pattern 2: ALWAYS declare a runtime-sized array only in a storage struct
array<T> with no count is legal ONLY as the last member of a struct in the
storage address space. Its length is queried with arrayLength(&ptr). A
runtime-sized array in a function, private, uniform, or workgroup
variable is a shader-creation error.
struct Particles {
count: u32,
data: array<vec4f>, // runtime-sized: legal here, last member, storage
}
@group(0) @binding(0) var<storage, read> particles: Particles;
fn total() -> u32 { return arrayLength(&particles.data); }
Pattern 3: ALWAYS give switch a default and NEVER expect fall-through
A switch selects on an i32 or u32. A default clause is mandatory. WGSL
has NO C-style fall-through: each case body runs in isolation. List multiple
selector values comma-separated to share one body.
fn classify(x: i32) -> i32 {
switch x {
case 0, 1: { return 10; } // 0 and 1 share this body
case 2: { return 20; }
default: { return -1; } // mandatory, even if logically unreachable
}
}
Pattern 4: ALWAYS pass a pointer to mutate caller memory
WGSL function parameters are ALWAYS passed by value. To let a helper write back
into the caller's variable, declare a ptr parameter, pass &var, and write
through *ptr.
fn add_one(p: ptr<function, f32>) { *p = *p + 1.0; }
fn use_it() -> f32 {
var v = 4.0;
add_one(&v); // v is now 5.0
return v;
}
Pattern 5: NEVER write a recursive WGSL function
Recursion is forbidden in WGSL. The static call graph MUST be acyclic. A
function that calls itself, directly or through a cycle, is a shader-creation
error. Rewrite recursion as a loop, for, or while.
// CORRECT: iterative factorial, no recursion
fn factorial(n: u32) -> u32 {
var result = 1u;
for (var i = 2u; i <= n; i++) { result = result * i; }
return result;
}
Pattern 6: NEVER mix .xyzw and .rgba in one swizzle
Vectors support component access with either the .xyzw set OR the .rgba set,
never mixed in a single swizzle. v.xr is a compile error. A 1-component swizzle
yields a scalar; 2-4 components yield a vector. Components may repeat in a read
(v.xxxx), never in an assignment target.
var color = vec4f(1.0, 0.5, 0.25, 1.0);
let rgb = color.rgb; // vec3f, single set
let flip = color.wzyx; // vec4f, single set
color.xy = vec2f(0.0, 0.0); // assignment target: no repeats
// let bad = color.xr; // ERROR: mixed .xyzw and .rgba
Common Anti-Patterns
type RGB = vec3<f32>; to declare a type alias. WHY it fails: WGSL has no
type keyword. The alias keyword is alias. type is a shader-creation
error.
Declaring var<private> buf: array<f32>; or a runtime-sized array in a
function or uniform. WHY it fails: a runtime-sized array<T> is legal ONLY
as the last member of a storage-address-space struct. Anywhere else it is
a shader-creation error.
A switch with no default, or expecting one case to fall through into
the next. WHY it fails: WGSL requires a default clause and runs each case
body in isolation. Missing default is a shader-creation error; there is no
fall-through to rely on.
Critical Warnings
- NEVER use
type for a type alias. The keyword is alias.
- NEVER declare a runtime-sized
array<T> outside the last member of a
storage struct. It is a shader-creation error in function, private,
uniform, and workgroup.
- NEVER omit the
default clause in a switch, and NEVER expect C-style
fall-through between case labels.
- NEVER write a recursive function or a cyclic call graph. Recursion is
forbidden; rewrite it as a loop.
- NEVER call an entry-point function (
@vertex, @fragment, @compute) from
other WGSL code. Entry points cannot be invoked as helpers.
- NEVER declare a
const fn. WGSL 1.0 has NO user-declarable const functions;
const-evaluation is a property of certain builtins only.
- NEVER mix
.xyzw with .rgba in one swizzle, and NEVER repeat a component
in a swizzle assignment target.
- NEVER use an
h literal suffix or f16 without enable f16; at the top of
the shader plus the shader-f16 device feature.
Reference Files
references/methods.md : the complete type list, all four declaration forms,
the operator and precedence table, control-flow constructs, and function rules.
references/examples.md : verified WGSL snippets for types, declarations,
operators, control flow, and functions.
references/anti-patterns.md : WGSL syntax mistakes with WHY-it-fails
explanations.
Related Skills
webgpu-wgsl-memory-layout : address-space detail and the @align / @size
struct layout rules referenced but not duplicated here.
webgpu-wgsl-builtins : @builtin values and the WGSL builtin function set.
webgpu-wgsl-vertex-shaders : @vertex entry points, attributes, and
inter-stage varyings.
webgpu-wgsl-uniformity : uniform control flow and why textureSample and
barriers are restricted.
1---2name: webgpu-wgsl-syntax3description: Use when writing WGSL shader code: declaring types, variables, operators, control flow, and functions. Prevents shader-creation errors from wrong type aliases, recursion, and missing switch defaults. Covers WGSL scalar / vector / matrix / array / struct types, var let const override declarations, operators, swizzling, control flow, and functions. Keywords: WGSL, WGSL syntax, vec3f, mat4x4f, var let const override, alias, switch default, swizzle, WGSL function, recursion not allowed, shader compile error, how do I write a WGSL shader.4license: MIT5---67# WGSL Syntax89Write valid WGSL (WebGPU Shading Language) shader code: types, declarations,10operators, control flow, and functions, without triggering shader-creation errors.1112## Quick Reference1314WGSL is the shading language of WebGPU 1.0-stable (Chrome 113+, Safari 26+,15Firefox 141+). WGSL is statically typed: every expression has a type known at16shader-creation time.1718| Type group | Forms | Notes |19|------------|-------|-------|20| Scalar | `bool`, `i32`, `u32`, `f32`, `f16` | `f16` needs `enable f16;` plus the `shader-f16` device feature |21| Literal suffix | `42i` (i32), `42u` (u32), `1.5f` (f32), `1.5h` (f16) | Unsuffixed `42` / `1.5` is an abstract numeric, materialized at use |22| Vector | `vec2<T>`, `vec3<T>`, `vec4<T>` | Aliases: `vec3f`, `vec4u`, `vec2i`, `vec3h`, etc. |23| Matrix | `matCxR<T>`, C columns x R rows, T floating-point | Column-major. Aliases: `mat4x4f`, `mat3x3f`, etc. |24| Array (fixed) | `array<f32, 16>` | Count is a const-expression |25| Array (runtime) | `array<f32>` | ONLY as the last member of a `storage` struct |26| Struct | `struct Light { position: vec3f, color: vec3f, }` | Members may carry `@align`, `@size`, `@location`, `@builtin` |27| Atomic | `atomic<i32>`, `atomic<u32>` | ONLY in `workgroup` or `storage`, accessed via atomic builtins |28| Type alias | `alias RGB = vec3<f32>;` | Keyword is `alias`, NEVER `type` |2930| Declaration | Form | Mutability |31|-------------|------|------------|32| `var` | `var<address_space, access> name: T = init;` | Mutable, lives in memory |33| `let` | `let name = value;` | Immutable, block-scoped, runtime value |34| `const` | `const name = expr;` | Immutable, compile-time const-expression |35| `override` | `override name: T = default;` | Pipeline-overridable, set by host, may carry `@id(n)` |3637Inside a function `var x = 0;` defaults to the `function` address space. At38module scope a `var` needs an address space, except `private` which is the39default. Address-space detail and memory layout: `webgpu-wgsl-memory-layout`.4041## Decision Tree4243```44Need to introduce a name in WGSL?45├── Value must change after first assignment (a mutable variable)?46│ └── var name = init; (function scope inside fn; needs an47│ address space at module scope)48│49├── Value is fixed but only known at runtime (a function argument, a50│ computed result you reuse)?51│ └── let name = value; (immutable, block-scoped)52│53├── Value is fixed AND computable entirely at shader-creation time54│ (a literal, math on literals)?55│ └── const name = expr; (compile-time constant)56│57└── Value must be chosen by the host at pipeline-creation time58 (a quality setting, a workgroup dimension)?59 └── override name: T = default; (add @id(n) for a stable numeric id)60```6162## Core Patterns6364### Pattern 1: ALWAYS use the `alias` keyword for type aliases6566WGSL spells the type-alias keyword `alias`. There is NO `type` keyword in WGSL.67Writing `type RGB = vec3<f32>;` is a shader-creation error.6869```wgsl70alias RGB = vec3<f32>;71alias Mat = mat4x4<f32>;7273fn tint(c: RGB) -> RGB { return c * 0.5; }74```7576### Pattern 2: ALWAYS declare a runtime-sized array only in a storage struct7778`array<T>` with no count is legal ONLY as the last member of a struct in the79`storage` address space. Its length is queried with `arrayLength(&ptr)`. A80runtime-sized array in a `function`, `private`, `uniform`, or `workgroup`81variable is a shader-creation error.8283```wgsl84struct Particles {85 count: u32,86 data: array<vec4f>, // runtime-sized: legal here, last member, storage87}88@group(0) @binding(0) var<storage, read> particles: Particles;8990fn total() -> u32 { return arrayLength(&particles.data); }91```9293### Pattern 3: ALWAYS give `switch` a `default` and NEVER expect fall-through9495A `switch` selects on an `i32` or `u32`. A `default` clause is mandatory. WGSL96has NO C-style fall-through: each case body runs in isolation. List multiple97selector values comma-separated to share one body.9899```wgsl100fn classify(x: i32) -> i32 {101 switch x {102 case 0, 1: { return 10; } // 0 and 1 share this body103 case 2: { return 20; }104 default: { return -1; } // mandatory, even if logically unreachable105 }106}107```108109### Pattern 4: ALWAYS pass a pointer to mutate caller memory110111WGSL function parameters are ALWAYS passed by value. To let a helper write back112into the caller's variable, declare a `ptr` parameter, pass `&var`, and write113through `*ptr`.114115```wgsl116fn add_one(p: ptr<function, f32>) { *p = *p + 1.0; }117118fn use_it() -> f32 {119 var v = 4.0;120 add_one(&v); // v is now 5.0121 return v;122}123```124125### Pattern 5: NEVER write a recursive WGSL function126127Recursion is forbidden in WGSL. The static call graph MUST be acyclic. A128function that calls itself, directly or through a cycle, is a shader-creation129error. Rewrite recursion as a `loop`, `for`, or `while`.130131```wgsl132// CORRECT: iterative factorial, no recursion133fn factorial(n: u32) -> u32 {134 var result = 1u;135 for (var i = 2u; i <= n; i++) { result = result * i; }136 return result;137}138```139140### Pattern 6: NEVER mix `.xyzw` and `.rgba` in one swizzle141142Vectors support component access with either the `.xyzw` set OR the `.rgba` set,143never mixed in a single swizzle. `v.xr` is a compile error. A 1-component swizzle144yields a scalar; 2-4 components yield a vector. Components may repeat in a read145(`v.xxxx`), never in an assignment target.146147```wgsl148var color = vec4f(1.0, 0.5, 0.25, 1.0);149let rgb = color.rgb; // vec3f, single set150let flip = color.wzyx; // vec4f, single set151color.xy = vec2f(0.0, 0.0); // assignment target: no repeats152// let bad = color.xr; // ERROR: mixed .xyzw and .rgba153```154155## Common Anti-Patterns1561571. `type RGB = vec3<f32>;` to declare a type alias. WHY it fails: WGSL has no158 `type` keyword. The alias keyword is `alias`. `type` is a shader-creation159 error.1601612. Declaring `var<private> buf: array<f32>;` or a runtime-sized array in a162 function or uniform. WHY it fails: a runtime-sized `array<T>` is legal ONLY163 as the last member of a `storage`-address-space struct. Anywhere else it is164 a shader-creation error.1651663. A `switch` with no `default`, or expecting one `case` to fall through into167 the next. WHY it fails: WGSL requires a `default` clause and runs each case168 body in isolation. Missing `default` is a shader-creation error; there is no169 fall-through to rely on.170171## Critical Warnings172173- NEVER use `type` for a type alias. The keyword is `alias`.174- NEVER declare a runtime-sized `array<T>` outside the last member of a175 `storage` struct. It is a shader-creation error in `function`, `private`,176 `uniform`, and `workgroup`.177- NEVER omit the `default` clause in a `switch`, and NEVER expect C-style178 fall-through between `case` labels.179- NEVER write a recursive function or a cyclic call graph. Recursion is180 forbidden; rewrite it as a loop.181- NEVER call an entry-point function (`@vertex`, `@fragment`, `@compute`) from182 other WGSL code. Entry points cannot be invoked as helpers.183- NEVER declare a `const fn`. WGSL 1.0 has NO user-declarable const functions;184 const-evaluation is a property of certain builtins only.185- NEVER mix `.xyzw` with `.rgba` in one swizzle, and NEVER repeat a component186 in a swizzle assignment target.187- NEVER use an `h` literal suffix or `f16` without `enable f16;` at the top of188 the shader plus the `shader-f16` device feature.189190## Reference Files191192- `references/methods.md` : the complete type list, all four declaration forms,193 the operator and precedence table, control-flow constructs, and function rules.194- `references/examples.md` : verified WGSL snippets for types, declarations,195 operators, control flow, and functions.196- `references/anti-patterns.md` : WGSL syntax mistakes with WHY-it-fails197 explanations.198199## Related Skills200201- `webgpu-wgsl-memory-layout` : address-space detail and the `@align` / `@size`202 struct layout rules referenced but not duplicated here.203- `webgpu-wgsl-builtins` : `@builtin` values and the WGSL builtin function set.204- `webgpu-wgsl-vertex-shaders` : `@vertex` entry points, attributes, and205 inter-stage varyings.206- `webgpu-wgsl-uniformity` : uniform control flow and why `textureSample` and207 barriers are restricted.