Core Concepts

Hooks & Context

Accessing runtime state and controlling rendering with useMotionGPU and context hooks.


This page covers the

useMotionGPU()
hook for accessing the runtime context, the
CurrentWritable
pattern for synchronous value access, 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';

  const gpu = useMotionGPU();
</script>
<script lang="ts">
  import { useMotionGPU } from '@motion-core/motion-gpu';

  const gpu = useMotionGPU();
</script>

Must be called inside a

<FragCanvas>
component tree. Calling it outside throws.

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

Standard Svelte stores require subscription to read values. In

useFrame
callbacks (which run 60 times per second), subscribing and unsubscribing per read is wasteful.

CurrentWritable<T>
and
CurrentReadable<T>
extend Svelte store contracts with a synchronous
.current
getter:

// Standard Svelte store — requires subscription
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
// Standard Svelte store — requires subscription
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

API

Method Description
.current
Synchronous read of the latest value
.set(value)
Update the value (writable only)
.subscribe(fn)
Standard Svelte subscription

When to use which

Context Use
Inside
useFrame
callbacks
.current
(runs 60+ times/sec)
Svelte reactive blocks (
$effect
, templates)
Standard
$store
subscription
One-off reads outside reactive blocks
.current

Integration patterns

Reading canvas size

<script lang="ts">
  import { useMotionGPU } from '@motion-core/motion-gpu';
  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';
  const gpu = useMotionGPU();
</script>

<p>Canvas: {$gpu.size.width}×{$gpu.size.height} @ {$gpu.dpr}x</p>

Conditional rendering control

<script lang="ts">
  import { useMotionGPU } from '@motion-core/motion-gpu';
  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';
  const gpu = useMotionGPU();

  function togglePause() {
    gpu.autoRender.set(!gpu.autoRender.current);
  }
</script>

<button onclick={togglePause}>
  {$gpu.autoRender ? 'Pause' : 'Resume'}
</button>

On-demand with external triggers

<script lang="ts">
  import { useMotionGPU, useFrame } from '@motion-core/motion-gpu';

  const gpu = useMotionGPU();

  $effect(() => {
    gpu.renderMode.set('on-demand');
  });

  $effect(() => {
    const canvas = gpu.canvas;
    if (!canvas) return;

    const handler = () => gpu.invalidate();
    canvas.addEventListener('pointermove', handler);
    return () => canvas.removeEventListener('pointermove', handler);
  });

  useFrame((state) => {
    state.setUniform('uTime', state.time);
  }, { autoInvalidate: false });
</script>
<script lang="ts">
  import { useMotionGPU, useFrame } from '@motion-core/motion-gpu';

  const gpu = useMotionGPU();

  $effect(() => {
    gpu.renderMode.set('on-demand');
  });

  $effect(() => {
    const canvas = gpu.canvas;
    if (!canvas) return;

    const handler = () => gpu.invalidate();
    canvas.addEventListener('pointermove', handler);
    return () => canvas.removeEventListener('pointermove', handler);
  });

  useFrame((state) => {
    state.setUniform('uTime', state.time);
  }, { autoInvalidate: false });
</script>