A scene is the createScene(root, surface, options) function a scene file exports, which returns the scene’s frame and destroy. Hosting it requires a device, error listeners, a surface, the scene and a scheduled frame loop, with cleanup in order. web.host handles these on the web, and useScene on React Native, for the same scene file.
Runs a scene on a canvas with a root, surface, frame loop and animation. destroy tears them
down in order. Use send to control the scene or onFrame to read it. If scene options are
optional, call host(createScene, { canvas }). Throws if both device and root are supplied.
@param ― createScene the scene file's createScene
@param ― options the scene's third argument
@param ― hosting the canvas, the device or root, and what the page runs beside the scene
@returns ― a hosted scene you send closures to and destroy
Receives the first scene error, from createScene, a frame, onFrame, ready or the device,
which stops the frames. Also receives each error a sent closure throws, which leaves the
frames running. Default console.error.
The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.
The offsetX read-only property of the MouseEvent interface provides the offset in the X coordinate of the mouse pointer between that event and the padding edge of the target node.
The offsetY read-only property of the MouseEvent interface provides the offset in the Y coordinate of the mouse pointer between that event and the padding edge of the target node.
Runs fn with the value on its runtime, after earlier sent calls. Any runtime can call it, so
a gesture on the UI runtime can control a scene on a worker. Worklets copies args and caches
objects by identity. If you change an object in place and send it again, the runtime receives
the first copy. Send a new object after a change.
send(({
scene: Game
scene})=>
scene: Game
scene.
Game.point(x: number, y: number): void
point(
offsetX: number
The offsetX read-only property of the MouseEvent interface provides the offset in the X coordinate of the mouse pointer between that event and the padding edge of the target node.
The offsetY read-only property of the MouseEvent interface provides the offset in the Y coordinate of the mouse pointer between that event and the padding edge of the target node.
The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.
send(fn, ...args) waits for scene creation and earlier sent work, then calls fn with { scene, root, surface, loop, ready }. run does the same and returns a promise for the result. It awaits async functions where the scene runs.
onFrame runs after the scene’s frame in every frame, for page code such as a HUD.
attach runs once the scene is up, with the stage. What it returns is disposed with the scene, so controls made there go with it: attach: ({ scene }) => web.orbitControls(scene.view, canvas).
stage.ready stays false while the scene’s ready promise is pending. Keep the loading screen visible until it resolves. A rejection reports a scene error.
The first scene error, from createScene, a frame, onFrame, ready or the device (an uncaptured error or its loss), goes to onError and stops the frames, so it shows once.
A sent closure that throws goes to onError each time and leaves the frames running, as an event handler’s error leaves requestAnimationFrame running.
To reach the stage directly from page code, take it once with const stage = await hosted.run((s) => s).
pause() and resume() pause the scene’s surface, and its frames and time with it. Like send, they wait for scene creation.
device takes a device of your own, which the host leaves alive. A test passes a recording root’s.
To time each pass of the scene, install the GPU debug panel before calling host.
destroy() releases everything in order, after what was sent before it, even while the scene is starting. Call it on pagehide, and from Vite’s import.meta.hot.dispose.
Omit scene options when they are optional, as in web.host(createScene, { canvas }).
In a React component, start the host in an effect and destroy it in the effect’s cleanup:
Runs a scene on a canvas with a root, surface, frame loop and animation. destroy tears them
down in order. Use send to control the scene or onFrame to read it. If scene options are
optional, call host(createScene, { canvas }). Throws if both device and root are supplied.
@param ― createScene the scene file's createScene
@param ― options the scene's third argument
@param ― hosting the canvas, the device or root, and what the page runs beside the scene
@returns ― a hosted scene you send closures to and destroy
Connects pointer input to an orbit camera's goal. Drag to orbit and use the wheel or pinch to
zoom. With pan, drag the other button to move the target. update eases the camera towards
the goal. Throws if pan is enabled but the goal has no target.
@param ― orbit an orbit camera, whose goal it writes
@param ― element what takes the drag, the wheel and the pinch
@returns ― the controls, which you destroy to remove the listeners
A page of several canvases shares one root rather than a device per scene. root takes a root you made, which the host leaves alive. schedule replaces requestAnimationFrame, and false runs no frames: the page runs them through the stage’s loop, such as to draw only the canvases on screen:
Runs a scene on a canvas with a root, surface, frame loop and animation. destroy tears them
down in order. Use send to control the scene or onFrame to read it. If scene options are
optional, call host(createScene, { canvas }). Throws if both device and root are supplied.
@param ― createScene the scene file's createScene
@param ― options the scene's third argument
@param ― hosting the canvas, the device or root, and what the page runs beside the scene
@returns ― a hosted scene you send closures to and destroy
Supplies a root that the host keeps alive, so scenes can share it. On React Native, requires
on: 'js'. Default a root that the host creates on device and destroys with the scene.
Supplies animation frames. With false, call stage.loop.run(seconds) to draw on demand.
Default requestAnimationFrame, or display vsync on a React Native worker.
Records and submits one frame with a fresh Frame and encoder. Requires finite, nondecreasing
seconds. Throws for an invalid time, a nested frame or an asynchronous recorder. Returns
false if the loop is destroyed before or during recording. Runs all after-submit callbacks. If
any throw, the frame still counts and run rethrows their errors.
Each host watches the device, and WebGPU doesn’t say which scene made an error, so the first uncaptured error or the loss reaches every scene’s onError.
Without a schedule the host has no frames to stop at an error; stop running the loop from onError.
@typegpu/kit/hosting is what web.host and React Native’s useScene are built from. It imports nothing from React Native.
stage(root, surface, createScene, options, { schedule?, onError, watch?, onFrame?, attach?, release? }) creates a scene and scheduled frame loop. schedule: false disables automatic frames. The first error from a frame, onFrame, ready or the watch device stops the frames and reaches onError once; fail(error) does the same for an error the host finds itself, and nothing once the stage is destroyed. destroy() stops frames, disposes attach’s result, destroys the loop and scene, then calls release to free host resources, even when the scene’s destroy throws.
watch(device, onError) calls onError with the device’s first uncaptured error, or its loss unless it was destroyed on purpose, until you dispose it.
local(create) keeps a value on the calling thread with send and run, matching runtimes.own on a worker, as in a test or on a web page.
Scene has frame, destroy and an optional ready. CreateScene creates it from (root, surface, options), and Stage holds it with host resources. HostOptions describes shared web.host and scenes.host options.
A view that draws only when asked, such as a model viewer, stages its scene on a shared root without a schedule, and runs a frame when the view moves:
Creates a scene on the host's root and surface, then creates its frames and animation. Destroys
them in reverse order. Owns the rule every host follows: the first error from a frame,
onFrame, ready or the watched device stops the frames and reaches onError once. If setup
fails after scene creation, destroys those resources and rethrows the error. The host keeps its
root and surface and destroys them in release.
@param ― createScene the scene file's createScene
@param ― surface the surface the scene draws into, which the host keeps
@param ― options the scene's third argument
@param ― hosting where frames come from, what takes their errors and what the host runs beside
@returns ― the stage, which you destroy before the root and the surface
Controls canvas transparency. With premultiplied, alpha below 1 reveals what is behind the
canvas, and colours must be premultiplied by alpha. Default opaque.
Creates a scene on the host's root and surface, then creates its frames and animation. Destroys
them in reverse order. Owns the rule every host follows: the first error from a frame,
onFrame, ready or the watched device stops the frames and reaches onError once. If setup
fails after scene creation, destroys those resources and rethrows the error. The host keeps its
root and surface and destroys them in release.
@param ― createScene the scene file's createScene
@param ― surface the surface the scene draws into, which the host keeps
@param ― options the scene's third argument
@param ― hosting where frames come from, what takes their errors and what the host runs beside
@returns ― the stage, which you destroy before the root and the surface
Watches the device from scene creation until destroy, and fails the stage on its first
uncaptured error or unexpected loss. Device events reach only the React Native JS thread, so
pass it there or on the web. Default none.
Receives the first scene error once, after the frames stop: from a frame, onFrame, a
rejected ready, the watched device or staged.fail. stage rethrows what createScene
throws instead.
Releases host resources, such as the surface and the root, at the end of destroy, also when
the scene's destroy throws. Does not run if stage throws. Default none.
release:()=>
constsurface:web.Surface
surface.
Surface.destroy(): void
Releases the drawing destination. Canvas surfaces unconfigure their contexts. Web surfaces
also stop observing the canvas. Later calls to view throw. Also [Symbol.dispose].
destroy()},
);
new
var ResizeObserver:new(callback:ResizeObserverCallback)=>ResizeObserver
The ResizeObserver interface reports changes to the dimensions of an Element's content or border box, or the bounding box of an SVGElement.
Records and submits one frame with a fresh Frame and encoder. Requires finite, nondecreasing
seconds. Throws for an invalid time, a nested frame or an asynchronous recorder. Returns
false if the loop is destroyed before or during recording. Runs all after-submit callbacks. If
any throw, the frame still counts and run rethrows their errors.
The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.
a root on the device it is given unless it was given a root ·the canvas's configuration ·a worker runtime unless it is given one ·the scene's stage and what its `attach` returned
lifecycle
✓destroy()✓[Symbol.dispose]
platform
nativeonly
on
Press send to run a closure when the scene runtime is free, then lengthen a frame beyond one vsync. In this model at one twenty-fifth speed, 'ui' drops app frames, while a worker keeps the UI running and counts missed scene frames in missed().
scenes.host hosts a scene, the createScene(root, surface, options) function that web.host takes, on a React Native canvas. It runs the scene on a worker runtime by default, or on the UI runtime or the JS thread. It owns a root on your device, the canvas’s configuration, the scene and the worker it creates, and schedules frames at display vsync. @typegpu/kit/runtimes provides the same runtime controls for code outside a kit scene. useScene wraps the host for a React Native canvas.
kit.react
platform
Hosts a scene on a react-native-webgpu <Canvas> through useScene.
Blockfall’s scene.ts runs unchanged on the web and on React Native. The screen passes blockfall, a worklet that calls createScene through a named import, so worklets loads scene.ts on the scene’s runtime with its shaders and modules. A 'worklet' line in createScene itself would copy the shaders it captures instead. If createScene isn’t a worklet, the hook’s error shows the line to write.
What the hook does for you:
A worker runs the scene by default, paced by vsync, with the timer floor set by runtimes.worker. Each component reuses one worker named after createScene, because worklets can’t destroy runtimes.
The hook uses the nearest <GPUDeviceProvider>’s device and checks it with native.checkDevice. Without a provider, it requests a device with the optionalFeatures the adapter has and destroys that device after releasing the scene.
The hook listens for uncaptured errors and device.lost on the JS thread with hosting.watch. The first error or loss releases the scene and sets error. Deliberate device destruction doesn’t count as loss. On a shared device, the error stops every scene because WebGPU doesn’t identify its source scene.
Remount the component with a different key to restart. A new <Canvas> under the same hook, such as one inside a remounted parent, restarts the scene on it. If a provider’s device is lost, remount the provider too.
Canvas layout changes resize the surface. AppState pauses and resumes it. Use paused to stop a canvas scrolled out of view.
Scheduled work catches errors. The first error sets error once. Every error but a sent closure’s stops the frames.
Top-level option callbacks use post to call the latest render’s function. An inline arrow doesn’t restart the scene.
Initial options come from the render that starts the scene. Send changes from an effect, as in useEffect(() => send?.(setWeather, weather), [send, weather]). Restarting the scene creates another send, so the effect resends the value.
Teardown runs on the scene’s runtime after any frame in flight and unconfigures the canvas. A remounted scene, including a StrictMode remount, starts after the previous teardown finishes.
on, in the hook’s third argument with the other options the card lists, takes 'worker', 'ui', 'js' or a runtime of your own. The scene is the same everywhere:
'worker' keeps scene work off the UI runtime. Pass a runtime as on to share one worker between components.
'ui' shares the UI runtime with Reanimated. Long scene frames delay the app’s UI. Use it when Reanimated must read shared values in the same frame.
'js' runs at most 60 Hz and needs no worklet wrapper. React Native DevTools can debug this runtime. Use it to debug a scene that normally runs on a worker.
send runs a closure on the scene’s runtime with the stage, which holds the scene, root, surface and loop. A closure reaches anything there, so the scene needs no message schema. run does the same and returns the result to React Native as a promise.
Call scenes.host directly in an app with its own lifecycle. Pass the device, the canvas’s context, its layout size and the pixel ratio, and call resize, pause, resume and destroy yourself.
It accepts web.host’s schedule, onFrame, attach and onError options. It also accepts canvas format, alphaMode, usage, colorSpace, toneMapping and a fixed size. Off the JS thread, onFrame and attach must be worklets. The host throws an error naming a callback that isn’t one.
Supply root instead of a device only with on: 'js'. A root must stay on the thread that created it.
schedule replaces display vsync. With false, no frames run automatically. Send ({ loop }) => loop.run(seconds) yourself.
The device’s errors reach only the JS thread. With on: 'js' the host watches the device; elsewhere, listen with hosting.watch(device, onError), as useScene does.
For a scene with two canvases or none, start it with the runtime pieces below.
Runs the loop on each scheduled frame. Time advances by at most maxDt per frame, never steps
back, and stops while the surface is paused. Destroying the animation keeps the loop alive.
Another animation on the same loop continues from its recorded time. A frame error stops the
animation.
@param ― loop the loop to run on each unpaused frame. Continues from its seconds if it ran before
@returns ― an animation you destroy to stop the frames
Creates a react-native-webgpu canvas surface for presentation on the JS thread. Throws if the
canvas has no WebGPU context or the fixed size is invalid.
@param ― canvas the CanvasRef from a <Canvas ref>; its context's canvas holds the size
@param ― options.pixelRatio PixelRatio.get()
@returns ― a surface you resize, pause, resume and destroy
Creates a react-native-webgpu canvas surface for presentation on the JS thread. Throws if the
canvas has no WebGPU context or the fixed size is invalid.
@param ― canvas the CanvasRef from a <Canvas ref>; its context's canvas holds the size
@param ― options.pixelRatio PixelRatio.get()
@returns ― a surface you resize, pause, resume and destroy
Creates a worker runtime with WebGPU installed and a minimum delay for zero-delay timers.
Worklets cannot end a runtime. It lives until nothing references it.
Relays display-linked frames from the UI runtime to a worker. A worker's own
requestAnimationFrame uses a 16 ms timer. Sends one frame at a time, skipping and counting
display frames that arrive while it runs. Pass it as schedule to frames.animate on that
runtime.
@param ― runtime the only runtime allowed to request these frames
@returns ― a schedule you destroy to stop the relay
vsync(
constworker:WorkletRuntime
worker);
exportinterface
interfaceStart
Start{
readonly
Start.device: GPUDevice
device:
interfaceGPUDevice
The GPUDevice interface of the WebGPU API represents a logical GPU device. This is the main interface through which the majority of WebGPU functionality is accessed.
Available only in secure contexts.
The GPUCanvasContext interface of the WebGPU API represents the WebGPU rendering context of a element, returned via an HTMLCanvasElement.getContext() call with a contextType of "webgpu".
Available only in secure contexts.
Calls fn on the React Native JS thread from any runtime. Copies arguments first, because
worklets caches sent objects by identity. Otherwise, changing an object in place and sending it
again delivers its first copy. Release builds omit the warning for this case.
Creates and keeps a value on runtime. create must be a worklet and may be asynchronous.
Calls sent before it resolves wait in order. A worklets Shareable holds the value on that
runtime. Nothing registers or looks it up by name.
@param ― runtime where the value lives
@param ― create a worklet that makes the value, there
@returns ― a handle you send closures to and destroy
Creates a react-native-webgpu canvas surface for presentation on the JS thread. Throws if the
canvas has no WebGPU context or the fixed size is invalid.
@param ― canvas the CanvasRef from a <Canvas ref>; its context's canvas holds the size
@param ― options.pixelRatio PixelRatio.get()
@returns ― a surface you resize, pause, resume and destroy
Runs the loop on each scheduled frame. Time advances by at most maxDt per frame, never steps
back, and stops while the surface is paused. Destroying the animation keeps the loop alive.
Another animation on the same loop continues from its recorded time. A frame error stops the
animation.
@param ― loop the loop to run on each unpaused frame. Continues from its seconds if it ran before
@returns ― an animation you destroy to stop the frames
animate(
constloop:Frames
loop,{
AnimateOptions.surface?:{
readonly paused: boolean;
}|undefined
Stops frames and elapsed time while paused is true. The first frame after resume() has a
dt of 0. On React Native, pass the canvas surface and pause it from AppState. On the web,
pause it from an IntersectionObserver or a pause menu. Without a surface, frames run until
the animation is destroyed. Default none.
Supplies animation frames. Use runtimes.vsync(worker) on a React Native worker runtime, or a
schedule you step by hand in a test. Default requestAnimationFrame.
Receives a frame error after the animation stops. Without a handler, the animation frame
callback throws the error. Default none.
onError:
var console:Console
console.
Console.error(...data: any[]): void(+1 overload)
The console.error() static method outputs a message to the console at the "error" log level. The message is only displayed to the user if the console is configured to display error output. In most cases, the log level is configured within the console UI. The message may be formatted as an error, with red colors and call stack information.
Stops requesting frames, even when called from a frame. Keeps the loop alive. Also
[Symbol.dispose].
destroy();
constloop:Frames
loop.
Frames.destroy(): void
Stops future submissions and keeps borrowed resources alive. Also [Symbol.dispose].
destroy();
constscene:{
frame(frame:Frame):void;
destroy():void;
game:{
drop():void;
};
}
scene.
functiondestroy():void
destroy();
constsurface:Surface
surface.
Surface.destroy(): void
Releases the drawing destination. Canvas surfaces unconfigure their contexts. Web surfaces
also stop observing the canvas. Later calls to view throw. Also [Symbol.dispose].
Receives errors from create, sent closures and calls that worklets cannot schedule. An
uncaught error in a worklet otherwise aborts a release build. Default console.error on the
runtime that owns the value.
onError:
var console:Console
console.
Console.error(...data: any[]): void(+1 overload)
The console.error() static method outputs a message to the console at the "error" log level. The message is only displayed to the user if the console is configured to display error output. In most cases, the log level is configured within the console UI. The message may be formatted as an error, with red colors and call stack information.
own keeps any value returned by create. The callback can use the same setup code as a web page.
Named imports let worklets load createScene, createFrames, animate and tgpu on the worker, including their shaders. Namespace imports such as import * as frames copy the namespace instead. Its functions can’t be called on that runtime, so the worklet uses none.
send(fn, ...args) runs fn with the value on its runtime, after everything sent before it; calls made while create runs wait for it. send is a worklet itself, so a Gesture Handler callback on the UI runtime sends straight to the worker.
run does the same from React Native and copies the result back as post does. An async closure is awaited on the value’s runtime, since a promise can’t cross runtimes.
send reuses cached argument copies. If you mutate an object and resend it, the receiver gets its first copy. Send another object to transfer changed values.
Errors from create, sent closures or scheduling send reach onError. destroy waits for earlier sent work, calls the value’s destroy on its runtime and releases it.
vsync(worker) relays the UI runtime’s display link. A worker requests a frame and the next vsync schedules it there. If the previous frame is still running, the relay skips that vsync and increments missed(). Give it to animate as schedule, which defaults to requestAnimationFrame; a test passes a schedule it steps by hand.
// the scene's frame, on the worker; `here` is an object the scene made once
pointer.read(here);
A cell is one fixed-type synchronizable per field, so a read takes no lock and allocates nothing. Fields are read one at a time, so a read can mix two writes.
It holds the latest value only. Send events that mustn’t be lost, like a tap or a key, through send, which queues them in order.
post(fn) calls React Native code with copied arguments. A reused object arrives with its values at the time of each call.
Worklet callbacks run on the calling runtime. Other callbacks run on React Native with copied arguments.
A renderer of your own.own(worker, createRenderer) holds it, and vsync paces it. Nothing needs frames or a TypeGPU Kit scene.
@typegpu/react. The resources it transfers keep working; runtimes registers no serializer of its own.
Reanimated and Gesture Handler. Gesture worklets write cells or call send. A shared value can go in a scene’s options, but reading one on a worker waits for the UI thread.
A runtime someone else made.own and vsync take any runtime.
Compiling off the frame’s runtime.own(worker, warm) compiles pipelines while the UI runtime draws. Dawn shares its pipeline cache across runtimes.
In a test, a scene builds on recordingRoot() with no worklets. local gives the same Owned on the test’s thread, and a hand-stepped schedule drives animate.
local lives in @typegpu/kit/hosting with Owned and the scene types, which import nothing from React Native, so a test or a web page needs no mock of worklets. runtimes re-exports it.
runtimes and scenes import react-native-worklets; only scenes/react imports React.
runtimes.cell needs worklets, so in its test a scene that reads one takes any object with the same read and write:
the device, device.lost, React state, the hook, assets
UI runtime
the display link and its relay, Gesture Handler’s callbacks, Reanimated
the scene’s runtime
the root, the modules, recording, submit, present and readback maps
Use one runtime to record into a canvas and its encoders. Other runtimes may share the device with implicit-device-synchronization, which react-native-webgpu requests by default. They must not share that canvas or those encoders.