Fixed-size Arrays
A fixed-size array type is declared as array<T, N>, where
T is the element type (with some restrictions), and
N is the element count.
In most cases N is a const-expression.
Exception for Workgroup Variables
When the array is used as the type of a workgroup variable, N can be an override-expression. This means the array size can be adjusted at pipeline-creation time, though it is still "fixed" before the shader executes.
Common Fixed-Size Array Types
| Declaration | Description |
|---|---|
array<f32, 5> |
A 5-element array of f32. |
array<array<f32, 4>, 8> |
An array of 8 arrays of 4 f32 (a nested 2D array). |
array<S, c> |
An array of c elements of type S, where c must be a constant expression. |
array<i32, 4 * blockSize> |
An array of i32 with 4 * blockSize elements, where blockSize must be a constant expression. |
With the one exception above, fixed-size array values can be used like other plain values, for example:
- in an expression,
- passed as a function argument,
- returned from a function,
- assigned to a variable, or
- used as the initializer for a variable or declared value.
Memory Layout and Strides
The byte size and alignment of a fixed-size array depend directly on the properties of its element type \(T\). The memory spacing is controlled by the element stride: the stride represents the total number of bytes occupied by a single element, including any necessary trailing padding to satisfy alignment requirements.
Custom Strides with @stride
In WGSL, you can optionally override the default element stride using the @stride attribute. This is particularly useful when interfacing with uniform or storage buffers that require specific alignment layouts (such as 16-byte alignment limits):
// Declares an array of four 32-bit floats, where each float
// is padded to occupy exactly 16 bytes in memory.
var<uniform> padded_floats: @stride(16) array<f32, 4>;
Nested (Multidimensional) Arrays
WGSL supports multi-dimensional collections by nesting arrays. For example, a 2D grid representing a 3D transformation or a spatial buffer can be defined as an array of arrays:
// A 2D grid containing 3 rows and 4 columns of floats
var matrix_grid: array<array<f32, 4>, 3>;
// To access the element at row r, column c:
let value = matrix_grid[r][c];
Out-of-Bounds (OOB) Safety
To prevent security vulnerabilities and undefined hardware behaviors, WGSL enforces a strict out-of-bounds safety model. The behavior depends on whether the index can be evaluated at compile-time:
1. Compile-Time Evaluation (Static OOB)
If the index is a constant expression and is out of bounds (for example, accessing first_fibs[10] on a 7-element array), the compiler will reject the shader and throw a compilation error.
2. Runtime Evaluation (Dynamic OOB)
If the index is dynamic and cannot be determined at compile-time, the index is automatically clamped to the safe range [0, N - 1], where \(N\) is the array size:
- A write to an out-of-bounds index will write to the last valid index.
- A read from an out-of-bounds index will read from the last valid index.
This bounds-clamping safety guarantees that shaders can never read or write to arbitrary GPU memory outside the array's allocation.
/*
* 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.
*/
const first_fibs = array<i32, 7>(1,1,2,3,5,8,13);
const i = 4;
const ith_fib = first_fibs[i];
// The element count can be a constant expression.
// array<i32,7> and array<i32, 2 * three + 1 > are the same type.
const three = 3;
const fibs_copy: array<i32, 2 * three + 1> = first_fibs;
// A no-argument array constructor fills the array with zeros.
const nine_zero_array = array<u32, 9>();
const ith_zero = nine_zero_array[i];
// A function that returns a fixed-size array.
fn get_two_fibs(i: u32, j: u32) -> array<i32,2> {
// This constructor infers the element type and element count.
return array(first_fibs[i], first_fibs[j]);
}