The frame scheduler manages all per-frame logic in MotionGPU. Every
useFrameuseFrameFor render modes, stages, and profiling, see Render Modes and Scheduling.
Basic usage
<script lang="ts">
import { useFrame } from '@motion-core/motion-gpu';
useFrame((state) => {
state.setUniform('uTime', state.time);
});
</script><script lang="ts">
import { useFrame } from '@motion-core/motion-gpu';
useFrame((state) => {
state.setUniform('uTime', state.time);
});
</script>useFrame<FragCanvas>Overloads
| Signature | Description |
|---|---|
useFrame(callback, options?) | Auto-generated task key |
useFrame(key, callback, options?) | Explicit stable key for ordering and debugging |
// Auto key
useFrame((state) => { ... });
// Explicit key
useFrame('camera-update', (state) => { ... });
// With options
useFrame('particles', (state) => { ... }, {
stage: particleStage,
autoInvalidate: false
});// Auto key
useFrame((state) => { ... });
// Explicit key
useFrame('camera-update', (state) => { ... });
// With options
useFrame('particles', (state) => { ... }, {
stage: particleStage,
autoInvalidate: false
});Return value
useFrame| Field | Type | Description |
|---|---|---|
task | { key: FrameKey; stage: FrameKey } | Task and stage identifiers |
start() | () => void | Enables the task (resumes execution) |
stop() | () => void | Disables the task (paused, not removed) |
started | Readable<boolean> | Reactive store for effective running state |
const handle = useFrame('animation', (state) => {
state.setUniform('uTime', state.time);
});
// Pause animation
handle.stop();
// Resume
handle.start();const handle = useFrame('animation', (state) => {
state.setUniform('uTime', state.time);
});
// Pause animation
handle.stop();
// Resume
handle.start();FrameState callback API
The
state| Field | Type | Description |
|---|---|---|
time | number | requestAnimationFrame |
delta | number | Time since last frame in seconds (clamped by maxDelta |
canvas | HTMLCanvasElement | Active canvas element |
renderMode | RenderMode | Current render mode |
autoRender | boolean | Current auto-render state |
setUniform(name, value) | function | Set a runtime uniform override |
setTexture(name, value) | function | Set a runtime texture source |
invalidate(token?) | function | Trigger invalidation for on-demand mode |
advance() | function | Request one frame in manual mode |
Example: complete runtime component
<script lang="ts">
import { useFrame, useMotionGPU } from '@motion-core/motion-gpu';
const gpu = useMotionGPU();
let mouseX = 0.5;
let mouseY = 0.5;
$effect(() => {
const canvas = gpu.canvas;
if (!canvas) return;
const onMove = (e: PointerEvent) => {
const rect = canvas.getBoundingClientRect();
mouseX = (e.clientX - rect.left) / rect.width;
mouseY = 1.0 - (e.clientY - rect.top) / rect.height;
};
canvas.addEventListener('pointermove', onMove);
return () => canvas.removeEventListener('pointermove', onMove);
});
useFrame((state) => {
state.setUniform('uTime', state.time);
state.setUniform('uMouse', [mouseX, mouseY]);
});
</script><script lang="ts">
import { useFrame, useMotionGPU } from '@motion-core/motion-gpu';
const gpu = useMotionGPU();
let mouseX = 0.5;
let mouseY = 0.5;
$effect(() => {
const canvas = gpu.canvas;
if (!canvas) return;
const onMove = (e: PointerEvent) => {
const rect = canvas.getBoundingClientRect();
mouseX = (e.clientX - rect.left) / rect.width;
mouseY = 1.0 - (e.clientY - rect.top) / rect.height;
};
canvas.addEventListener('pointermove', onMove);
return () => canvas.removeEventListener('pointermove', onMove);
});
useFrame((state) => {
state.setUniform('uTime', state.time);
state.setUniform('uMouse', [mouseX, mouseY]);
});
</script>UseFrameOptions
| Option | Type | Default | Description |
|---|---|---|---|
autoStart | boolean | true | Whether the task starts enabled |
autoInvalidate | boolean | true | Auto-invalidate every frame (for on-demand mode) |
invalidation | FrameTaskInvalidation | Implicit | Explicit invalidation policy override |
stage | FrameKeyFrameStage | Main stage | Which stage to assign the task to |
before | task ref or list | undefined | This task must run before the specified task(s) |
after | task ref or list | undefined | This task must run after the specified task(s) |
running | () => boolean | undefined | Dynamic gate — task is skipped when this returns false |
Invalidation policies
The invalidation policy controls whether a task’s execution triggers a re-render in
on-demandSimple policies
| Value | Behaviour |
|---|---|
'never' | Task never triggers invalidation |
'always' | Task always triggers invalidation |
Object policies
// Always invalidate with a token
{ mode: 'always', token: 'my-task' }
// Never invalidate
{ mode: 'never' }
// On-change: invalidate only when token value changes
{ mode: 'on-change', token: () => someReactiveValue }// Always invalidate with a token
{ mode: 'always', token: 'my-task' }
// Never invalidate
{ mode: 'never' }
// On-change: invalidate only when token value changes
{ mode: 'on-change', token: () => someReactiveValue }on-change semantics
on-changeWith
on-changeuseFrame('background-inset', (state) => {
state.setUniform('uColor', currentColor);
}, {
autoInvalidate: false,
invalidation: {
mode: 'on-change',
token: () => currentColor.join(',')
}
});useFrame('background-inset', (state) => {
state.setUniform('uColor', currentColor);
}, {
autoInvalidate: false,
invalidation: {
mode: 'on-change',
token: () => currentColor.join(',')
}
});Task ordering with dependencies
Use
beforeafterconst physics = useFrame('physics', (state) => {
// Update physics simulation
});
const render = useFrame('render-update', (state) => {
// Read physics results
}, {
after: physics.task
});const physics = useFrame('physics', (state) => {
// Update physics simulation
});
const render = useFrame('render-update', (state) => {
// Read physics results
}, {
after: physics.task
});Dependencies are resolved via topological sort.
- Circular dependencies throw a deterministic error.
- Missing /
beforereferences throw a deterministic error.after
Lifecycle
- automatically unregisters the task when the component is destroyed (via Svelte’s
useFrame).onDestroy - The /
stop()methods pause/resume without unregistering — the task remains in the schedule but is skipped.start() - The gate is checked each frame — a stopped task or a task with
runningis simply skipped, not removed.running: () => false