Components & Swizzling
In WGSL, vectors are multi-component containers. To manipulate vectors effectively, you need to read individual components, reorder them, or index them like arrays. WGSL provides high-performance, built-in syntax for component selection, indexing, and swizzling.
Component Access Styles
You can access individual vector components using dot-notation. WGSL supports two distinct semantic naming styles:
| Style | Valid Components | Typical Use Case |
|---|---|---|
| Coordinate Style | .x, .y, .z, .w |
Spatial positions, coordinates, and offsets |
| Color Style | .r, .g, .b, .a |
Color channels (Red, Green, Blue, Alpha) |
Strict Naming Rules
To keep code readable and prevent compiler ambiguity, WGSL enforces two critical rules:
- No Style Mixing: You cannot mix coordinate and color naming styles within a single access expression.
my_vec.xyis valid.my_vec.rgis valid.my_vec.xgis invalid and results in a compilation error.
- Bounds Checking: You can only access components that exist within the vector's declared dimension.
- Attempting to access
.zor.bon avec2will cause a compile-time error. - Attempting to access
.wor.aon avec3will cause a compile-time error.
- Attempting to access
Array-Like Indexing
If you need to access components dynamically using variables or numerical offsets, you can index a vector just like a standard array using 0-based integer indices:
let position = vec3f(1.0, 2.0, 3.0);
let first_comp = position[0]; // Resolves to 1.0 (equivalent to position.x)
let second_comp = position[1]; // Resolves to 2.0 (equivalent to position.y)
Dynamic Indexing Constraint
While constant indices (like position[0]) can be evaluated at compile-time or pipeline-creation time, dynamic variable indices (like position[index]) are evaluated at runtime on the GPU.
Component Swizzling
Swizzling is an extremely powerful technique that allows you to construct a new, smaller, or identical-sized vector by combining components of an existing vector in any order, duplicating them as needed.
You swizzle by appending multiple component letters after the dot:
let v = vec4f(1.0, 2.0, 3.0, 4.0);
let reversed = v.zyx; // Constructs vec3f(3.0, 2.0, 1.0)
let red_green = v.rg; // Constructs vec2f(1.0, 2.0)
let replicated = v.xxxx; // Constructs vec4f(1.0, 1.0, 1.0, 1.0)
The resulting vector's size is determined by the number of component letters you specify (from 2 up to 4 elements).
The Read-Only (Rvalue) Constraint
Crucial WGSL Restriction
In WGSL, swizzles are strictly rvalues (read-only expressions). Unlike some other shading languages (such as GLSL), you cannot write to a swizzle or use it on the left-hand side of an assignment.
Try It Out: Interact with Swizzling!
The interactive visualizer on the right (or below) renders a real-time wave pattern. You can use swizzling overrides in the shader to manipulate both color channels and coordinates:
- Color Swapping: Uncomment line 43 (
final_color = vec4f(base_color.bgr, 1.0);) to swap the red and blue channels, transforming warm pinks/oranges into cool cyan/blue gradients. - Component Isolation: Comment line 43 out, and uncomment line 46 (
final_color = vec4f(base_color.ggg, 1.0);) to duplicate the green component, creating a monochromatic glowing green wave. - Coordinate Swizzling: Uncomment line 49 (
final_color = vec4f(sin(in.uv.xyx * 10.0 + t) * 0.5 + 0.5, 1.0);). Notice how the coordinate swizzle.xyxtransforms the flat 2D coordinate space into a rich, complex 3D color field!
/*
* Copyright ©2026 Michael R. Bernstein. Licensed under Apache 2.0.
* See root README.md for global project-wide upstream attributions.
*/
@group(0) @binding(0) var<uniform> frame: u32;
struct VertexOutput {
@builtin(position) pos: vec4f,
@location(0) uv: vec2f,
}
@vertex
fn vtx_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {
// A single giant triangle covering the screen (-1 to 3 clip space)
const pos = array(
vec2f(-1.0, -1.0),
vec2f( 3.0, -1.0),
vec2f(-1.0, 3.0)
);
var out: VertexOutput;
out.pos = vec4f(pos[vertex_index], 0.0, 1.0);
out.uv = pos[vertex_index] * 0.5 + vec2f(0.5);
return out;
}
@fragment
fn frag_main(in: VertexOutput) -> @location(0) vec4f {
let t = f32(frame) * 0.02;
// Create a base moving color vector using spatial coordinates and time
let r = sin(in.uv.x * 6.28 + t) * 0.5 + 0.5;
let g = sin(in.uv.y * 6.28 - t) * 0.5 + 0.5;
let b = cos((in.uv.x + in.uv.y) * 3.14 + t) * 0.5 + 0.5;
let base_color = vec4f(r, g, b, 1.0);
// SWIZZLING OVERRIDES: Try uncommenting and editing different swizzles below!
var final_color = base_color;
// Option 1: Swap red and blue channels (color swapping)
// final_color = vec4f(base_color.bgr, 1.0);
// Option 2: Extract green channel to make it monochromatic (component isolation)
// final_color = vec4f(base_color.ggg, 1.0);
// Option 3: Generate a 3D color wave from 2D coordinates (.xyx)
// final_color = vec4f(sin(in.uv.xyx * 10.0 + t) * 0.5 + 0.5, 1.0);
return final_color;
}