Skip to content

@typegpu/kit/web, @typegpu/kit/nativeweb · native

Surfaces

A canvas configured for your root, with a drawing buffer that follows its layout, on the web and React Native.

kit.web

platform

Hosts scenes on web canvases and provides surfaces, GPU pacing, pointer input and orbit controls.

web.surface(root, canvas, options?) → Surface

web.paced(queue, options?) → Schedule

web.orbitControls(orbit, element, options?) → OrbitControls

web.pointer(element, options?) → Pointer

and web.host, web.video

rootborrows
the root
canvastakes
the canvas it configures and sizes
Surfacereturns
a surface you draw into and destroy
queuetakes
the device's queue, root.device.queue
Schedulereturns
a schedule to give animate
orbittakes
an orbit camera, whose goal it writes
elementtakes
what takes the drag, the wheel and the pinch
OrbitControlsreturns
the controls, which you destroy to remove the listeners
elementtakes
what the pointer moves over, usually the canvas
Pointerreturns
the pointer, which you destroy to remove the listeners
types
CreateSceneSceneFixedSizeSurfaceOrbitGoalOrbitControlsPointerHandlerPointerStageHostedVideo
SurfaceOptionssurface · 7 fields
size?

FixedSize= the layout size multiplied by the capped pixel ratio, following layout changes

Fixes the drawing-buffer size before surface returns.

maxPixelRatio?

number= 2

Caps drawing-buffer pixels per CSS pixel unless size is set.

format?

GPUTextureFormat= navigator.gpu.getPreferredCanvasFormat()

Sets the canvas texture format.

alphaMode?

GPUCanvasAlphaMode= opaque

Controls canvas transparency.

usage?

GPUTextureUsageFlags= GPUTextureUsage.RENDER_ATTACHMENT

Sets the canvas texture usage flags.

colorSpace?

PredefinedColorSpace= 'srgb'

Sets the colour space of canvas values.

toneMapping?

GPUCanvasToneMapping= { mode: 'standard' }

Controls the range of canvas values for display.

PacedOptionspaced · 1 field
inFlight?

number= 2

Limits the number of frames whose GPU work may be unfinished when the next frame starts.

OrbitControlsOptionsorbitControls · 6 fields
sensitivity?

number= 0.008, as cameras.turn

Sets rotation per dragged pixel, in radians.

zoomSpeed?

number= 0.001

Sets wheel zoom speed.

button?

'primary' | 'secondary'= 'primary'

Orbits with this mouse button.

pan?

boolean= false

Enables panning with the secondary button, or the middle one when it orbits, and with two fingers moving together.

page?

boolean= false, so the element handles every drag and wheel input

Allows page scrolling around an embedded view.

onChange?

() => void= none

Runs after each input that changes the goal, for views that draw on demand.

PointerOptionspointer · 7 fields
move?

PointerHandler= none

Runs as the pointer moves over the element, or anywhere while it holds a button down.

down?

PointerHandler= none

Runs as a button goes down on the element.

up?

PointerHandler= none

Runs as a button comes up, or as the browser cancels the pointer.

leave?

() => void= none

Runs as the pointer leaves the element with no button down.

zoom?

(by: number) => void= none

Runs as the wheel turns or two fingers pinch on the element, with the factor to scale a distance by, above 1 farther: e^(deltaY × 0.001) for the wheel, and the ratio of the fingers' last spread to the new one for a pinch.

page?

boolean= false, so the element takes every touch drag and wheel turn

Allows page scrolling around an embedded element: touch drags scroll the page instead of reaching move, and only the wheel with Ctrl or Command held zooms.

menu?

boolean= true

Shows the context menu on a secondary click.

HostOptionshost · 15 fields
canvas

HTMLCanvasElement

Supplies the canvas for the scene.

optionalFeatures?

Iterable<GPUFeatureName>= none

Requests these features when the adapter supports them, such as timestamp-query.

device?

