defineMaterial is the entrypoint for creating materials in Motion GPU. It validates your inputs, freezes the result, and produces an immutable FragMaterial that FragCanvas uses to build the WebGPU rendering pipeline.
Basic usage
Input fields
Fragment contract
The fragment source must contain this function signature:
defineMaterial validates this contract semantically and throws immediately with targeted errors:
- missing
fragentrypoint - wrong parameter list (must be exactly
uv: vec2f) - wrong return type (must be
vec4f)
What defineMaterial does internally
- Validates the fragment contract — checks entrypoint name, parameter contract, and return type.
- Normalizes uniforms — validates identifier names, infers types from values, preserves input order (layout/sig ordering is sorted later).
- Normalizes textures — validates identifier names and clones definitions.
- Normalizes defines — validates identifier names, checks value constraints (finite numbers, integer checks for
i32/u32, non-negative foru32). - Normalizes includes — validates identifier names, checks for non-empty source strings.
- Normalizes storage buffers — validates identifier names, checks
size > 0,size % 4 === 0, valid type and access mode, optionalinitialDatafits within declared size. - Freezes the output object and its top-level maps (
uniforms,textures,defines,includes,storageBuffers).
Immutability
The returned FragMaterial is frozen with Object.freeze. This means:
- You cannot modify the material after creation.
- The
uniforms,textures,defines,includes, andstorageBufferssub-objects are also frozen. - To change a material, create a new one with
defineMaterial(...).
This immutability is critical for Motion GPU’s caching: the material signature is computed once and used to detect when the renderer needs rebuilding.
Material signatures
When FragCanvas resolves a material, it computes a deterministic signature from:
- The preprocessed fragment (after include/define expansion).
- The uniform layout (sorted name/type sequence).
- The texture key list (sorted).
- The normalized texture sampling/upload config for each texture (including
fragmentVisible). - The storage buffer key list (sorted) with size, type, and access mode.
This signature is combined with outputColorSpace to form the final pipeline signature. Only changes in this combined signature trigger a renderer rebuild.
What triggers rebuilds vs. buffer updates
Full example with all fields
Misuse guards
Practical guidance
- Keep material objects stable — reuse the same instance. Creating a new material with the same content still matches the same signature, but unnecessary allocations add overhead.
- Put shape/type changes in
defineMaterial— uniform types and texture bindings should be declared upfront. - Put value changes in
useFrame— animation, interaction, and dynamic state go throughstate.setUniform()andstate.setTexture(). - Use defines for compile-time branches — the compiler can eliminate dead code paths.
- Use includes for shared utility code — noise functions, SDF helpers, color transforms.