useTextureFor texture declaration and configuration in materials, see Texture Definitions.
Basic usage
<script lang="ts">
import { useFrame, useTexture } from '@motion-core/motion-gpu';
const loaded = useTexture(['/assets/albedo.png']);
useFrame((state) => {
const tex = loaded.textures.current?.[0];
state.setTexture('uAlbedo', tex ? { source: tex.source } : null);
});
</script><script lang="ts">
import { useFrame, useTexture } from '@motion-core/motion-gpu';
const loaded = useTexture(['/assets/albedo.png']);
useFrame((state) => {
const tex = loaded.textures.current?.[0];
state.setTexture('uAlbedo', tex ? { source: tex.source } : null);
});
</script>useTextureURL input forms
| Form | Description |
|---|---|
string[] | Static list of URLs |
() => string[] | Lazy URL provider (evaluated on each load/reload) |
// Static
const tex = useTexture(['/assets/a.png', '/assets/b.png']);
// Dynamic
const tex = useTexture(() => [
`/assets/textures/${currentSet}/diffuse.png`,
`/assets/textures/${currentSet}/normal.png`
]);// Static
const tex = useTexture(['/assets/a.png', '/assets/b.png']);
// Dynamic
const tex = useTexture(() => [
`/assets/textures/${currentSet}/diffuse.png`,
`/assets/textures/${currentSet}/normal.png`
]);Return value (UseTextureResult)
UseTextureResult| Field | Type | Description |
|---|---|---|
textures | CurrentReadable<LoadedTexture[]null> | Loaded textures in input order, or null |
loading | CurrentReadable<boolean> | true |
error | CurrentReadable<Errornull> | Last loading error |
reload | () => Promise<void> | Triggers a fresh load with current URL input |
Using .current for synchronous access
.currentAll stores expose a
.currentuseFrameuseFrame((state) => {
if (loaded.loading.current) return; // Skip while loading
if (loaded.error.current) return; // Skip on error
const textures = loaded.textures.current;
if (textures) {
state.setTexture('uDiffuse', { source: textures[0].source });
state.setTexture('uNormal', { source: textures[1].source });
}
});useFrame((state) => {
if (loaded.loading.current) return; // Skip while loading
if (loaded.error.current) return; // Skip on error
const textures = loaded.textures.current;
if (textures) {
state.setTexture('uDiffuse', { source: textures[0].source });
state.setTexture('uNormal', { source: textures[1].source });
}
});Using Svelte subscriptions for reactive UI
<script lang="ts">
const { textures, loading, error } = useTexture(['/assets/albedo.png']);
</script>
{#if $loading}
<p>Loading textures...</p>
{:else if $error}
<p>Error: {$error.message}</p>
{:else}
<p>Loaded {$textures?.length ?? 0} textures</p>
{/if}<script lang="ts">
const { textures, loading, error } = useTexture(['/assets/albedo.png']);
</script>
{#if $loading}
<p>Loading textures...</p>
{:else if $error}
<p>Error: {$error.message}</p>
{:else}
<p>Loaded {$textures?.length ?? 0} textures</p>
{/if}TextureLoadOptions
| Field | Type | Default | Description |
|---|---|---|---|
colorSpace | 'srgb''linear' | 'srgb' | Affects colorSpaceConversion |
requestInit | RequestInit | undefined | Forwarded to fetch() |
decode | TextureDecodeOptions | See below | Bitmap decode settings |
signal | AbortSignal | undefined | External cancellation signal |
update | TextureUpdateMode | undefined | Metadata attached to loaded textures |
flipY | boolean | undefined | Metadata attached to loaded textures |
premultipliedAlpha | boolean | undefined | Metadata attached to loaded textures |
generateMipmaps | boolean | undefined | Metadata attached to loaded textures |
TextureDecodeOptions
| Field | Default | Description |
|---|---|---|
colorSpaceConversion | 'none''default' | Colour space handling during bitmap decode |
premultiplyAlpha | 'default' | Alpha premultiplication during decode |
imageOrientation | 'none' | Bitmap orientation ('none''flipY' |
Example with options
const loaded = useTexture(
['/assets/hdr-env.png'],
{
colorSpace: 'linear',
generateMipmaps: true,
decode: {
premultiplyAlpha: 'none'
}
}
);const loaded = useTexture(
['/assets/hdr-env.png'],
{
colorSpace: 'linear',
generateMipmaps: true,
decode: {
premultiplyAlpha: 'none'
}
}
);LoadedTexture shape
Each loaded texture provides:
| Field | Type | Description |
|---|---|---|
url | string | Source URL |
source | ImageBitmap | Decoded bitmap — pass to state.setTexture |
width | number | Bitmap width in pixels |
height | number | Bitmap height in pixels |
colorSpace | 'srgb''linear' | Effective colour space |
update | 'once''onInvalidate''perFrame' | Effective update mode metadata |
flipY | boolean | Effective flip-y metadata |
premultipliedAlpha | boolean | Effective premultiplied alpha metadata |
generateMipmaps | boolean | Effective mipmap metadata |
dispose | () => void | Releases the bitmap resources |
Abort, reload, and race safety
useTextureCancellation on reload
When
reload()Request versioning
Each load increments an internal version counter. When a response arrives, it is only accepted if its version matches the current counter. This eliminates race conditions from overlapping loads.
Dispose on replacement
When new textures are loaded successfully, the previous bitmaps are disposed. When the component is destroyed, all current bitmaps are disposed.
Abort error suppression
AbortErrorerrorBlob cache
Under the hood,
texture-loader.ts- If two calls request the same URL with the same options, only one network request is made.
useTexture - The cache key is deterministic: .
JSON.stringify({ url, colorSpace, requestInit, decode }) - Entries are evicted when their reference count drops to zero (e.g., all components using that URL are destroyed).
Component lifecycle
useTextureonDestroy- On destroy, flag is set.
disposed - The internal is incremented (invalidating any in-flight result).
requestVersion - The active is aborted.
AbortController - All current bitmap textures are disposed.
This means you don’t need to manually clean up — the hook handles its own lifecycle.
Error handling
| Failure | Source |
|---|---|
createImageBitmap | Runtime capability check |
| Non-OK HTTP response (4xx, 5xx) | URL fetch |
| Invalid image data | Bitmap decode |
| Abort / cancellation | AbortError |