GPUDevice= a device that the web host requests and destroys with the scene

Supplies a device that the host keeps alive.

root?

TgpuRoot= a root that the host creates on device and destroys with the scene

Supplies a root that the host keeps alive, so scenes can share it.

schedule?

Schedule | false= requestAnimationFrame, or display vsync on a React Native worker

Supplies animation frames.

onFrame?

(stage: Stage<S, Canvas>, frame: Frame) => void= none

Runs after the scene's frame each frame, for page work such as a HUD that reads scene state.

attach?

(stage: Stage<S, Canvas>) => Disposable | void= none

Connects page code, such as orbit controls, to the stage after scene creation.

onError?

Callback<[unknown]>= console.error

Receives the first scene error, from createScene, a frame, onFrame, ready or the device, which stops the frames.

format?

GPUTextureFormat= navigator.gpu.getPreferredCanvasFormat()

Sets the canvas texture format.

alphaMode?

GPUCanvasAlphaMode= opaque

Controls canvas transparency.

usage?

GPUTextureUsageFlags= GPUTextureUsage.RENDER_ATTACHMENT

Sets the canvas texture usage flags.

colorSpace?

PredefinedColorSpace= 'srgb'

Sets the colour space of canvas values.

toneMapping?

GPUCanvasToneMapping= { mode: 'standard' }

Controls the range of canvas values for display.

size?

FixedSize= the layout size multiplied by the capped pixel ratio, following layout changes

Fixes the drawing-buffer size before surface returns.

maxPixelRatio?

number= 2

Caps drawing-buffer pixels per CSS pixel unless size is set.

VideoOptionsvideo · 1 field
mirrored?

boolean= false

Flips every frame left to right, as a mirror shows a front camera.

owns

the canvas's configuration and a resize observer; a hosted scene also owns the surface ·its root and the device unless it was given them ·the scene's stage and what its `attach` returned

allocates

each frame

A view of the canvas's texture, which is new every frame; under paced, a promise of the frame's work; for video, an imported texture per read of a frame.

lifecycle

destroy()[Symbol.dispose]

platform

webonly

Raise the phone pixel-ratio cap to align drawing-buffer checks with the screen-pixel grid at 3×. The magnified view spans six CSS pixels; the full-canvas counts show 2.25 times as many pixels as at 2×.

A page lays out a canvas in CSS pixels, while its drawing buffer measures canvas.width by canvas.height device pixels. Layout, screen and zoom changes can make those sizes disagree, blurring or stretching the picture. A surface configures the canvas for your root and keeps the drawing buffer matched to its layout.

import * as
import web
web
from '@typegpu/kit/web';
const
const canvas: HTMLCanvasElement
canvas
=
var document: Document

window.document returns a reference to the document contained in the window.

MDN Reference

document
.
ParentNode.querySelector<"canvas">(selectors: "canvas"): HTMLCanvasElement | null (+4 overloads)

Returns the first element that is a descendant of node that matches selectors.

MDN Reference

querySelector
('canvas')!;
const
const surface: web.Surface
surface
=
import web
web
.
function surface(root: TgpuRoot, canvas: HTMLCanvasElement, options?: web.SurfaceOptions): web.Surface

Configures a canvas for the root. Matches its drawing buffer to the layout size or the fixed options.size. Throws if the fixed size is invalid.

@param ― canvas the canvas it configures and sizes

@returns ― a surface you draw into and destroy

surface
(
const root: TgpuRoot
root
,
const canvas: HTMLCanvasElement
canvas
);
const
const loop: frames.Frames
loop
=
import frames
frames
.
function create(root: TgpuRoot, record: Recorder): frames.Frames

Records a frame into one encoder and submits once, with a clock. Borrows the root. The synchronous recorder borrows each frame's encoder.

@submits ― always once a frame after your recorder returns, and again at each frame.submit()

@param ― record your frame, recorded into each frame's encoder

@returns ― a loop you run with each frame's time and destroy

