This page covers useMotionGPU() for runtime context access, usePointer() for normalized pointer input, the CurrentWritable pattern for synchronous reads, and recommended integration patterns.
For advanced user-defined context, see User Context (Advanced).
useMotionGPU()
useMotionGPU() returns the MotionGPUContext — the primary API for reading runtime state and controlling rendering from within FragCanvas children.
<script lang="ts">
import { useMotionGPU } from '@motion-core/motion-gpu/svelte';
const gpu = useMotionGPU();
</script><script lang="ts">
import { useMotionGPU } from '@motion-core/motion-gpu/svelte';
const gpu = useMotionGPU();
</script>import { useMotionGPU } from '@motion-core/motion-gpu/react';
function Component() {
const gpu = useMotionGPU();
}import { useMotionGPU } from '@motion-core/motion-gpu/react';
function Component() {
const gpu = useMotionGPU();
}<script setup lang="ts">
import { useMotionGPU } from '@motion-core/motion-gpu/vue';
const gpu = useMotionGPU();
</script><script setup lang="ts">
import { useMotionGPU } from '@motion-core/motion-gpu/vue';
const gpu = useMotionGPU();
</script>Must be called inside a <FragCanvas> component tree. Calling it outside throws.
usePointer()
usePointer() provides adapter-managed pointer tracking (mouse, touch, pen) with shader-friendly coordinates:
state.current.uvin0..1with Y-up orientationstate.current.ndcin-1..1- optional synthesized click snapshots via
lastClick.current - optional automatic frame wakeups (
requestFrame: 'auto')
Use it to remove canvas event-listener boilerplate in runtime components.
MotionGPUContext shape
Reactive stores
| Store | Type | Description |
|---|---|---|
gpu.size | CurrentReadable<{ width: number; height: number }> | Canvas size in CSS pixels (from getBoundingClientRect) |
gpu.dpr | CurrentWritable<number> | Device pixel ratio |
gpu.maxDelta | CurrentWritable<number> | Max frame delta (seconds) |
gpu.renderMode | CurrentWritable<RenderMode> | Current render mode |
gpu.autoRender | CurrentWritable<boolean> | Auto-render gate |
gpu.user | CurrentWritable<Record<string symbol, unknown>> | User-defined namespace store |
Direct values
| Property | Type | Description |
|---|---|---|
gpu.canvas | HTMLCanvasElement undefined | The active canvas element (undefined until mounted) |
Control methods
| Method | Description |
|---|---|
gpu.invalidate() | Request re-render in on-demand mode |
gpu.advance() | Request exactly one frame in manual mode |
Scheduler access
| Method | Description |
|---|---|
gpu.scheduler.createStage(key, options) | Creates a named execution stage |
gpu.scheduler.getSchedule() | Returns resolved stage/task order |
gpu.scheduler.setDiagnosticsEnabled(flag) | Enable/disable single-frame timing |
gpu.scheduler.setProfilingEnabled(flag) | Enable/disable rolling profiling |
gpu.scheduler.setProfilingWindow(n) | Set rolling window size |
gpu.scheduler.getLastRunTimings() | Last frame’s timing data |
gpu.scheduler.getProfilingSnapshot() | Aggregate profiling stats |
gpu.scheduler.resetProfiling() | Clear profiling history |
For higher-level scheduler configuration and debug snapshots, use the advanced helper exports:
import { applySchedulerPreset, captureSchedulerDebugSnapshot } from '@motion-core/motion-gpu/advanced';import { applySchedulerPreset, captureSchedulerDebugSnapshot } from '@motion-core/motion-gpu/advanced';The CurrentWritable / CurrentReadable pattern
Store-like subscriptions require setup/teardown to read values reactively. In useFrame callbacks (which run 60 times per second), subscribing and unsubscribing per read is wasteful.
CurrentWritable<T> and CurrentReadable<T> extend reactive store contracts with a synchronous .current getter:
// Subscription read — requires subscribe/unsubscribe
let value: number;
const unsub = gpu.maxDelta.subscribe(v => { value = v; });
// ... use value ...
unsub();
// CurrentWritable — zero-overhead synchronous read
const value = gpu.maxDelta.current; // Instant, no subscription// Subscription read — requires subscribe/unsubscribe
let value: number;
const unsub = gpu.maxDelta.subscribe(v => { value = v; });
// ... use value ...
unsub();
// CurrentWritable — zero-overhead synchronous read
const value = gpu.maxDelta.current; // Instant, no subscriptionAPI
| Method | Description |
|---|---|
.current | Synchronous read of the latest value |
.set(value) | Update the value (writable only) |
.subscribe(fn) | Standard reactive-store style subscription |
When to use which
| Context | Use |
|---|---|
Inside useFrame callbacks | .current (runs 60+ times/sec) |
Framework reactive bindings ($store, useSyncExternalStore, ref + .subscribe, etc.) | Standard .subscribe integration |
| One-off reads outside reactive blocks | .current |
Integration patterns
Reading canvas size
<script lang="ts">
import { useMotionGPU } from '@motion-core/motion-gpu/svelte';
const gpu = useMotionGPU();
</script>
<p>Canvas: {$gpu.size.width}×{$gpu.size.height} @ {$gpu.dpr}x</p><script lang="ts">
import { useMotionGPU } from '@motion-core/motion-gpu/svelte';
const gpu = useMotionGPU();
</script>
<p>Canvas: {$gpu.size.width}×{$gpu.size.height} @ {$gpu.dpr}x</p>import { useMotionGPU } from '@motion-core/motion-gpu/react';
import { useSyncExternalStore } from 'react';
function Component() {
const gpu = useMotionGPU();
const size = useSyncExternalStore(gpu.size.subscribe, () => gpu.size.current);
const dpr = useSyncExternalStore(gpu.dpr.subscribe, () => gpu.dpr.current);
return <p>Canvas: {size.width}×{size.height} @ {dpr}x</p>;
}import { useMotionGPU } from '@motion-core/motion-gpu/react';
import { useSyncExternalStore } from 'react';
function Component() {
const gpu = useMotionGPU();
const size = useSyncExternalStore(gpu.size.subscribe, () => gpu.size.current);
const dpr = useSyncExternalStore(gpu.dpr.subscribe, () => gpu.dpr.current);
return <p>Canvas: {size.width}×{size.height} @ {dpr}x</p>;
}<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue';
import { useMotionGPU } from '@motion-core/motion-gpu/vue';
const gpu = useMotionGPU();
const size = ref(gpu.size.current);
const dpr = ref(gpu.dpr.current);
let unsubs: (() => void)[] = [];
onMounted(() => {
unsubs.push(gpu.size.subscribe((v) => (size.value = v)));
unsubs.push(gpu.dpr.subscribe((v) => (dpr.value = v)));
});
onBeforeUnmount(() => unsubs.forEach((u) => u()));
</script>
<template>
<p>Canvas: {{ size.width }}×{{ size.height }} @ {{ dpr }}x</p>
</template><script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue';
import { useMotionGPU } from '@motion-core/motion-gpu/vue';
const gpu = useMotionGPU();
const size = ref(gpu.size.current);
const dpr = ref(gpu.dpr.current);
let unsubs: (() => void)[] = [];
onMounted(() => {
unsubs.push(gpu.size.subscribe((v) => (size.value = v)));
unsubs.push(gpu.dpr.subscribe((v) => (dpr.value = v)));
});
onBeforeUnmount(() => unsubs.forEach((u) => u()));
</script>
<template>
<p>Canvas: {{ size.width }}×{{ size.height }} @ {{ dpr }}x</p>
</template>Conditional rendering control
<script lang="ts">
import { useMotionGPU } from '@motion-core/motion-gpu/svelte';
const gpu = useMotionGPU();
function togglePause() {
gpu.autoRender.set(!gpu.autoRender.current);
}
</script>
<button onclick={togglePause}>
{$gpu.autoRender ? 'Pause' : 'Resume'}
</button><script lang="ts">
import { useMotionGPU } from '@motion-core/motion-gpu/svelte';
const gpu = useMotionGPU();
function togglePause() {
gpu.autoRender.set(!gpu.autoRender.current);
}
</script>
<button onclick={togglePause}>
{$gpu.autoRender ? 'Pause' : 'Resume'}
</button>import { useMotionGPU } from '@motion-core/motion-gpu/react';
import { useSyncExternalStore } from 'react';
function Component() {
const gpu = useMotionGPU();
const autoRender = useSyncExternalStore(
gpu.autoRender.subscribe,
() => gpu.autoRender.current
);
function togglePause() {
gpu.autoRender.set(!gpu.autoRender.current);
}
return (
<button onClick={togglePause}>
{autoRender ? 'Pause' : 'Resume'}
</button>
);
}import { useMotionGPU } from '@motion-core/motion-gpu/react';
import { useSyncExternalStore } from 'react';
function Component() {
const gpu = useMotionGPU();
const autoRender = useSyncExternalStore(
gpu.autoRender.subscribe,
() => gpu.autoRender.current
);
function togglePause() {
gpu.autoRender.set(!gpu.autoRender.current);
}
return (
<button onClick={togglePause}>
{autoRender ? 'Pause' : 'Resume'}
</button>
);
}<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue';
import { useMotionGPU } from '@motion-core/motion-gpu/vue';
const gpu = useMotionGPU();
const autoRender = ref(gpu.autoRender.current);
let unsub: (() => void) | undefined;
onMounted(() => {
unsub = gpu.autoRender.subscribe((v) => (autoRender.value = v));
});
onBeforeUnmount(() => unsub?.());
function togglePause() {
gpu.autoRender.set(!gpu.autoRender.current);
}
</script>
<template>
<button @click="togglePause">
{{ autoRender ? 'Pause' : 'Resume' }}
</button>
</template><script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue';
import { useMotionGPU } from '@motion-core/motion-gpu/vue';
const gpu = useMotionGPU();
const autoRender = ref(gpu.autoRender.current);
let unsub: (() => void) | undefined;
onMounted(() => {
unsub = gpu.autoRender.subscribe((v) => (autoRender.value = v));
});
onBeforeUnmount(() => unsub?.());
function togglePause() {
gpu.autoRender.set(!gpu.autoRender.current);
}
</script>
<template>
<button @click="togglePause">
{{ autoRender ? 'Pause' : 'Resume' }}
</button>
</template>On-demand with external triggers
<script lang="ts">
import { useMotionGPU, useFrame, usePointer } from '@motion-core/motion-gpu/svelte';
const gpu = useMotionGPU();
const pointer = usePointer({ requestFrame: 'auto' });
$effect(() => {
gpu.renderMode.set('on-demand');
});
useFrame((state) => {
state.setUniform('uTime', state.time);
state.setUniform('uMouse', pointer.state.current.uv);
}, { autoInvalidate: false });
</script><script lang="ts">
import { useMotionGPU, useFrame, usePointer } from '@motion-core/motion-gpu/svelte';
const gpu = useMotionGPU();
const pointer = usePointer({ requestFrame: 'auto' });
$effect(() => {
gpu.renderMode.set('on-demand');
});
useFrame((state) => {
state.setUniform('uTime', state.time);
state.setUniform('uMouse', pointer.state.current.uv);
}, { autoInvalidate: false });
</script>import { useMotionGPU, useFrame, usePointer } from '@motion-core/motion-gpu/react';
import { useEffect } from 'react';
function Component() {
const gpu = useMotionGPU();
const pointer = usePointer({ requestFrame: 'auto' });
useEffect(() => {
gpu.renderMode.set('on-demand');
}, [gpu]);
useFrame((state) => {
state.setUniform('uTime', state.time);
state.setUniform('uMouse', pointer.state.current.uv);
}, { autoInvalidate: false });
return null;
}import { useMotionGPU, useFrame, usePointer } from '@motion-core/motion-gpu/react';
import { useEffect } from 'react';
function Component() {
const gpu = useMotionGPU();
const pointer = usePointer({ requestFrame: 'auto' });
useEffect(() => {
gpu.renderMode.set('on-demand');
}, [gpu]);
useFrame((state) => {
state.setUniform('uTime', state.time);
state.setUniform('uMouse', pointer.state.current.uv);
}, { autoInvalidate: false });
return null;
}<script setup lang="ts">
import { onMounted } from 'vue';
import { useMotionGPU, useFrame, usePointer } from '@motion-core/motion-gpu/vue';
const gpu = useMotionGPU();
const pointer = usePointer({ requestFrame: 'auto' });
onMounted(() => {
gpu.renderMode.set('on-demand');
});
useFrame((state) => {
state.setUniform('uTime', state.time);
state.setUniform('uMouse', pointer.state.current.uv);
}, { autoInvalidate: false });
</script><script setup lang="ts">
import { onMounted } from 'vue';
import { useMotionGPU, useFrame, usePointer } from '@motion-core/motion-gpu/vue';
const gpu = useMotionGPU();
const pointer = usePointer({ requestFrame: 'auto' });
onMounted(() => {
gpu.renderMode.set('on-demand');
});
useFrame((state) => {
state.setUniform('uTime', state.time);
state.setUniform('uMouse', pointer.state.current.uv);
}, { autoInvalidate: false });
</script>