Pointers as Short Names
When working with deeply nested, complex structures, writing out full variable access paths like particles[i].physics.transform.position over and over is tedious, error-prone, and clutters your shader code.
Inside a function, you can combine the address-of operator & with an immutable let-declaration to create a clean, ultra-fast short name (alias pointer) targeting a sub-element inside a larger structure.
Member Access Precedence: The Parentheses Rule
When using pointers as short names to access structure fields or array items, you must be careful with operator binding precedence.
In WGSL, member access (the . operator) and array indexing (the [] operator) bind more tightly than the dereference operator (*).
Precedence Reference Cheat-Sheet
Always parenthesize dereferences before doing member access or array indexing:
- Structure Field:
(*p).member - Array Indexing:
(*p)[index]
Playground Walkthrough
In the accompanying interactive playground, we use the Buffer Viewer to show how input buffer values are transformed into output buffer values.
The shader defines a custom DataPoint structure:
Inside the main entrypoint, we load values from the input buffer, load them into a local DataPoint instance, and then use & to create a pointer short-name to that structure:
We then cleanly read and update the structure members using correct precedence:
The computed values are then written to the output storage buffer. You can click on the input cells below to modify the values in real-time and watch the GPU animate the scaled output!
/*
* Copyright ©2026 Michael R. Bernstein. All new modifications licensed under Apache 2.0.
* Upstream lineage ©2023 governed by original BSD 3-Clause. See README.md.
*/
struct DataPoint {
val: f32,
scaled: f32,
}
@group(0) @binding(0) var<storage, read> input_data: array<f32>;
@group(0) @binding(1) var<storage, read_write> output_data: array<f32>;
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) global_id: vec3u) {
let index = global_id.x;
let length = arrayLength(&input_data);
if (index < length) {
// 1. Initialize a local composite structure
var data: DataPoint;
data.val = input_data[index];
data.scaled = 0.0;
// 2. Create a pointer as a short name targeting our local structure
let p = &data;
// 3. Access and update structure members using correct pointer precedence.
// Parentheses are required: (*p).member binds dereference first.
let original = (*p).val;
(*p).scaled = original * 3.0;
// 4. Output the updated struct value back to the buffer
output_data[index] = (*p).scaled;
}
}