create
(
const root: TgpuRoot
root
, (
frame: frames.Frame
frame
) => {
const
const pass: TgpuRenderPass
pass
=
frame: frames.Frame
frame
.
Frame.encoder: TgpuCommandEncoder

Records commands for submission after the recorder returns. submit() replaces this encoder. Read it at each call instead of keeping it, and let the frame submit it.

encoder
.
TgpuCommandEncoder.beginRenderPass(descriptor: TgpuRenderPassDescriptor): TgpuRenderPass

Begins recording a render pass. Attachment views accept TypeGPU textures, texture views and canvas contexts, next to raw

GPUTextureView

s.

beginRenderPass
({
TgpuRenderPassDescriptor.colorAttachments?: ColorAttachment | readonly (ColorAttachment | null)[] | undefined
colorAttachments
: {
ColorAttachment.view: GPUTextureView | (ColorTextureConstraint & RenderFlag) | TgpuTextureView<WgslTexture<WgslTextureProps>> | TgpuTextureRenderView | GPUCanvasContext

A

GPUTextureView

describing the texture subresource that will be output to for this color attachment.

view
:
const surface: web.Surface
surface
.
Surface.view(frame: frames.Frame): GPUTextureView

Returns a view of the current canvas texture, created once per texture. Applies pending size changes first, which a pass using context would skip.

view
(
frame: frames.Frame
frame
) } });
// draw
const pass: TgpuRenderPass
pass
.
TgpuRenderPass.end(): void

Completes the recording of this render pass

end
();
});
import frames
frames
.
function animate(loop: Pick<frames.Frames, "run"> & Partial<Pick<frames.Frames, "seconds">>, options?: frames.AnimateOptions): frames.Animation
export animate

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
(
const loop: frames.Frames
loop
); // runs the loop at every animation frame
  • The drawing buffer uses the layout size multiplied by the screen’s pixel ratio, capped at maxPixelRatio, 2 by default. On a 3× phone, this shades 44% of the full-resolution pixels and uses less than half the memory for full-size targets.
  • pixelRatio is the capped number of drawing-buffer pixels per CSS pixel or point. Multiply point sizes by it for dots, hairlines and margins. A separately read screen ratio can exceed the cap.
  • maxPixelRatio caps the ratio but never raises it above the screen’s ratio. To render more pixels, draw into a target sized with targets.scaled(surface, 2) and filter it down. To use the same size on every screen, fix the size.
  • The size is clamped to the device’s maxTextureDimension2D.
  • For fewer pixels, or a resolution that changes while the game runs, render at a resolution below the surface.
flow-field65 536 particles, each steered by the Perlin noise under it.
This canvas hosts the flow field scene. The loupe shows drawing-buffer pixels one for one with screen pixels, up to a pixel ratio of 2. Point size is measured in drawing-buffer pixels.

A benchmark, GPU capture or offline render needs a fixed pixel size, such as 2560 × 1440. Pass size in pixels to set the drawing buffer before surface returns. Layout and pixel ratio changes leave it unchanged. The canvas’s CSS size still scales the image on screen. This benchmark steps frames by hand:

import * as
import web
web
from '@typegpu/kit/web';
const
const surface: web.Surface
surface
=
import web
web
.
function surface(root: TgpuRoot, canvas: HTMLCanvasElement, options?: web.SurfaceOptions): web.Surface

Configures a canvas for the root. Matches its drawing buffer to the layout size or the fixed options.size. Throws if the fixed size is invalid.

@param ― canvas the canvas it configures and sizes

@returns ― a surface you draw into and destroy

