| name | webgpu-syntax-textures |
| description | Use when creating WebGPU textures, texture views, or samplers, choosing a texture format, or importing a video frame as an external texture. Prevents validation failures from wrong usage flags, sampler / binding-type mismatches, and stale external-texture references. Covers createTexture, GPUTextureFormat, GPUTextureUsage, createView and view dimensions, mip levels, multisample textures, createSampler, and importExternalTexture. Keywords: createTexture, createView, createSampler, GPUTextureFormat, GPUTextureUsage, GPUSampler, importExternalTexture, mip levels, multisample, texture binding, comparison sampler, how do I load a texture, blurry texture, video texture.
|
| license | MIT |
| compatibility | Designed for Claude Code. Requires WebGPU 1.0-stable. |
| metadata | {"author":"OpenAEC-Foundation","version":"1.0"} |
WebGPU Textures, Views and Samplers
Create GPUTexture, GPUTextureView, and GPUSampler objects with correct
formats, usage flags, and binding-type matching. Targets WebGPU 1.0-stable
(Chrome 113+, Safari 26+, Firefox 141+).
Quick Reference
createTexture descriptor
const texture = device.createTexture({
label: "albedo",
size: [width, height, 1],
mipLevelCount: 1,
sampleCount: 1,
dimension: "2d",
format: "rgba8unorm",
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
viewFormats: [],
});
GPUTextureFormat (most common values)
| Group | Formats |
|---|
| 8-bit color | rgba8unorm, rgba8unorm-srgb, bgra8unorm, bgra8unorm-srgb |
| 16-bit float | r16float, rgba16float |
| 32-bit float | r32float, rgba32float (filtering needs float32-filterable) |
| Depth / stencil | depth16unorm, depth24plus, depth24plus-stencil8, depth32float |
| Depth gated | depth32float-stencil8 (needs depth32float-stencil8 feature) |
ALWAYS take the canvas format from navigator.gpu.getPreferredCanvasFormat().
See webgpu-syntax-canvas-context.
GPUTextureUsage flags
| Flag | Allows the texture to be |
|---|
COPY_SRC | source of copyTextureToBuffer / copyTextureToTexture |
COPY_DST | destination of writeTexture / copyBufferToTexture |
TEXTURE_BINDING | bound and sampled in a shader |
STORAGE_BINDING | bound as a storage texture (read/write in a shader) |
RENDER_ATTACHMENT | a color or depth-stencil attachment of a render pass |
createSampler descriptor
const sampler = device.createSampler({
addressModeU: "clamp-to-edge",
addressModeV: "clamp-to-edge",
addressModeW: "clamp-to-edge",
magFilter: "linear",
minFilter: "linear",
mipmapFilter: "linear",
lodMinClamp: 0,
lodMaxClamp: 32,
maxAnisotropy: 1,
});
Sampler binding types
| Sampler | Binding-layout sampler.type |
|---|
Any filter "linear", no compare | "filtering" |
All filters "nearest", no compare | "non-filtering" |
compare set | "comparison" |
Decision Tree
Need a texture? -> what is it for?
Sampled in a shader -> usage |= TEXTURE_BINDING (+ COPY_DST to upload)
Render pass output -> usage |= RENDER_ATTACHMENT
Sampled later AND a render output -> usage = RENDER_ATTACHMENT | TEXTURE_BINDING
Compute storage texture -> usage |= STORAGE_BINDING (sampleCount must be 1)
Read back to CPU -> usage |= COPY_SRC
Pick a format:
Canvas / swap-chain -> navigator.gpu.getPreferredCanvasFormat()
Color art assets, sRGB -> rgba8unorm-srgb
Linear data (normal maps) -> rgba8unorm
HDR / intermediate render -> rgba16float
Pure depth buffer -> depth24plus
Depth + stencil -> depth24plus-stencil8
Pick a view dimension:
Plain 2D image -> "2d" (default)
Texture array -> "2d-array"
Cubemap (6 layers) -> "cube"
Volume texture -> "3d"
Pick a sampler:
Smooth scaling -> magFilter/minFilter "linear"
Pixel-art / data lookup -> magFilter/minFilter "nearest"
Mipmapped texture -> mipmapFilter "linear"
Shadow map depth compare -> set compare, e.g. "less"
Core Patterns
ALWAYS include every usage the texture is actually used for
A texture used as both a render target and a sampled input needs both flags.
A missing flag fails validation at bind-group or render-pass time, not at
createTexture time.
const offscreen = device.createTexture({
size: [w, h],
format: "rgba16float",
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
});
ALWAYS create texture views fresh from the owning texture
texture.createView(descriptor?) returns a GPUTextureView. For a plain 2D
single-mip texture, texture.createView() with no descriptor is correct.
const fullView = texture.createView();
const oneMip = texture.createView({ baseMipLevel: 2, mipLevelCount: 1 });
const faceView = cubeTex.createView({ dimension: "cube" });
ALWAYS match the sampler binding type to the sampler
A "filtering" sampler binding accepts a sampler with "linear" filters. A
"non-filtering" binding rejects any sampler with a "linear" filter. A
compare sampler requires a "comparison" binding. See webgpu-syntax-bind-groups.
{ binding: 0, visibility: GPUShaderStage.FRAGMENT, sampler: { type: "filtering" } }
{ binding: 0, resource: device.createSampler({ magFilter: "linear", minFilter: "linear" }) }
ALWAYS set sampleCount to 4 on both the texture and the pipeline for MSAA
sampleCount is 1 or 4 only. A multisample texture requires
usage |= RENDER_ATTACHMENT, forbids STORAGE_BINDING, and requires
mipLevelCount: 1. The render pipeline multisample.count MUST equal 4 too.
const msaaColor = device.createTexture({
size: [w, h],
sampleCount: 4,
format: presentationFormat,
usage: GPUTextureUsage.RENDER_ATTACHMENT,
});
NEVER cache a GPUExternalTexture across frames
device.importExternalTexture({ source }) returns a GPUExternalTexture. With
an HTMLVideoElement source it expires immediately after first use (single-frame
lifetime). Re-import every frame and rebuild the bind group every frame.
function frame() {
const bindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{ binding: 0, resource: sampler },
{ binding: 1, resource: device.importExternalTexture({ source: videoEl }) },
],
});
requestAnimationFrame(frame);
}
ALWAYS gate feature-dependent formats on adapter.features
depth32float-stencil8 needs the depth32float-stencil8 feature. Filtering an
r32float / rgba32float texture needs float32-filterable. Request the
feature on the device only after confirming the adapter has it.
const requiredFeatures = adapter.features.has("float32-filterable")
? ["float32-filterable"] : [];
const device = await adapter.requestDevice({ requiredFeatures });
Common Anti-Patterns
-
Missing TEXTURE_BINDING or RENDER_ATTACHMENT usage. A texture created
with only COPY_DST cannot be sampled or rendered into. WHY it fails:
bind-group and render-pass validation check the texture's usage flags and
reject a texture that lacks the required capability.
-
Hard-coding bgra8unorm for the canvas. getPreferredCanvasFormat()
returns rgba8unorm on some platforms. WHY it fails: a mismatch forces a
per-frame format conversion and can break pipeline fragment.targets format
matching.
-
Caching a GPUExternalTexture. WHY it fails: an external texture from an
HTMLVideoElement expires after the task that first uses it; reusing the
stale handle fails validation on the next frame.
Critical Warnings
- NEVER use a
"non-filtering" sampler binding type with a sampler that has a
"linear" filter. Validation rejects the bind group.
- NEVER sample an
r32float / rgba32float texture with a filtering sampler
unless the device was created with the float32-filterable feature.
- NEVER set
sampleCount to any value other than 1 or 4.
- NEVER combine
sampleCount: 4 with STORAGE_BINDING or mipLevelCount > 1.
- NEVER expect WebGPU to generate mipmaps automatically.
mipLevelCount > 1
allocates the levels; you MUST fill them. See webgpu-impl-render-targets.
- NEVER set
maxAnisotropy > 1 unless magFilter, minFilter, and
mipmapFilter are all "linear".
- NEVER reuse a texture view created from
context.getCurrentTexture() across
frames. See webgpu-syntax-canvas-context.
Reference Files
references/methods.md : full descriptors, format list, usage flags, view
dimensions, sampler fields, importExternalTexture signature.
references/examples.md : verified code for sampled upload, render target,
mipmapped views, sampler creation, external texture from video.
references/anti-patterns.md : detailed mistakes with WHY-it-fails analysis.
Related skills: webgpu-syntax-canvas-context, webgpu-syntax-bind-groups,
webgpu-wgsl-textures, webgpu-impl-render-targets.