| name | motion-gpu-adapters-wgsl |
| description | Build and edit MotionGPU code across framework-agnostic core and Svelte/React/Vue adapters. Use when implementing or refactoring FragCanvas-based components, defineMaterial shaders, useFrame runtime logic, textures/useTexture workflows, render passes/targets, compute shaders/storage buffers, render-mode scheduling, or MotionGPU error handling and diagnostics. |
MotionGPU Core + Adapters Skill
Use this skill to produce production-grade MotionGPU code across:
- framework-agnostic core (
@motion-core/motion-gpu, @motion-core/motion-gpu/core),
- Svelte adapter (
@motion-core/motion-gpu/svelte),
- React adapter (
@motion-core/motion-gpu/react),
- Vue adapter (
@motion-core/motion-gpu/vue).
Treat Svelte, React, and Vue as first-class adapters. Do not assume Svelte-only APIs.
Source of Truth
Treat public package entrypoints as authoritative:
| Entrypoint | Layer | What it exposes |
|---|
@motion-core/motion-gpu | Core | Framework-agnostic runtime primitives (defineMaterial, resolveMaterial, scheduler/runtime builders, passes, texture loader, error normalization) |
@motion-core/motion-gpu/advanced | Core | Core + scheduler helpers (applySchedulerPreset, captureSchedulerDebugSnapshot) |
@motion-core/motion-gpu/core | Core | Same core API surface as root, explicit core path |
@motion-core/motion-gpu/core/advanced | Core | Same advanced core helper surface |
@motion-core/motion-gpu/svelte | Adapter | Svelte FragCanvas, hooks (useMotionGPU, useFrame, usePointer, useTexture), passes, material helpers |
@motion-core/motion-gpu/svelte/advanced | Adapter | Svelte adapter + user context APIs + scheduler helpers |
@motion-core/motion-gpu/react | Adapter | React FragCanvas, hooks (useMotionGPU, useFrame, usePointer, useTexture), passes, material helpers |
@motion-core/motion-gpu/react/advanced | Adapter | React adapter + user context APIs + scheduler helpers |
@motion-core/motion-gpu/vue | Adapter | Vue FragCanvas, composables (useMotionGPU, useFrame, usePointer, useTexture), passes, material helpers |
@motion-core/motion-gpu/vue/advanced | Adapter | Vue adapter + user context APIs + scheduler helpers |
Advanced adapter exports:
- Svelte, React, and Vue advanced entrypoints export:
useMotionGPUUserContext
setMotionGPUUserContext
applySchedulerPreset
captureSchedulerDebugSnapshot
- React advanced additionally exports:
useSetMotionGPUUserContext
Import only from public entrypoints above. Never import from internal package paths (/src, /lib/core, etc.).
Documentation sources:
- LLM docs index:
http://motion-gpu.dev/llms.txt
- Docs generated from source live under
apps/web/src/routes/docs
If examples conflict with exported runtime behavior, prefer exported API contracts from entrypoints.
Adapter Differences (Must Be Preserved)
When writing or refactoring code, keep these differences explicit.
FragCanvas props
Shared runtime props (all adapters):
material, renderTargets, passes, clearColor, color, renderMode, autoRender, maxDelta, dpr, adapterOptions, deviceDescriptor, showErrorOverlay, onError, errorHistoryLimit, onErrorHistory
Adapter-specific differences:
- Svelte:
class?: string
style?: string
children?: Snippet
errorRenderer?: Snippet<[MotionGPUErrorReport]>
- React:
className?: string
style?: React.CSSProperties
children?: ReactNode
errorRenderer?: (report: MotionGPUErrorReport) => ReactNode
- Vue:
canvasClass?: string
canvasStyle?: string | Record<string, string | number>
- default slot for children
#errorRenderer="{ report }" scoped slot for custom error UI
User context writes
- All adapters support
setMotionGPUUserContext(namespace, valueOrFactory, options?).
- React additionally supports
useSetMotionGPUUserContext() and should prefer it for effect/event-handler writes.
SetMotionGPUUserContextOptions supports:
existing?: 'skip' | 'replace' | 'merge'
functionValue?: 'factory' | 'value'
useTexture signature
- Shared return shape:
{ textures, loading, error, errorReport, reload }
- Shared URL input:
string[] | () => string[]
- Options input:
- Svelte:
TextureLoadOptions | () => TextureLoadOptions
- React:
TextureLoadOptions
- Vue:
TextureLoadOptions | () => TextureLoadOptions
usePointer signature
- Shared return shape:
{ state, lastClick, resetClick }
- Shared option highlights:
requestFrame?: 'auto' | 'invalidate' | 'advance' | 'none'
capturePointer?: boolean
trackWhilePressedOutsideCanvas?: boolean
- click synthesis options (
clickEnabled, clickMaxDurationMs, clickMaxMovePx, clickButtons)
- callbacks:
onMove, onDown, onUp, onClick
- Coordinate conventions:
state.current.uv uses shader-friendly Y-up (0..1)
state.current.ndc uses Y-up (-1..1)
Hard Contracts
Enforce these constraints without exceptions:
- Material shader entrypoint must be exactly:
fn frag(uv: vec2f) -> vec4f
ShaderPass shader entrypoint must be exactly:
fn shade(inputColor: vec4f, uv: vec2f) -> vec4f
ComputePass shader must contain @compute @workgroup_size(...) and a fn compute(...) entrypoint.
- Call
useFrame(), useMotionGPU(), and usePointer() only inside the <FragCanvas> subtree.
- Declare all runtime-updated uniforms/textures in
defineMaterial(...) first.
- Use WGSL-safe identifiers for uniforms/textures/defines/includes/storage buffers:
[A-Za-z_][A-Za-z0-9_]*
- Use
needsSwap: true only with input: 'source' and output: 'target'.
- Never read from
input: 'canvas' in render passes.
- Use explicit
{ type: 'mat4x4f', value: [...] } for matrix uniforms.
- Keep
maxDelta > 0 and scheduler profiling window > 0.
- Build materials via
defineMaterial(...); never handcraft FragMaterial.
- In
manual mode, call advance() to render; invalidate() alone does not render.
- For
invalidation: { mode: 'on-change' }, always provide token.
- Read/write named pass slots only when declared in
renderTargets.
- Declare all storage buffers in
defineMaterial({ storageBuffers }) before using writeStorageBuffer/readStorageBuffer.
- Storage buffer
size must be > 0 and a multiple of 4.
PingPongComputePass iterations must be >= 1.
- Compute passes do not participate in render pass slot routing (no
input/output/needsSwap) and always dispatch before the base scene render.
FragCanvas.color.dynamicRange: 'hdr' cannot be combined with color.toneMapping: 'khronos-pbr-neutral'; Khronos PBR Neutral maps HDR scene values to SDR.
Architecture Pattern
Default to host + runtime split in all adapters.
- Host component:
- Create stable
material with defineMaterial(...).
- Render
FragCanvas with material.
- Attach
passes, renderTargets, renderMode, onError as needed.
- Runtime child component:
- Call
useFrame(...) for per-frame updates.
- Call
useMotionGPU() for canvas/scheduler/render controls.
- Call
usePointer(...) for normalized mouse/touch/pen input and click snapshots.
- Use
useTexture(...) for URL texture IO.
Prefer this split even for simple effects. It keeps context usage valid and readable.
Implementation Workflow
1. Classify request
Pick one main mode:
- Static shader (no runtime updates).
- Animated shader (uniform updates in
useFrame).
- Interactive shader (pointer/state-driven updates).
- Texture-driven shader (
useTexture and state.setTexture).
- Post-processing pipeline (
ShaderPass/BlitPass/CopyPass).
- Compute shader (
ComputePass/PingPongComputePass with storage buffers).
- Advanced scheduling/user context (advanced entrypoints).
2. Pick layer and adapter
- If building framework runtime usage, pick adapter entrypoint (
/svelte, /react, or /vue).
- If building framework-independent tooling, adapter internals, or low-level integrations, use core entrypoints (
@motion-core/motion-gpu or /core).
- If adapter is not explicitly stated:
- follow existing imports/files in the target codebase,
- preserve current adapter,
- avoid mixing adapter APIs in one component.
3. Design material boundary
Put in material:
- Fragment WGSL source.
- Uniform declarations and initial values.
- Texture declarations and sampler/upload defaults.
- Storage buffer declarations (
storageBuffers) with size, type, access mode, and optional initialData.
defines for compile-time constants.
includes for reusable WGSL chunks.
color pipeline options for output encoding, tone mapping, HDR/SDR presentation, canvas color space, and internal working format.
Put in runtime (useFrame):
state.setUniform(...) for dynamic values.
state.setTexture(...) for dynamic texture sources.
state.writeStorageBuffer(name, data, { offset? }) to write CPU data to GPU storage buffers.
state.readStorageBuffer(name) to read GPU storage buffer data back (Promise<ArrayBuffer>).
state.invalidate(...) and state.advance() control.
4. Pick render cadence intentionally
Choose mode by behavior:
always: continuous animation/video.
on-demand: interaction or sporadic updates.
manual: explicit frame stepping/testing/capture.
If using on-demand, define invalidation policy explicitly:
- Keep
autoInvalidate: true for frame-driven effects.
- Use
autoInvalidate: false + invalidation: { mode: 'on-change', token: ... } for state-driven redraws.
Render-mode semantics:
on-demand renders one initial frame, then sleeps until invalidated.
- Switching to
on-demand triggers one frame.
manual ignores invalidation-only flow; requires advance().
5. Add error strategy at creation time
Always wire onError.
Keep default overlay in dev unless the task explicitly requires custom UI.
Disable overlay only when the user asks for silent/custom handling.
6. Validate before finalizing
Run checks available in the target package/app:
pnpm run check
pnpm run test
pnpm run lint
If working from the monorepo root, prefer scoped commands such as pnpm --dir packages/motion-gpu run test or pnpm --dir apps/web run check.
If repository scripts use other package manager commands, run equivalents (npm/yarn/bun).
If a script is missing, run closest available static/type/test checks and report what was not run.
If touching .svelte files and svelte-autofixer is available, run:
npx @sveltejs/mcp svelte-autofixer <path-to-file>
Authoring Rules by Domain
WGSL
- Use
motiongpuFrame.time, motiongpuFrame.delta, motiongpuFrame.resolution for frame data.
- Read user uniforms through
motiongpuUniforms.<name>.
- Sample textures with generated pairs:
uTex and uTexSampler.
usePointer().state.current.uv already provides Y-up UV; flip Y manually only for custom DOM event wiring.
Uniforms
- Prefer shorthand for scalar/vector:
0, [x,y], [x,y,z], [x,y,z,w].
- Use explicit typed form for clarity and matrices.
- Keep types stable; type/shape changes require new material.
Textures
- Set static sampling defaults in
defineMaterial({ textures }).
- Use runtime
state.setTexture for source changes.
- Use
fragmentVisible: false for compute-only texture slots that should not consume fragment sampler/texture bindings.
- Storage textures require explicit
format, width, and height; they must not define a source.
- Integer storage texture formats (
*uint, *sint) default fragmentVisible to false; explicitly setting fragmentVisible: true for them throws because fragment bindings are texture_2d<f32>.
- Update-mode guidance:
once for static images,
onInvalidate for event-driven updates,
perFrame for video/canvas streams.
- Use
null safely to unbind user source (fallback texture remains valid).
Includes and Defines
- Use
includes for reusable shader functions.
- Keep include chunks non-empty and non-circular.
- Use
defines for compile-time toggles and loop constants.
- Use typed integer defines for integer loops:
{ type: 'i32', value: N } or { type: 'u32', value: N }.
- Use typed vector defines for static WGSL vector constants:
{ type: 'vec2f', value: [x, y] }, { type: 'vec3f', value: [r, g, b] }, or { type: 'vec4f', value: [r, g, b, a] }.
- Use uniforms instead of defines for values that change per frame or through interaction.
- Expect renderer rebuild when define/include output changes.
Color Pipeline
- Use
FragCanvas.color for color pipeline configuration.
color.outputEncoding controls final SDR encoding ('srgb' by default, 'linear' for linear output).
color.toneMapping: 'khronos-pbr-neutral' enables the private final presentation pass and expects non-negative linear HDR scene color.
color.dynamicRange: 'hdr' requests HDR canvas presentation; dynamicRange: 'auto' tries HDR and falls back to SDR.
color.workingFormat: 'auto' selects rgba16float for HDR or tone-mapped pipelines, otherwise the preferred canvas format.
- Changing any color pipeline option is a renderer signature change and rebuilds the renderer.
Scheduler and User Context
- Use
applySchedulerPreset(...) when selecting performance, balanced, or debug behavior.
- Keep
diagnosticsEnabled and profilingEnabled equal when overriding preset options.
- Keep
profilingWindow finite and > 0.
- Use
setMotionGPUUserContext(namespace, value) for shared canvas-subtree state.
- Default conflict behavior is
existing: 'skip'; pass existing: 'replace' or existing: 'merge' intentionally.
- In React, prefer
useSetMotionGPUUserContext() for writes in effects and event handlers.
- Use
useMotionGPUUserContext(namespace?) as read API.
Passes and Targets
- Start with
ShaderPass unless copy/blit is sufficient.
- Use
CopyPass when fast copy can apply; it falls back automatically.
- Use named
renderTargets for multi-resolution or branching pipelines.
- Validate slot availability order: write before read in same frame plan.
Compute Shaders and Storage Buffers
- Declare storage buffers in
defineMaterial({ storageBuffers: { name: { size, type, access? } } }).
- Use
ComputePass for single-dispatch GPU compute; PingPongComputePass for iterative simulations.
- Compute passes are accepted in the same
passes array as render passes, but dispatch in declaration order before the base scene render.
- Compute passes do not read/write render pass slots and cannot satisfy render-pass slot dependencies.
dispatch can be static tuple [x, y?, z?], 'auto', or dynamic function.
- Use
state.writeStorageBuffer(name, data) in useFrame to upload CPU data before compute.
- Use
state.readStorageBuffer(name) to read back GPU results asynchronously.
- Storage buffers are bound at group(1) in compute shaders; storage textures at group(2).
- Fragment shaders can read storage buffers as read-only via
var<storage, read> at group(1).
fragmentVisible: false affects only fragment-stage texture bindings; compute storage texture bindings at group(2) are still generated.
PingPongComputePass generates two texture bindings from target:
{target}A sampled read texture at @group(2) @binding(0)
{target}B write storage texture at @group(2) @binding(1)
PingPongComputePass requires target texture declared with storage: true and explicit width/height.
Canonical Templates
Svelte minimal animated component
<script lang="ts">
import { FragCanvas, defineMaterial } from '@motion-core/motion-gpu/svelte';
import Runtime from './Runtime.svelte';
const material = defineMaterial({
fragment: `
fn frag(uv: vec2f) -> vec4f {
let t = 0.5 + 0.5 * sin(motiongpuUniforms.uTime + uv.x * 8.0);
return vec4f(vec3f(t), 1.0);
}
`,
uniforms: { uTime: 0 }
});
</script>
<FragCanvas {material}>
<Runtime />
</FragCanvas>
<script lang="ts">
import { useFrame } from '@motion-core/motion-gpu/svelte';
useFrame((state) => {
state.setUniform('uTime', state.time);
});
</script>
React minimal animated component
import { FragCanvas, defineMaterial, useFrame } from '@motion-core/motion-gpu/react';
const material = defineMaterial({
fragment: `
fn frag(uv: vec2f) -> vec4f {
let t = 0.5 + 0.5 * sin(motiongpuUniforms.uTime + uv.x * 8.0);
return vec4f(vec3f(t), 1.0);
}
`,
uniforms: { uTime: 0 }
});
function Runtime() {
useFrame((state) => {
state.setUniform('uTime', state.time);
});
return null;
}
export function App() {
return (
<FragCanvas material={material}>
<Runtime />
</FragCanvas>
);
}
React advanced user-context write
import { useSetMotionGPUUserContext } from '@motion-core/motion-gpu/react/advanced';
export function QualityButton() {
const setUserContext = useSetMotionGPUUserContext();
return (
<button
onClick={() => {
setUserContext('config', { quality: 'medium' }, { existing: 'merge' });
}}
>
Medium
</button>
);
}
Debugging Playbook
Follow this order:
- Contract errors:
- Verify
frag(...) and shade(...) signatures first.
- Missing runtime binding:
- Ensure names exist in
material.uniforms or material.textures.
- WGSL compile errors:
- Read normalized report from
onError.
- Map error to fragment/include/define line.
- Pass graph errors:
- Verify
needsSwap, input/output slots, and target declarations.
- No redraw in
on-demand:
- Check invalidation path and
autoInvalidate settings.
- If mode is
manual, use advance().
- Texture issues:
- Confirm source readiness (
readyState for video).
- Check update mode and source dimensions.
- Compute shader errors:
- Verify
@compute @workgroup_size(...) and fn compute(...).
- Ensure storage buffers are declared before use.
- Check
writeStorageBuffer offset + data size does not exceed buffer size.
- Verify dispatch dimensions match workgroup layout.
Quality Checklist Before Delivery
Ship only when all checks pass:
- Keep shader contracts valid (
frag/shade/compute signatures).
- Keep all runtime-updated keys predeclared in material.
- Keep render mode and invalidation strategy intentional and documented.
- Keep error handling present (
onError at minimum).
- Keep passes/targets routing valid.
- Keep imports on public entrypoints only.
- Keep storage buffer names declared before
writeStorageBuffer/readStorageBuffer usage.
- Keep adapter-specific API differences correct (
class vs className, errorRenderer, children, useSetMotionGPUUserContext).
- Keep checks/tests executed or report what was not run.