surface
(
const root: TgpuRoot
root
,
const canvas: HTMLCanvasElement
canvas
, {
SurfaceOptions.size?: web.FixedSize | undefined

Fixes the drawing-buffer size before surface returns. The canvas CSS size still scales it on screen. Default the layout size multiplied by the capped pixel ratio, following layout changes.

size
: {
FixedSize.width: number
width
: 2560,
FixedSize.height: number
height
: 1440 } });
const
const scene: {
frame(frame: Frame): void;
}
scene
=
function createScene(root: TgpuRoot, surface: Surface): {
frame(frame: Frame): void;
}
createScene
(
const root: TgpuRoot
root
,
const surface: web.Surface
surface
);
const
const loop: frames.Frames
loop
=
import frames
frames
.
function create(root: TgpuRoot, record: Recorder): frames.Frames

Records a frame into one encoder and submits once, with a clock. Borrows the root. The synchronous recorder borrows each frame's encoder.

@submits ― always once a frame after your recorder returns, and again at each frame.submit()

@param ― record your frame, recorded into each frame's encoder

@returns ― a loop you run with each frame's time and destroy

create
(
const root: TgpuRoot
root
, (
frame: frames.Frame
frame
) =>
const scene: {
frame(frame: Frame): void;
}
scene
.
function frame(frame: Frame): void
frame
(
frame: frames.Frame
frame
));
for (let
let i: number
i
= 0;
let i: number
i
< 600;
let i: number
i
++)
const loop: frames.Frames
loop
.
Frames.run(seconds: number): boolean

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.

run
(
let i: number
i
/ 60);
await
const root: TgpuRoot
root
.
TgpuRoot.device: GPUDevice

The GPU device associated with this root.

device
.
GPUDevice.queue: GPUQueue

The queue read-only property of the GPUDevice interface returns the primary GPUQueue for the device.

MDN Reference

The primary

GPUQueue

for this device.

queue
.
GPUQueue.onSubmittedWorkDone(): Promise<undefined> (+1 overload)

Returns a Promise that resolves once this queue finishes processing all the work submitted up to this moment. Resolution of this Promise implies the completion of

GPUBuffer#mapAsync

calls made prior to that call, on

GPUBuffer

s last used exclusively on that queue.

onSubmittedWorkDone
();
  • pixelRatio is the size’s pixelRatio, 1 unless you pass it, so what is measured in points comes out the same on every screen. maxPixelRatio doesn’t apply.
  • setSize(size) fixes a new size from the next view(), for a run at several resolutions; setSize(undefined) follows the layout again. web.host takes size too, and its page reaches setSize through send(({ surface }) => …).
  • A size that isn’t whole pixels from 1 to maxTextureDimension2D throws.
  • On React Native, native.surface and scenes.host take size too. resize then changes nothing until setSize(undefined), and setSize applies at once.
  • On the web, a ResizeObserver watches the canvas’s device-pixel box, the screen pixels it covers, so the drawing buffer matches the screen pixel for pixel. Without that box, the size is the CSS size times the ratio, rounded.
  • width and height report a size change immediately. The next view() applies it to the canvas in the frame where render targets rebuild through fit(). Deferring the canvas resize avoids clearing a frame already drawn.

view(frame) applies any pending size change and returns a view cached for the current canvas texture. Using surface.context directly as an attachment doesn’t apply pending sizes.

Use the Surface type from @typegpu/kit for code shared by both platforms and its tests. Pass the frame to view so React Native can present after submit.

