# Motion GPU
Motion GPU is a lightweight WebGPU runtime for writing Shadertoy-style fullscreen shaders in pure WGSL. It provides a framework-agnostic core with a Svelte 5 adapter for building fullscreen shader pipelines, offering a minimal runtime loop, scheduler, and render graph specifically designed for fragment-driven GPU programs. Unlike general-purpose 3D engines, Motion GPU focuses on running fullscreen fragment shaders and multi-pass GPU pipelines with a significantly smaller bundle size (3.5-5x smaller than Three.js).
The library follows a simple three-step workflow: define an immutable material with `defineMaterial()`, render it with `
Canvas: {$gpu.size.width}x{$gpu.size.height} @ {$gpu.dpr}x
``` ## useTexture Hook Loads textures from URLs with reactive loading/error state and automatic cleanup. ```svelte {#if $loaded.loading}Loading textures...
{:else if $loaded.error}Error: {$loaded.error.message}
{:else}Loaded {$loaded.textures?.length ?? 0} textures
{/if} ``` ## ShaderPass Programmable post-process pass with custom WGSL fragment shader. The fragment must declare `fn shade(inputColor: vec4f, uv: vec2f) -> vec4f`. ```typescript import { ShaderPass } from '@motion-core/motion-gpu/svelte'; // Vignette effect const vignettePass = new ShaderPass({ fragment: ` fn shade(inputColor: vec4f, uv: vec2f) -> vec4f { let dist = distance(uv, vec2f(0.5, 0.5)); let v = smoothstep(0.9, 0.35, dist); return vec4f(inputColor.rgb * v, inputColor.a); } `, enabled: true, needsSwap: true, // Swap source/target after render input: 'source', // Read from source buffer output: 'target', // Write to target buffer filter: 'linear' }); // Gamma correction to canvas (final pass) const gammaPass = new ShaderPass({ needsSwap: false, input: 'source', output: 'canvas', fragment: ` fn shade(inputColor: vec4f, uv: vec2f) -> vec4f { return vec4f(pow(inputColor.rgb, vec3f(1.0 / 2.2)), inputColor.a); } ` }); // Hot-swap shader at runtime vignettePass.setFragment(` fn shade(inputColor: vec4f, uv: vec2f) -> vec4f { return inputColor; // Passthrough } `); ``` ## BlitPass Fullscreen texture blit pass that copies input to output using configurable filter mode. ```typescript import { BlitPass } from '@motion-core/motion-gpu/svelte'; const blitPass = new BlitPass({ enabled: true, needsSwap: true, input: 'source', output: 'target', filter: 'nearest', // 'linear' or 'nearest' clear: false, clearColor: [0, 0, 0, 1], preserve: true }); ``` ## CopyPass Optimized texture copy with GPU-side `copyTextureToTexture` when possible, falling back to blit when direct copy conditions aren't met. ```typescript import { CopyPass } from '@motion-core/motion-gpu/svelte'; const copyPass = new CopyPass({ enabled: true, needsSwap: true, input: 'source', output: 'target', filter: 'linear' }); // Direct GPU copy when: same size/format, different textures, neither is canvas ``` ## Uniform Types Supported uniform types with shorthand and explicit declaration forms. ```typescript import { defineMaterial } from '@motion-core/motion-gpu/svelte'; const material = defineMaterial({ fragment: ` fn frag(uv: vec2f) -> vec4f { let t = motiongpuUniforms.uTime; let pos = motiongpuUniforms.uPosition; let color = motiongpuUniforms.uColor; let tint = motiongpuUniforms.uTint; let matrix = motiongpuUniforms.uModelMatrix; return vec4f(color * t, 1.0); } `, uniforms: { // Shorthand (type inferred from value shape) uTime: 0, // f32 uPosition: [0.5, 0.5], // vec2f uColor: [1.0, 0.5, 0.0], // vec3f uTint: [1.0, 1.0, 1.0, 0.8], // vec4f // Explicit (required for mat4x4f) uBrightness: { type: 'f32', value: 1.0 }, uModelMatrix: { type: 'mat4x4f', value: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ] } } }); // Built-in frame uniforms (auto-provided, don't declare): // motiongpuFrame.time - f32 timestamp in seconds // motiongpuFrame.delta - f32 frame delta in seconds // motiongpuFrame.resolution - vec2f canvas size in pixels ``` ## Texture Configuration Configure texture sampling, upload behavior, and update strategies. ```typescript import { defineMaterial } from '@motion-core/motion-gpu/svelte'; const material = defineMaterial({ fragment: ` fn frag(uv: vec2f) -> vec4f { let color = textureSample(uAlbedo, uAlbedoSampler, uv); let video = textureSample(uVideo, uVideoSampler, uv); return mix(color, video, 0.5); } `, textures: { uAlbedo: { colorSpace: 'srgb', // 'srgb' or 'linear' flipY: true, // Flip vertically on upload generateMipmaps: true, // Generate mip chain premultipliedAlpha: false, // Alpha premultiplication update: 'once', // 'once' | 'onInvalidate' | 'perFrame' anisotropy: 4, // Anisotropic filtering (1-16) filter: 'linear', // 'linear' or 'nearest' addressModeU: 'repeat', // 'clamp-to-edge' | 'repeat' | 'mirror-repeat' addressModeV: 'repeat' }, uVideo: { update: 'perFrame' // Re-upload every frame for video } } }); ``` ## Video Texture Example Bind video elements as textures with automatic per-frame updates. ```svelte