This page covers texture declarations in defineMaterial, the runtime texture value forms, update modes, upload behavior, mipmap generation, and sampler configuration.
For loading textures from URLs, see Loading Textures.
Declaring textures
Textures are declared in the textures field of defineMaterial:
Each texture with fragmentVisible: true (default) creates two WGSL bindings:
uFoo: texture_2d<f32>— the textureuFooSampler: sampler— the associated sampler
When fragmentVisible: false, the texture slot is excluded from fragment WGSL declarations and group(0) fragment bind-group bindings.
TextureDefinition fields
Runtime texture value forms
You can set texture sources at definition time or at runtime via state.setTexture():
Example: setting a texture from a video
<script lang="ts">
import { useFrame } from '@motion-core/motion-gpu/svelte';
let video: HTMLVideoElement;
useFrame((state) => {
if (video && video.readyState >= 2) {
state.setTexture('uVideo', video);
}
});
</script>
<video bind:this={video} src="/assets/loop.mp4" autoplay loop muted playsinline />Because the source is an HTMLVideoElement, the update mode is automatically set to perFrame.
Update modes
The update mode controls when the texture is re-uploaded to the GPU:
Resolution precedence
- Runtime override —
TextureData.update(fromstate.setTexture({ source, update: '...' })) - Definition default —
TextureDefinition.update(fromdefineMaterial({ textures: { ... } })) - Automatic fallback —
HTMLVideoElement→perFrame, everything else →once
Storage textures (compute)
When storage: true is enabled, the texture participates in compute bindings (group(2)):
formatmust be a storage-compatible format.- For
PingPongComputePass, the target texture must have explicitwidthandheight. - Storage textures are compute-managed and are not source-uploaded through normal texture update flow.
- Use
fragmentVisible: falsefor compute-only storage textures that should not consume fragment sampler/texture bindings.
Upload behavior
The renderer decides how to handle each texture per frame:
The renderer maintains a fallback 1×1 opaque white texture for each binding. If the user source is null, the fallback is used so the shader always has a valid binding.
Mipmap generation
When generateMipmaps: true is set:
- The base level (mip 0) is uploaded normally with
copyExternalImageToTexture. - Each subsequent mip level is generated by drawing into an offscreen canvas, halving dimensions each time.
- Each downscaled level is uploaded individually.
The implementation uses OffscreenCanvas when available, falling back to a regular <canvas> element.
Mipmap generation adds upload cost proportional to ~33% of the base texture size. Use it when texture minification is visible (e.g., a texture displayed at varying scales).
Sampler configuration
The sampler created for each texture is configured from the TextureDefinition fields:
If generateMipmaps is enabled, mipmapFilter is also set to the same value as filter.
Naming rules
Texture identifiers follow the same rules as uniforms: [A-Za-z_][A-Za-z0-9_]*. Invalid names throw at material definition time.