import type {
(alias) interface Frame
import Frame

Represents one frame being recorded. Each run creates a separate object, so modules can compare frames by identity.

Frame
,
(alias) interface Surface
import Surface

Provides a destination for frames, such as a canvas from web.surface or native.surface, or a texture from offscreenSurface in a test. Pass the frame to view for code that works with every destination.

Surface
} from '@typegpu/kit';
export function
function createScene(root: TgpuRoot, surface: Surface): {
frame(frame: Frame): void;
}
createScene
(
root: TgpuRoot
root
:
(alias) interface TgpuRoot
import TgpuRoot
TgpuRoot
,
surface: Surface
surface
:
(alias) interface Surface
import Surface

Provides a destination for frames, such as a canvas from web.surface or native.surface, or a texture from offscreenSurface in a test. Pass the frame to view for code that works with every destination.

Surface
) {
return {
function frame(frame: Frame): void
frame
(
frame: Frame
frame
:
(alias) interface Frame
import Frame

Represents one frame being recorded. Each run creates a separate object, so modules can compare frames by identity.

Frame
) {
const
const pass: TgpuRenderPass
pass
=
frame: Frame
frame
.
Frame.encoder: TgpuCommandEncoder

Records commands for submission after the recorder returns. submit() replaces this encoder. Read it at each call instead of keeping it, and let the frame submit it.

encoder
.
TgpuCommandEncoder.beginRenderPass(descriptor: TgpuRenderPassDescriptor): TgpuRenderPass

Begins recording a render pass. Attachment views accept TypeGPU textures, texture views and canvas contexts, next to raw

GPUTextureView

s.

beginRenderPass
({
TgpuRenderPassDescriptor.colorAttachments?: ColorAttachment | readonly (ColorAttachment | null)[] | undefined
colorAttachments
: {
ColorAttachment.view: GPUTextureView | (ColorTextureConstraint & RenderFlag) | TgpuTextureView<WgslTexture<WgslTextureProps>> | TgpuTextureRenderView | GPUCanvasContext

A

GPUTextureView

describing the texture subresource that will be output to for this color attachment.

view
:
surface: Surface
surface
.
Surface.view(frame: Frame): GPUTextureView

Returns a view of the current texture, created once per texture. React Native requires the frame to present after submission.

view
(
frame: Frame
frame
) },
});
// draw
const pass: TgpuRenderPass
pass
.
TgpuRenderPass.end(): void

Completes the recording of this render pass

end
();
},
};
}

With --disable-gpu-vsync --disable-frame-rate-limit, Chrome can schedule frames faster than the GPU finishes them. Use web.paced(queue) as schedule to bound queued work. It waits until at most inFlight frames remain, 2 by default, before scheduling another frame. Normal vsync needs no extra pacing.

import frames
frames
.
function animate(loop: Pick<frames.Frames, "run"> & Partial<Pick<frames.Frames, "seconds">>, options?: frames.AnimateOptions): frames.Animation
export animate

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
(
const loop: frames.Frames
loop
, {
AnimateOptions.schedule?: frames.Schedule | undefined

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.

schedule
:
import web
web
.
function paced(queue: Pick<GPUQueue, "onSubmittedWorkDone">, options?: web.PacedOptions): frames.Schedule

Supplies animation frames that wait while inFlight frames remain unfinished on the GPU. Use as animate's schedule when frame callbacks can outpace GPU work. Throws unless inFlight is an integer above 0.

@param ― queue the device's queue, root.device.queue

@returns ― a schedule to give animate

paced
(
const root: TgpuRoot
root
.
TgpuRoot.device: GPUDevice

The GPU device associated with this root.

device
.
GPUDevice.queue: GPUQueue

The queue read-only property of the GPUDevice interface returns the primary GPUQueue for the device.

MDN Reference

The primary

GPUQueue

for this device.

queue
) });

web.pointer(element, options) follows the mouse, touch or pen on an element in the coordinates a camera’s rayAt takes:

import * as
import web
web
from '@typegpu/kit/web';
const
const ray: {
origin: number[];
direction: number[];
}
ray
= {
origin: number[]
origin
: [0, 0, 0],
direction: number[]
direction
: [0, 0, 0] };
const
const ground: number[]
ground
= [0, 0, 0];
const
const mouse: web.Pointer
mouse
=
import web
web
.
function pointer(element: HTMLElement, options?: web.PointerOptions): web.Pointer

Follows the mouse, touch or pen on an element in the coordinates rayAt takes, 0 to 1 across the element with +y down. Read the position each frame or take each move, press, release and zoom. A button pressed on the element keeps the pointer until it comes up, so a drag continues outside and its positions run past 0 and 1.

@param ― element what the pointer moves over, usually the canvas

@returns ― the pointer, which you destroy to remove the listeners

pointer
(
const canvas: HTMLCanvasElement
canvas
, {
PointerOptions.down?: web.PointerHandler | undefined

Runs as a button goes down on the element. Read the button from event.button. Default none.

down
(
x: number
x
,
y: number
y
,
event: PointerEvent
event
) {
if (
event: PointerEvent
event
.
MouseEvent.button: number

The MouseEvent.button read-only property indicates which button was pressed or released on the mouse to trigger the event.

MDN Reference

button
!== 0) return;
const view: cameras.OrbitCamera
view
.
CpuView.rayAt(x: number, y: number, out: {
readonly origin: cameras.Out3;
readonly direction: cameras.Out3;
}): void

Writes the ray through a point of the surface into out, as ray does. x and y range from 0 to 1 across the surface with +y down, as pointer handlers receive them.

rayAt
(
x: number
x
,
y: number
y
,
const ray: {
origin: number[];
direction: number[];
}
ray
);
if (
import cameras
cameras
.
function hitHeight(ray: Ray, height: number | ((x: number, z: number) => number), out?: cameras.Out3, options?: cameras.HitOptions): number | undefined

Finds where a ray, such as one from view.rayAt, meets the ground at height, such as the ground under a pointer. A height function, such as a terrain's, is searched in growing steps up to reach, then refined by halving the last step. Writes the point into out when given. Allocates nothing.

@param ― ray an origin and a unit direction

@param ― height the y of a level plane, or of the ground at each x and z

@param ― out where the point goes

@returns ― the distance along the ray, or undefined for a ray that misses

hitHeight
(
const ray: {
origin: number[];
direction: number[];
}
ray
, 0,
const ground: number[]
ground
) !==
var undefined
undefined
)
function select(point: number[]): void
select
(
const ground: number[]
ground
);
},
});
// Each frame, the ground under the pointer
if (
const mouse: web.Pointer
mouse
.
Pointer.over: boolean

Tells whether the pointer is over the element or holds a button down that began on it.

over
) {
const view: cameras.OrbitCamera
view
.
CpuView.rayAt(x: number, y: number, out: {
readonly origin: cameras.Out3;
readonly direction: cameras.Out3;
}): void

Writes the ray through a point of the surface into out, as ray does. x and y range from 0 to 1 across the surface with +y down, as pointer handlers receive them.

rayAt
(
const mouse: web.Pointer
mouse
.
Pointer.x: number

Holds the last position from 0 to 1 across the element, +y down. Starts at 0.5.

x
,
const mouse: web.Pointer
mouse
.
Pointer.y: number

Holds the last position from 0 to 1 down the element, as x does.

y
,
const ray: {
origin: number[];
direction: number[];
}
ray
);
import cameras
cameras
.
function hitHeight(ray: Ray, height: number | ((x: number, z: number) => number), out?: cameras.Out3, options?: cameras.HitOptions): number | undefined

Finds where a ray, such as one from view.rayAt, meets the ground at height, such as the ground under a pointer. A height function, such as a terrain's, is searched in growing steps up to reach, then refined by halving the last step. Writes the point into out when given. Allocates nothing.

@param ― ray an origin and a unit direction

@param ― height the y of a level plane, or of the ground at each x and z

@param ― out where the point goes

@returns ― the distance along the ray, or undefined for a ray that misses

hitHeight
(
const ray: {
origin: number[];
direction: number[];
}
ray
, 0,
const ground: number[]
ground
);
}
  • x and y run from 0 to 1 across the element with +y down, as rayAt takes them and projectAt writes them. Read them each frame, or take them in move, down and up.
  • zoom(by) runs as the wheel turns, with edeltaY·0.001, and as two fingers pinch, with the ratio of their last spread to the new one. Multiply a distance by by, which is above 1 farther.
  • A press keeps the pointer until its button comes up, so a drag continues outside the element, with positions past 0 and 1. up also runs when the browser cancels a pointer.
  • over is true while the pointer is over the element or holds a button pressed on it. A finger is over only while it touches. buttons holds the buttons down, as PointerEvent.buttons does.
  • It sets the element’s touch-action to none, or pan-y with page: true so touch drags scroll the page and only the wheel with Ctrl or ⌘ held zooms. menu: false keeps the context menu shut for drags with the secondary button.
  • destroy(), or [Symbol.dispose], removes the listeners and restores touch-action. Create it in attach on a hosted page to dispose it with the scene.
  • On React Native, divide a gesture’s position by the canvas’s layout size for the same coordinates.

kit.native

platform

Provides a react-native-webgpu canvas surface that pauses in the background, and a check that two threads can share a device.

native.surface(root, canvas, { pixelRatio, … }) → Surface

native.checkDevice(device)

rootborrows
the root
canvastakes
the CanvasRef from a <Canvas ref>; its context's canvas holds the size
pixelRatiotakes
PixelRatio.get()
Surfacereturns
a surface you resize, pause, resume and destroy
devicetakes
the device to check, such as a <GPUDeviceProvider>'s
types
FixedSizeCanvasSurface
SurfaceOptionssurface · 8 fields
pixelRatio

number

Sets the display pixel ratio from PixelRatio.get().

maxPixelRatio?

number= 2

Caps drawing-buffer pixels per point unless size is set.

size?

FixedSize= the layout size in points multiplied by the capped pixel ratio

Fixes the drawing-buffer size.

format?

GPUTextureFormat= navigator.gpu.getPreferredCanvasFormat()

Sets the canvas texture format.

alphaMode?

GPUCanvasAlphaMode= opaque

Controls canvas transparency.

usage?

GPUTextureUsageFlags= GPUTextureUsage.RENDER_ATTACHMENT

Sets the canvas texture usage flags.

colorSpace?

PredefinedColorSpace= 'srgb'

Sets the colour space of canvas values.

toneMapping?

GPUCanvasToneMapping= { mode: 'standard' }

Controls the range of canvas values for display.

owns

the canvas's configuration

allocates

each frame

A view of the canvas's texture, which is new every frame.

lifecycle

destroy()[Symbol.dispose]

requires
  • implicit-device-synchronizationrequired

    React-native-webgpu requests it for every device; checkDevice warns without it. Devices used from two threads require it to avoid crashes.

platform

nativeonly

@typegpu/kit/native has no React Native imports. Pass it the canvas, pixel ratio and layout.

  • native.surface takes the CanvasRef that <Canvas ref> holds, the web’s canvas options and the pixelRatio. The drawing buffer’s size is on its context’s canvas.
  • resize(width, height) takes the layout in points, as onLayout reports it.
  • view(frame) schedules context.present() after submit. React Native requires an explicit present. Dawn rejects presenting without first acquiring the canvas texture.
  • A backgrounded app must not submit GPU work. pause() sets paused, which frames.animate checks before every frame, and view() throws while paused, so a loop of your own fails at once rather than submitting in the background.
  • resume() reconfigures the context with every canvas option. After a long background pause, the first getCurrentTexture can block the JS thread.
  • device.lost and uncapturederror reach only the main JS runtime.
  • tgpu.init({ device: { optionalFeatures } }) keeps each feature the adapter has; device.features says which it got.

destroy(), or using, unconfigures the context and stops the web surface’s observer. view() then throws The surface was destroyed.

  • Above the cap, the device-pixel size is scaled by maxPixelRatio / ratio, so the drawing buffer matches the screen pixel for pixel only at or below the cap.
  • On React Native, a frame that takes the view and is never submitted drops its present; the next frame that takes the view presents.
  • The web surface’s view() doesn’t throw while paused, since a submit from a paused page does no harm.
  • implicit-device-synchronization costs one uncontended mutex per API call.