Skip to content

@typegpu/kit, @typegpu/kit/baketext · font · typeset · bake

Text

Labels and HUD text, baked to MSDF at build time and drawn as quads with no WebAssembly on the device.

kit.text

module

Creates labels drawn from a baked MSDF font atlas.

text.create(root, font, { capacity, length, place?, camera?, … }) → TextSet

rootborrows
the root
fontneeds
the font it draws with, which it borrows
place?takes
a 'use gpu' function; it and shade carry the fills of what they read beside the camera, such as tgpu.fn(lifted).pipe(scene.inject())
camera?needs
cameras
TextSetreturns
a set of labels you destroy before its font
GPU functions
facingflatscreenpinned
schemas
LabelQuadFragmentPlacedSeenCorner
types
PlaceTextSet
Optionscreate · 7 fields
capacity

number

Sets the number of labels to an integer of at least 1.

length

number

Limits glyphs drawn per label to an integer of at least 1.

place?

Place= facing

Places glyph corners with facing, flat, pinned, screen or a custom function.

camera?

CameraBase= none, which requires camera-dependent text to use a description that injects a camera

Supplies the camera for facing, flat and pinned, unless the pass description injects its own.

shade?

(fragment: Fragment) => d.v4f= the label colour over the glyph with one pixel of antialiasing at its edge

Returns premultiplied colour for a fragment.

blend?

'over' | 'add' | 'translucent' | GPUBlendState= over

Selects over for text that covers the background, add for glowing text, translucent for the passes' translucency targets, or a custom blend state.

label?

string= text

Sets the drawing pipeline label.

records
  • drawing.drawrender

drawing, added to passes, after the passes that move the labels.

owns

2 buffers ·a sampler

The quads and the labels.

lifecycle

destroy()[Symbol.dispose]

injects
  • per-instance

    move, setLabel, setQuad, quadAt and erase, for kernels piped with it.

binds
  • 3 storage · 1 uniform

    The drawing, the quads, the labels and the font's glyphs, and passes.pixels with text.screen or text.pinned.

  • 1 storage

    move or setLabel, the labels.

  • 1 storage

    setQuad, quadAt or erase, the quads.

needs
  • cameras

    As options.camera; fills a hook in its pipelines.

  • font

    As font.

reach
6.4 px
Type a label and select distance or round to see bands every three screen pixels. Distances stop at reach, which grows with size; wider outlines or glows fill the glyph quad.

HTML labels over a canvas cannot move from kernels, hide behind the scene or run on React Native. GPU text needs glyph layout and an atlas. @pmndrs/glyph shapes strings at runtime with 1.4 MB of WebAssembly, which Hermes cannot run.

Bake fonts with Glyph at build time. It produces a multi-channel signed distance field, or MSDF, atlas and JSON with advances and kerning. On the device, JavaScript lays out text and the GPU draws one instanced quad per glyph.

import {
import font
font
,
import text
text
} from '@typegpu/kit';
import
const interData: string
interData
from '@typegpu/kit/fonts/inter.json?url';
import
const interAtlas: string
interAtlas
from '@typegpu/kit/fonts/inter.rgba?url';
// Vite serves the shipped font as two files
const [
const data: any
data
,
const atlas: ArrayBuffer
atlas
] = await
var Promise: PromiseConstructor

Represents the completion of an asynchronous operation

Promise
.
PromiseConstructor.all<[Promise<any>, Promise<ArrayBuffer>]>(values: [Promise<any>, Promise<ArrayBuffer>]): Promise<[any, ArrayBuffer]> (+1 overload)

Creates a Promise that is resolved with an array of results when all of the provided Promises resolve, or rejected when any Promise is rejected.

@param ― values An array of Promises.

@returns ― A new Promise.

all
([
function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>
fetch
(
const interData: string
interData
).
Promise<Response>.then<any, never>(onfulfilled?: ((value: Response) => any) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<any>

Attaches callbacks for the resolution and/or rejection of the Promise.

@param ― onfulfilled The callback to execute when the Promise is resolved.

@param ― onrejected The callback to execute when the Promise is rejected.

@returns ― A Promise for the completion of which ever callback is executed.

then
((
response: Response
response
) =>
response: Response
response
.
Body.json(): Promise<any>
json
()),
function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>
fetch
(
const interAtlas: string
interAtlas
).
Promise<Response>.then<ArrayBuffer, never>(onfulfilled?: ((value: Response) => ArrayBuffer | PromiseLike<ArrayBuffer>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<ArrayBuffer>

Attaches callbacks for the resolution and/or rejection of the Promise.

@param ― onfulfilled The callback to execute when the Promise is resolved.

@param ― onrejected The callback to execute when the Promise is rejected.

@returns ― A Promise for the completion of which ever callback is executed.

then
((
response: Response
response
) =>
response: Response
response
.
Body.arrayBuffer(): Promise<ArrayBuffer>
arrayBuffer
()),
]);
const
const sans: font.Font
sans
=
import font
font
.
function create(root: TgpuRoot, baked: font.Baked): font.Font

Uploads a baked font atlas and glyph quads for text sets. The font also provides advances and kerning for CPU layout. Throws if the bake version is not 1 or the atlas byte count does not match its dimensions.

@owns ― a texture, a buffer: the atlas, and each glyph's quad

@param ― baked the bake's JSON and the atlas's bytes, as read returns them

@returns ― a font you destroy after the text sets that draw with it

create
(
const root: TgpuRoot
root
,
import font
font
.
function read(json: unknown, atlas: ArrayBufferLike | ArrayBufferView): font.Baked

Validates JSON from typegpu-kit-bake-font and pairs it with atlas bytes. Throws with the field name for invalid data or versions, or if the atlas size does not match.

@param ― json the JSON, as text or parsed

@param ― atlas the atlas's bytes, 4 a texel

@returns ― the bake, as create takes it; it throws, naming the field, on JSON of another shape or version, and on an atlas of another size

read
(
const data: any
data
,
const atlas: ArrayBuffer
atlas
));
const
const tags: text.TextSet
tags
=
import text
text
.
function create(root: TgpuRoot, font: font.Font, options: text.Options): text.TextSet

Creates labels drawn from a baked MSDF font atlas. The CPU lays out each string, and instanced glyph quads place it in world or screen space. Throws unless capacity and length are integers of at least 1.

@injects ― per-instance move, setLabel, setQuad, quadAt and erase, for kernels piped with it

@owns ― 2 buffers, a sampler: the quads and the labels

@binds ― 3 storage, 1 uniform: the drawing, the quads, the labels and the font's glyphs, and passes.pixels with text.screen or text.pinned

@binds ― 1 storage: move or setLabel, the labels

@binds ― 1 storage: setQuad, quadAt or erase, the quads

@records ― drawing, added to passes, after the passes that move the labels.

@param ― font the font it draws with, which it borrows

@param ― options.place a 'use gpu' function; it and shade carry the fills of what they read beside the camera, such as tgpu.fn(lifted).pipe(scene.inject())

@returns ― a set of labels you destroy before its font

create
(
const root: TgpuRoot
root
,
const sans: font.Font
sans
, {
Options.camera?: cameras.CameraBase | undefined

Supplies the camera for facing, flat and pinned, unless the pass description injects its own. Screen text needs no camera. Default none, which requires camera-dependent text to use a description that injects a camera.

camera
:
const view: cameras.OrbitCamera
view
,
Options.capacity: number

Sets the number of labels to an integer of at least 1.

capacity
: 16,
Options.length: number

Limits glyphs drawn per label to an integer of at least 1. Spaces use no glyph slot; glyphs beyond the limit are omitted.

length
: 24,
Options.place?: text.Place | undefined

Places glyph corners with facing, flat, pinned, screen or a custom function. Default facing.

place
:
import text
text
.
const pinned: (position: v3f, offset: v2f) => v4f

Places text at a world point with constant screen size. em uses points.

pinned
,
});
const
const canvas: passes.Passes<{
readonly color: typeof passes.color;
}>
canvas
=
import passes
passes
.
function create(root: TgpuRoot, screen: passes.Screen, options?: passes.Options): passes.Passes<{
readonly color: typeof passes.color;
}> (+1 overload)

Describes the targets and outputs for scene passes. With a surface such as the canvas, writes only each renderer's colour. With a target set, writes colour into the target marked color, depth into the target marked depth, and other outputs into their named targets. Multisampled targets resolve according to resolve. Targets with layers draw one pass per layer, and every target the description draws into needs the same layers. add builds a renderer's drawing. begin starts a pass. pipe supplies fills such as a camera.

@param ― screen the surface, or the targets, which give the formats; each one the description names is drawn into

@param ― writes what fills each target, by its key in screen

@returns ― a description you destroy, which holds the pipelines it builds

create
(
const root: TgpuRoot
root
,
const surface: web.Surface
surface
).
Passes<{ readonly color: typeof color; }>.pipe(fill: Inject): passes.Passes<{
readonly color: typeof passes.color;
}>

Adds fill after existing fills and returns a description of the same passes. Piped fills take precedence over a drawing's own. For example, passes.create(…).pipe(view.inject()) uses the camera from view.

pipe
(
const view: cameras.OrbitCamera
view
.
CameraBase.inject(): Inject

Fills cameras.current for the pipelines created from the configuration it is piped into.

inject
());
const
const drawn: passes.Drawn
drawn
=
const canvas: passes.Passes<{
readonly color: typeof passes.color;
}>
canvas
.
Passes<{ readonly color: typeof color; }>.add<WgslStruct<{
position: Vec4f;
uv: Vec2f;
bounds: Vec4f;
color: Vec4f;
label: U32;
glyph: U32;
local: Vec2f;
}>, {
label: number;
color: v4f;
}>(this: passes.Passes<passes.Fed<{
label: number;
color: v4f;
}, {
position: v4f;
uv: v2f;
bounds: v4f;
color: v4f;
label: number;
glyph: number;
local: v2f;
}>>, drawing: passes.Drawing<WgslStruct<{
position: Vec4f;
uv: Vec2f;
bounds: Vec4f;
color: Vec4f;
label: U32;
glyph: U32;
local: Vec2f;
}>, {
label: number;
color: v4f;
}>): passes.Drawn (+1 overload)

Builds a pipeline for drawing that writes every described output and returns its draw methods. If the drawing cannot supply the fields an output needs, type checking fails with that output's name. Use without to leave it out or instead to replace it.

add
(
const tags: text.TextSet
tags
.
TextSet.drawing: passes.Drawing<WgslStruct<{
position: Vec4f;
uv: Vec2f;
bounds: Vec4f;
color: Vec4f;
label: U32;
glyph: U32;
local: Vec2f;
}>, {
label: number;
color: v4f;
}>

Draws all labels when added to a canvas or scene pass description. It tests depth without writing it.

drawing
);
const tags: text.TextSet
tags
.
TextSet.write(label: number, text: string, options: Options): Layout

Lays out and uploads only the specified label. Use write(label, '') to empty it. Returns the layout, valid until the next write. Throws for a label outside the set or an em value that is not greater than 0.

write
(0, 'Lantern', {
Options.em: number

Sets positive units per em. World text uses world units. Text placed with screen or pinned uses points, scaled by the text set pixelRatio.

em
: 18,
Options.baseline?: "bottom" | "top" | "cap" | "middle" | "alphabetic" | undefined

Sets the vertical anchor at the first line top, the top of its capitals, block middle, first baseline or last line bottom. Default middle.

baseline
: 'bottom' });
const tags: text.TextSet
tags
.
TextSet.labels: TgpuBuffer<WgslArray<WgslStruct<{
position: Vec3f;
color: Vec4f;
}>>> & StorageFlag

Holds label anchors and colours, initially at the origin and white. Use TypeGPU patch to upload selected fields, or move and setLabel to write from a kernel.

labels
.
TgpuBuffer<WgslArray<WgslStruct<{ position: Vec3f; color: Vec4f; }>>>.patch(data: Record<number, {
position?: InferInput<Vec3f> | undefined;
color?: InferInput<Vec4f> | undefined;
} | undefined> | InferInput<WgslStruct<{
position: Vec3f;
color: Vec4f;
}>>[] | undefined): void
patch
({ 0: {
position: [number, number, number]
position
: [2, 1.5, 0],
color: [number, number, number, number]
color
: [1, 0.8, 0.4, 1] } });
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
(
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 canvas: passes.Passes<{
readonly color: typeof passes.color;
}>
canvas
.
Passes<{ readonly color: typeof color; }>.record(frame: frames.Frame | TgpuCommandEncoder, drawn: passes.Drawn | readonly passes.Drawn[], options?: passes.BeginOptions<{
readonly color: typeof passes.color;
}> | undefined): void

Begins a pass, draws drawn or each drawing in a list in order, then ends the pass. Reuse the list to avoid allocating each frame. Call begin yourself when you need to choose drawings per frame. Records nothing while enabled is false.

record
(
frame: frames.Frame
frame
,
const drawn: passes.Drawn
drawn
);
}),
);
  • A set holds capacity labels with at most length glyphs each. write(label, text, options) lays out one label and uploads its glyphs. Spaces use no glyph slots. Glyphs beyond length are omitted.
  • labels stores anchors and linear colours, initially white at the origin. Move or fade labels with TypeGPU’s patch, which uploads only supplied fields. Kernels can use tags.move(label, position) and tags.setLabel(label, value). These updates do not repeat layout.
  • This label uses 18 points per em. Their pixel size is em times the pass’s pixels per point, the surface’s pixelRatio, giving a consistent size across screens. Labels centre on their anchor. Use baseline: 'bottom' to place their bottom at the anchor.
  • canvas.add(tags.drawing) builds the pipeline into a pass description. One draw renders capacity * length instances. Empty slots still run four vertices each.
  • Load font bytes in your code. See Loading a font for each platform.
  • The solar system example bends its labels around each planet with a custom place, and types them on glyph by glyph.

A kernel created from root.pipe(tags.inject()) can write glyphs. quads stores length entries per label in label order. Each entry is a text.Quad:

  • tags.setQuad(label, k, quad) sets glyph k. Its pen and em use the units expected by place. glyph is the font index plus 1, or 0 to hide it. Read indices and advances from font.glyphOf and font.advance.
  • tags.erase(label) hides all of a label’s glyphs. Erase a label before a kernel writes fewer glyphs than write did.
  • tags.quadAt(label, k) reads glyph k. To show text a kernel picks, write each choice once into a set you never draw, and copy the chosen label’s quads with setQuad(0, k, bank.quadAt(choice, k)) from a kernel piped with both sets. The stampede names the horse under the pointer this way.

kit.font

module

Uploads a baked font atlas and glyph quads for text sets.

font.create(root, baked) → Font

font.metrics(data) → Metrics

font.read(json, atlas) → Baked

rootborrows
the root
bakedtakes
the bake's JSON and the atlas's bytes, as read returns them
Fontreturns
a font you destroy after the text sets that draw with it
datatakes
the bake's JSON, as read returns it
Metricsreturns
the font's metrics, which hold nothing on the GPU
jsontakes
the JSON, as text or parsed
atlastakes
the atlas's bytes, 4 a texel
Bakedreturns
the bake, as create takes it; it throws, naming the field, on JSON of another shape or version, and on an atlas of another size
schemas
Glyph
types
BakedFontDataGlyphDataMetricsFont
owns

a texture ·a buffer

The atlas, and each glyph's quad.

lifecycle

destroy()[Symbol.dispose]

The package includes baked Inter SemiBold in @typegpu/kit/fonts/inter.json and inter.rgba. Baking a font produces the same two files. Pass the JSON as text or parsed data, plus atlas bytes, to font.read(json, atlas). It returns a Baked with data, a FontData, and atlas for font.create. It throws for versions other than 1, missing or mistyped fields, or an atlas size other than 4 bytes per texel at the JSON dimensions. Field errors name the field.

Vite serves package files at their own URLs with ?url in development and builds, as in the opening example. Use types from vite/client. Fetch your own baked font from public/ by its path.

kit.bake

tool

This Node build-time tool bakes fonts for @typegpu/kit with @pmndrs/glyph.

bake({ input, … }) → Promise<Bake>

format(data)

Promise<Bake>returns
the JSON font.create reads, the atlas's RGBA bytes, and the characters the font lacks
datatakes
the font's metrics, glyphs and kerning: what bake returns as data
constants
ASCII
types
Bake
BakeOptionsbake · 5 fields
input

string

Selects a static TTF, OTF, TTC or OTC file.

characters?

string= none

Adds characters to printable ASCII, such as …°é.

emSize?

number= 40

Sets atlas texels per em.

pixelRange?

number= 8

Sets the signed-distance range of the atlas in texels.

fontFaceIndex?

number= 0

Selects a face within a font collection.

typegpu-kit-bake-font runs on Node and requires @pmndrs/glyph 0.1.0 installed alongside it.

typegpu-kit-bake-font --input Inter-SemiBold.ttf --output public/fonts/inter --characters "…↓"
  • The command writes inter.json and inter.rgba. They include printable ASCII, the supplied --characters and a fallback glyph that advances without drawing.
  • It warns of characters given that the font has no glyph for.
  • Glyph bakes only static fonts. Pin variable-font axes first, for example with fonttools varLib.instancer Font.ttf wght=600.
  • The shipped Inter is baked with … at the defaults below, under the SIL Open Font License.
Flag bake Default
--characters characters Additional characters beyond printable ASCII. Only Latin text is supported because runtime layout does not shape glyphs.
--em-size emSize 40 Atlas texels per em. Larger values keep corners sharper on large text and make a larger atlas.
--pixel-range pixelRange 8 Distance range in atlas texels. It limits how far outlines and glows can extend.
--font-face-index fontFaceIndex 0 The face of a collection.

In a build script, import bake(options) from @typegpu/kit/bake. Pass the font file as input with the options above. The result contains Baked for font.create and absent, the requested characters missing from the font. format(data) produces the command’s JSON format, with one line per glyph and kerning pair.

import {
function writeFile(file: PathLike | FileHandle, data: string | NodeJS.ArrayBufferView | Iterable<string | NodeJS.ArrayBufferView> | AsyncIterable<string | NodeJS.ArrayBufferView>, options?: (ObjectEncodingOptions & {
mode?: Mode | undefined;
flag?: OpenMode | undefined;
flush?: boolean | undefined;
} & EventEmitter<T extends EventMap<T> = DefaultEventMap>.Abortable) | BufferEncoding | null): Promise<void>

Asynchronously writes data to a file, replacing the file if it already exists. data can be a string, a buffer, an AsyncIterable, or an Iterable object.

The encoding option is ignored if data is a buffer.

If options is a string, then it specifies the encoding.

The mode option only affects the newly created file. See fs.open() for more details.

Any specified FileHandle has to support writing.

It is unsafe to use fsPromises.writeFile() multiple times on the same file without waiting for the promise to be settled.

Similarly to fsPromises.readFile - fsPromises.writeFile is a convenience method that performs multiple write calls internally to write the buffer passed to it. For performance sensitive code consider using fs.createWriteStream() or filehandle.createWriteStream().

It is possible to use an AbortSignal to cancel an fsPromises.writeFile(). Cancelation is "best effort", and some amount of data is likely still to be written.

import { writeFile } from 'node:fs/promises';
import { Buffer } from 'node:buffer';
try {
const controller = new AbortController();
const { signal } = controller;
const data = new Uint8Array(Buffer.from('Hello Node.js'));
const promise = writeFile('message.txt', data, { signal });
// Abort the request before the promise settles.
controller.abort();
await promise;
} catch (err) {
// When a request is aborted - err is an AbortError
console.error(err);
}

Aborting an ongoing request does not abort individual operating system requests but rather the internal buffering fs.writeFile performs.

@since ― v10.0.0

@param ― file filename or FileHandle

@return ― Fulfills with undefined upon success.

writeFile
} from 'node:fs/promises';
import {
function bake(options: BakeOptions): Promise<Bake>

Bakes printable ASCII and characters from a font file into an atlas and metrics. Requires a static font and a single uncompressed RGBA8 atlas page. Unsupported font or atlas data throw.

@param ― options the font file, and what to bake beside ASCII

@returns ― the JSON font.create reads, the atlas's RGBA bytes, and the characters the font lacks

bake
,
function format(data: FontData): string

Formats baked font JSON with one line per glyph and kerning pair so diffs show individual changes.

@param ― data the font's metrics, glyphs and kerning: what bake returns as data

@returns ― the JSON text

format
} from '@typegpu/kit/bake';
const {
const data: FontData
data
,
const atlas: Uint8Array<ArrayBufferLike>

Holds RGBA texels in rows from top to bottom, with 4 bytes per texel. The median of the first three distance channels preserves corners. The fourth channel rounds corners.

atlas
,
const absent: string

Lists requested characters that the font lacks. They use the missing glyph.

absent
} = await
function bake(options: BakeOptions): Promise<Bake>

Bakes printable ASCII and characters from a font file into an atlas and metrics. Requires a static font and a single uncompressed RGBA8 atlas page. Unsupported font or atlas data throw.

@param ― options the font file, and what to bake beside ASCII

@returns ― the JSON font.create reads, the atlas's RGBA bytes, and the characters the font lacks

bake
({
BakeOptions.input: string

Selects a static TTF, OTF, TTC or OTC file. glyph cannot bake variable fonts.

input
: 'Inter-SemiBold.ttf',
BakeOptions.characters?: string | undefined

Adds characters to printable ASCII, such as …°é. Only Latin text is supported because the runtime performs no shaping. Default none.

characters
: '…' });
if (
const absent: string

Lists requested characters that the font lacks. They use the missing glyph.

absent
) throw new
var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error
(`Inter has no glyph for ${
const absent: string

Lists requested characters that the font lacks. They use the missing glyph.

absent
}.`);
await
function writeFile(file: PathLike | FileHandle, data: string | NodeJS.ArrayBufferView | Iterable<string | NodeJS.ArrayBufferView> | AsyncIterable<string | NodeJS.ArrayBufferView>, options?: (ObjectEncodingOptions & {
mode?: Mode | undefined;
flag?: OpenMode | undefined;
flush?: boolean | undefined;
} & EventEmitter<T extends EventMap<T> = DefaultEventMap>.Abortable) | BufferEncoding | null): Promise<void>

Asynchronously writes data to a file, replacing the file if it already exists. data can be a string, a buffer, an AsyncIterable, or an Iterable object.

The encoding option is ignored if data is a buffer.

If options is a string, then it specifies the encoding.

The mode option only affects the newly created file. See fs.open() for more details.

Any specified FileHandle has to support writing.

It is unsafe to use fsPromises.writeFile() multiple times on the same file without waiting for the promise to be settled.

Similarly to fsPromises.readFile - fsPromises.writeFile is a convenience method that performs multiple write calls internally to write the buffer passed to it. For performance sensitive code consider using fs.createWriteStream() or filehandle.createWriteStream().

It is possible to use an AbortSignal to cancel an fsPromises.writeFile(). Cancelation is "best effort", and some amount of data is likely still to be written.

import { writeFile } from 'node:fs/promises';
import { Buffer } from 'node:buffer';
try {
const controller = new AbortController();
const { signal } = controller;
const data = new Uint8Array(Buffer.from('Hello Node.js'));
const promise = writeFile('message.txt', data, { signal });
// Abort the request before the promise settles.
controller.abort();
await promise;
} catch (err) {
// When a request is aborted - err is an AbortError
console.error(err);
}

Aborting an ongoing request does not abort individual operating system requests but rather the internal buffering fs.writeFile performs.

@since ― v10.0.0

@param ― file filename or FileHandle

@return ― Fulfills with undefined upon success.

writeFile
('public/fonts/inter.json',
function format(data: FontData): string

Formats baked font JSON with one line per glyph and kerning pair so diffs show individual changes.

@param ― data the font's metrics, glyphs and kerning: what bake returns as data

@returns ― the JSON text

format
(
const data: FontData
data
));
await
function writeFile(file: PathLike | FileHandle, data: string | NodeJS.ArrayBufferView | Iterable<string | NodeJS.ArrayBufferView> | AsyncIterable<string | NodeJS.ArrayBufferView>, options?: (ObjectEncodingOptions & {
mode?: Mode | undefined;
flag?: OpenMode | undefined;
flush?: boolean | undefined;
} & EventEmitter<T extends EventMap<T> = DefaultEventMap>.Abortable) | BufferEncoding | null): Promise<void>

Asynchronously writes data to a file, replacing the file if it already exists. data can be a string, a buffer, an AsyncIterable, or an Iterable object.

The encoding option is ignored if data is a buffer.

If options is a string, then it specifies the encoding.

The mode option only affects the newly created file. See fs.open() for more details.

Any specified FileHandle has to support writing.

It is unsafe to use fsPromises.writeFile() multiple times on the same file without waiting for the promise to be settled.

Similarly to fsPromises.readFile - fsPromises.writeFile is a convenience method that performs multiple write calls internally to write the buffer passed to it. For performance sensitive code consider using fs.createWriteStream() or filehandle.createWriteStream().

It is possible to use an AbortSignal to cancel an fsPromises.writeFile(). Cancelation is "best effort", and some amount of data is likely still to be written.

import { writeFile } from 'node:fs/promises';
import { Buffer } from 'node:buffer';
try {
const controller = new AbortController();
const { signal } = controller;
const data = new Uint8Array(Buffer.from('Hello Node.js'));
const promise = writeFile('message.txt', data, { signal });
// Abort the request before the promise settles.
controller.abort();
await promise;
} catch (err) {
// When a request is aborted - err is an AbortError
console.error(err);
}

Aborting an ongoing request does not abort individual operating system requests but rather the internal buffering fs.writeFile performs.

@since ― v10.0.0

@param ― file filename or FileHandle

@return ― Fulfills with undefined upon success.

writeFile
('public/fonts/inter.rgba',
const atlas: Uint8Array<ArrayBufferLike>

Holds RGBA texels in rows from top to bottom, with 4 bytes per texel. The median of the first three distance channels preserves corners. The fourth channel rounds corners.

atlas
);

The atlas must fit on one page of Glyph’s packer. The bake reports an error if it does not.

kit.typeset

cpu

This module lays out strings on the CPU using baked font advances and kerning.

typeset.create(capacity) → Typesetter

kind
cpu
types
OptionsLayoutTypesetter

write lays text out with these options.

Option Default
em Units per em. Use world units for world text and points for screen or pinned text.
align 'center' Horizontal anchor position within each line, 'left', 'center' or 'right'.
baseline 'middle' Vertical anchor position within the block, 'top', 'cap' for the top of the first line’s capitals, 'middle', 'alphabetic' for the first baseline, or 'bottom'.
tracking 0 Space added after each character but the last of a line, in em.

\n, \r\n and \r start lines. Text does not wrap to a width.

write returns the label’s layout, with its width, height and lines, valid until the set’s next write. Size a panel behind a label from it.

Use the same layout without drawing to measure text before writing it. typeset.create(capacity).set(font, text, options) returns the same layout. Once its arrays fit the longest text, it allocates no more. typeset reads only advances and kerning. Pass font.metrics(data) without a root to measure text in Node, a worker or before tgpu.init.

const {
const data: font.FontData
data
} =
import font
font
.
function read(json: unknown, atlas: ArrayBufferLike | ArrayBufferView): font.Baked

Validates JSON from typegpu-kit-bake-font and pairs it with atlas bytes. Throws with the field name for invalid data or versions, or if the atlas size does not match.

@param ― json the JSON, as text or parsed

@param ― atlas the atlas's bytes, 4 a texel

@returns ― the bake, as create takes it; it throws, naming the field, on JSON of another shape or version, and on an atlas of another size

read
(
const json: string
json
,
const atlas: ArrayBuffer
atlas
);
const
const panel: typeset.Typesetter
panel
=
import typeset
typeset
.
function create(capacity: number): typeset.Typesetter

Creates a typesetter that places at most capacity glyphs and measures text of any length. Text sets use one for each label. Use it separately to measure text before drawing, such as when sizing a panel behind a label. Throws unless capacity is an integer of at least 1.

@param ― capacity how many glyphs it places: a text set lays each label out into one of its length

@returns ― a typesetter whose set lays a string out into its arrays, growing them only for longer text

create
(32).
Typesetter.set(font: font.Metrics, text: string, options: typeset.Options): typeset.Typesetter

Replaces the layout with text using a font or its metrics. Returns this layout. Throws unless em is greater than 0.

set
(
import font
font
.
function metrics(data: font.FontData): font.Metrics

Creates metrics for CPU text layout without uploading an atlas. Use them to measure text in Node, in a worker or before tgpu.init.

@param ― data the bake's JSON, as read returns it

@returns ― the font's metrics, which hold nothing on the GPU

metrics
(
const data: font.FontData
data
), 'Game over', {
Options.em: number

Sets positive units per em. World text uses world units. Text placed with screen or pinned uses points, scaled by the text set pixelRatio.

em
: 24 });
const [
const width: number
width
,
const height: number
height
] = [
const panel: typeset.Typesetter
panel
.
Layout.width: number

Gives the widest line in the units of em. The block height spans the first line top to the last line bottom.

width
,
const panel: typeset.Typesetter
panel
.
Layout.height: number
height
]; // in points, as em is

place converts a glyph corner to clip space using the label’s position and the corner offset. Choose one of four functions:

place position em is in
text.facing A point in the world; the text faces the camera. World units
text.flat A point in the world; the text lies on the ground, read along +x. World units
text.pinned A point in the world; the text stays as large wherever it is. Points
text.screen Pixels from the drawn region’s top-left in x and y, depth in z. Points
  • A point equals the passes.pixels’s pixelRatio pixels of the target the pass draws, giving consistent label sizes across screens and in low-resolution targets.
  • Screen positions use pixels. Multiply pointer-event offsets by the surface’s pixelRatio. Picker takes points and performs that multiplication itself.
  • facing, flat and pinned use the set’s camera or the camera injected by its pass description. screen needs no camera.

Write a custom place as a 'use gpu' function taking position, offset and a text.Placed. The latter contains label, the glyph index within the label as glyph, its pen and its em. To size in points, read the pixel dimensions and pixel ratio from passes.pixels.$. Both place and shade can read cameras.current.$. If a callback reads another module, fill that dependency, for example with place: tgpu.fn(wobble).pipe(noise.inject()).

To make a rising “+1” swell, read its scale from a buffer indexed by label. Multiply the offset to scale around the anchor:

const
const scales: TgpuReadonly<d.WgslArray<d.F32>>
scales
=
const root: TgpuRoot
root
.
TgpuRoot.createReadonly<d.WgslArray<d.F32>>(typeSchema: d.WgslArray<d.F32>, initial?: ((buffer: TgpuBuffer<NoInfer<d.WgslArray<d.F32>>>) => void) | d.InferInput<NoInfer<d.WgslArray<d.F32>>> | undefined): TgpuReadonly<d.WgslArray<d.F32>> (+1 overload)

Allocates memory on the GPU, allows passing data between host and shader. Read-only on the GPU, optimized for large data. For a general-purpose buffer, use

TgpuRoot.createBuffer

.

@param ― typeSchema The type of data that this buffer will hold.

@param ― initial Either initial value of the buffer, or an initializer to execute on the mapped buffer. (optional)

createReadonly
(
import d
d
.
arrayOf<d.F32>(elementType: d.F32, elementCount: number): d.WgslArray<d.F32> (+2 overloads)
export arrayOf

@location ― . Wrap align/size in a struct instead, e.g. d.arrayOf(d.struct({ value: d.align(16, d.u32) }), n).

arrayOf
(
import d
d
.
const f32: d.F32
export f32

A schema that represents a 32-bit float value. (equivalent to f32 in WGSL)

Can also be called to cast a value to an f32.

@example const value = f32(); // 0

@example const value = f32(1.23); // 1.23

@example const value = f32(true); // 1

f32
, 16),
var Array: ArrayConstructor
(arrayLength?: number) => any[] (+2 overloads)
Array
(16).
Array<any>.fill(value: any, start?: number, end?: number): any[]

Changes all array elements from start to end index to a static value and returns the modified array

@param ― value value to fill array section with

@param ― start index to start filling the array at. If start is negative, it is treated as length+start where length is the length of the array.

@param ― end index to stop filling the array at. If end is negative, it is treated as length+end.

fill
(1));
const
const swelling: (position: d.v3f, offset: d.v2f, glyph: text.Placed) => d.v4f
swelling
= (
position: d.v3f
position
:
import d
d
.
export v3f

Interface representing its WGSL vector type counterpart: vec3f or vec3. A vector with 3 elements of type f32

v3f
,
offset: d.v2f
offset
:
import d
d
.
export v2f

Interface representing its WGSL vector type counterpart: vec2f or vec2. A vector with 2 elements of type f32

v2f
,
glyph: {
label: number;
glyph: number;
pen: d.v2f;
em: number;
}
glyph
:
import text
text
.
type Placed = {
label: number;
glyph: number;
pen: d.v2f;
em: number;
}

This schema describes the glyph passed to place for each corner.

Placed
) => {
'use gpu';
return
import text
text
.
const pinned: (position: d.v3f, offset: d.v2f) => d.v4f

Places text at a world point with constant screen size. em uses points.

pinned
(
position: d.v3f
position
,
offset: d.v2f
offset
*
const scales: TgpuReadonly<d.WgslArray<d.F32>>
scales
.
TgpuReadonly<WgslArray<F32>>.$: number[]
$
[
glyph: {
label: number;
glyph: number;
pen: d.v2f;
em: number;
}
glyph
.
label: number
label
]!);
};
const
const pops: text.TextSet
pops
=
import text
text
.
function create(root: TgpuRoot, font: font.Font, options: text.Options): text.TextSet

Creates labels drawn from a baked MSDF font atlas. The CPU lays out each string, and instanced glyph quads place it in world or screen space. Throws unless capacity and length are integers of at least 1.

@injects ― per-instance move, setLabel, setQuad, quadAt and erase, for kernels piped with it

@owns ― 2 buffers, a sampler: the quads and the labels

@binds ― 3 storage, 1 uniform: the drawing, the quads, the labels and the font's glyphs, and passes.pixels with text.screen or text.pinned

@binds ― 1 storage: move or setLabel, the labels

@binds ― 1 storage: setQuad, quadAt or erase, the quads

@records ― drawing, added to passes, after the passes that move the labels.

@param ― font the font it draws with, which it borrows

@param ― options.place a 'use gpu' function; it and shade carry the fills of what they read beside the camera, such as tgpu.fn(lifted).pipe(scene.inject())

@returns ― a set of labels you destroy before its font

create
(
const root: TgpuRoot
root
,
const sans: font.Font
sans
, {
Options.camera?: cameras.CameraBase | undefined

Supplies the camera for facing, flat and pinned, unless the pass description injects its own. Screen text needs no camera. Default none, which requires camera-dependent text to use a description that injects a camera.

camera
:
const view: cameras.OrbitCamera
view
,
Options.capacity: number

Sets the number of labels to an integer of at least 1.

capacity
: 16,
Options.length: number

Limits glyphs drawn per label to an integer of at least 1. Spaces use no glyph slot; glyphs beyond the limit are omitted.

length
: 4,
Options.place?: text.Place | undefined

Places glyph corners with facing, flat, pinned, screen or a custom function. Default facing.

place
:
const swelling: (position: d.v3f, offset: d.v2f, glyph: text.Placed) => d.v4f
swelling
,
});
const pops: text.TextSet
pops
.
TextSet.write(label: number, text: string, options: Options): Layout

Lays out and uploads only the specified label. Use write(label, '') to empty it. Returns the layout, valid until the next write. Throws for a label outside the set or an em value that is not greater than 0.

write
(3, '+1', {
Options.em: number

Sets positive units per em. World text uses world units. Text placed with screen or pinned uses points, scaled by the text set pixelRatio.

em
: 14 });
const scales: TgpuReadonly<d.WgslArray<d.F32>>
scales
.
TgpuBufferBindingBase<WgslArray<F32>>.patch(data: number[] | Float32Array<ArrayBufferLike> | Record<number, number | undefined> | undefined): void
patch
({ 3: 1.6 });

A kernel that moves the “+1” can write its scale too, through scales.buffer.as('mutable').

shade(fragment) returns premultiplied colour from a text.Fragment:

  • color and label give the label colour and index. glyph is the glyph index within the label, starting at 0.
  • local is the pixel position relative to the anchor, in em with y pointing up.
  • distance is the distance to the sharp glyph edge in screen pixels, positive inside. round measures distance to a rounded edge from the atlas’s fourth channel. The figure shows both.
  • reach limits both distances to half the pixel range scaled to the drawn glyph. With the shipped bake, it is pixels per em divided by 10.

An outline two pixels wide, narrower where the atlas doesn’t reach that far:

const
const outlined: (fragment: text.Fragment) => d.v4f
outlined
= (
fragment: {
label: number;
glyph: number;
local: d.v2f;
color: d.v4f;
distance: number;
round: number;
reach: number;
}
fragment
:
import text
text
.
type Fragment = {
label: number;
glyph: number;
local: d.v2f;
color: d.v4f;
distance: number;
round: number;
reach: number;
}

This schema describes a glyph fragment passed to shade.

Fragment
) => {
'use gpu';
const
const width: number
width
=
import std
std
.
function min(fst: number, ...rest: number[]): number (+1 overload)
export min
min
(2,
fragment: {
label: number;
glyph: number;
local: d.v2f;
color: d.v4f;
distance: number;
round: number;
reach: number;
}
fragment
.
reach: number

Gives the maximum range of distance and round in screen pixels. It is half the font distanceRange in texels, scaled to the drawn size. Values stop at -reach outside the glyph, so an outline wider than reach fills the entire quad.

reach
- 0.5);
const
const fill: number
fill
=
import std
std
.
function saturate(value: number): number (+1 overload)
export saturate
saturate
(
fragment: {
label: number;
glyph: number;
local: d.v2f;
color: d.v4f;
distance: number;
round: number;
reach: number;
}
fragment
.
distance: number

Gives signed distance to the glyph edge in screen pixels, positive inside. It uses the median of the first three atlas channels.

distance
+ 0.5);
const
const outline: number
outline
=
import std
std
.
function saturate(value: number): number (+1 overload)
export saturate
saturate
(
fragment: {
label: number;
glyph: number;
local: d.v2f;
color: d.v4f;
distance: number;
round: number;
reach: number;
}
fragment
.
round: number

Gives signed distance from the fourth atlas channel in screen pixels. It rounds corners and extends up to reach, for outlines and glows.

round
+
const width: number
width
+ 0.5) *
fragment: {
label: number;
glyph: number;
local: d.v2f;
color: d.v4f;
distance: number;
round: number;
reach: number;
}
fragment
.
color: d.v4f

Holds the label colour and opacity.

color
.
v4f.w: number
w
;
return
import d
d
.
const vec4f: d.Vec4f
(v0: AnyNumericVec3Instance, w: number) => d.v4f (+9 overloads)
vec4f
(
fragment: {
label: number;
glyph: number;
local: d.v2f;
color: d.v4f;
distance: number;
round: number;
reach: number;
}
fragment
.
color: d.v4f

Holds the label colour and opacity.

color
.
xyz: d.v3f
xyz
*
const fill: number
fill
*
const outline: number
outline
,
const outline: number
outline
);
};
const
const score: text.TextSet
score
=
import text
text
.
function create(root: TgpuRoot, font: font.Font, options: text.Options): text.TextSet

Creates labels drawn from a baked MSDF font atlas. The CPU lays out each string, and instanced glyph quads place it in world or screen space. Throws unless capacity and length are integers of at least 1.

@injects ― per-instance move, setLabel, setQuad, quadAt and erase, for kernels piped with it

@owns ― 2 buffers, a sampler: the quads and the labels

@binds ― 3 storage, 1 uniform: the drawing, the quads, the labels and the font's glyphs, and passes.pixels with text.screen or text.pinned

@binds ― 1 storage: move or setLabel, the labels

@binds ― 1 storage: setQuad, quadAt or erase, the quads

@records ― drawing, added to passes, after the passes that move the labels.

@param ― font the font it draws with, which it borrows

@param ― options.place a 'use gpu' function; it and shade carry the fills of what they read beside the camera, such as tgpu.fn(lifted).pipe(scene.inject())

@returns ― a set of labels you destroy before its font

create
(
const root: TgpuRoot
root
,
const sans: font.Font
sans
, {
Options.capacity: number

Sets the number of labels to an integer of at least 1.

capacity
: 1,
Options.length: number

Limits glyphs drawn per label to an integer of at least 1. Spaces use no glyph slot; glyphs beyond the limit are omitted.

length
: 12,
Options.place?: text.Place | undefined

Places glyph corners with facing, flat, pinned, screen or a custom function. Default facing.

place
:
import text
text
.
const screen: (position: d.v3f, offset: d.v2f) => d.v4f

Places text on screen with position in pixels from the top-left of the region the pass draws. em uses points, the pass's passes.pixels pixels per point, to preserve label size across pixel densities.

screen
,
Options.shade?: ((fragment: text.Fragment) => d.v4f) | undefined

Returns premultiplied colour for a fragment. Default the label colour over the glyph with one pixel of antialiasing at its edge.

shade
:
const outlined: (fragment: text.Fragment) => d.v4f
outlined
,
});

A full two-pixel outline needs 25 pixels per em with the shipped bake, or 12.5 points at 2×. Outlines or glows wider than reach fill the glyph quad. For example, round + 2.5 produces boxes below 25 pixels per em. Bake with a wider --pixel-range for wide effects on small text.

Use glyph and local to animate labels without rewriting quads. This typewriter effect fades glyphs below a rising count and applies a vertical gradient from the baseline:

const
const typed: TgpuUniform<d.F32>
typed
=
const root: TgpuRoot
root
.
TgpuRoot.createUniform<d.F32>(typeSchema: d.F32, initial?: number | ((buffer: TgpuBuffer<NoInfer<d.F32>>) => void) | undefined): TgpuUniform<d.F32> (+1 overload)

Allocates memory on the GPU, allows passing data between host and shader. Read-only on the GPU, optimized for small data. For a general-purpose buffer, use

TgpuRoot.createBuffer

.

@param ― typeSchema The type of data that this buffer will hold.

@param ― initial Either initial value of the buffer, or an initializer to execute on the mapped buffer. (optional)

createUniform
(
import d
d
.
const f32: d.F32
export f32

A schema that represents a 32-bit float value. (equivalent to f32 in WGSL)

Can also be called to cast a value to an f32.

@example const value = f32(); // 0

@example const value = f32(1.23); // 1.23

@example const value = f32(true); // 1

f32
);
const
const TOP: d.v3f
TOP
=
import d
d
.
const vec3f: d.Vec3f
(x: number, y: number, z: number) => d.v3f (+5 overloads)
vec3f
(1, 0.85, 0.4);
const
const typewriter: (fragment: text.Fragment) => d.v4f
typewriter
= (
fragment: {
label: number;
glyph: number;
local: d.v2f;
color: d.v4f;
distance: number;
round: number;
reach: number;
}
fragment
:
import text
text
.
type Fragment = {
label: number;
glyph: number;
local: d.v2f;
color: d.v4f;
distance: number;
round: number;
reach: number;
}

This schema describes a glyph fragment passed to shade.

Fragment
) => {
'use gpu';
const
const shown: number
shown
=
import std
std
.
function saturate(value: number): number (+1 overload)
export saturate
saturate
(
const typed: TgpuUniform<d.F32>
typed
.
TgpuUniform<F32>.$: number
$
-
import d
d
.
function f32(v?: number | boolean): number
export f32

A schema that represents a 32-bit float value. (equivalent to f32 in WGSL)

Can also be called to cast a value to an f32.

@example const value = f32(); // 0

@example const value = f32(1.23); // 1.23

@example const value = f32(true); // 1

f32
(
fragment: {
label: number;
glyph: number;
local: d.v2f;
color: d.v4f;
distance: number;
round: number;
reach: number;
}
fragment
.
glyph: number

Gives the zero-based glyph index within the label. Spaces use no index.

glyph
));
const
const ink: d.v3f
ink
=
import std
std
.
mix<d.v3f>(e1: d.v3f, e2: d.v3f, e3: number): d.v3f (+2 overloads)
export mix
mix
(
fragment: {
label: number;
glyph: number;
local: d.v2f;
color: d.v4f;
distance: number;
round: number;
reach: number;
}
fragment
.
color: d.v4f

Holds the label colour and opacity.

color
.
xyz: d.v3f
xyz
,
const TOP: d.v3f
TOP
,
import std
std
.
function saturate(value: number): number (+1 overload)
export saturate
saturate
(
fragment: {
label: number;
glyph: number;
local: d.v2f;
color: d.v4f;
distance: number;
round: number;
reach: number;
}
fragment
.
local: d.v2f

Holds the pixel offset from the label anchor in em, with y increasing upwards.

local
.
v2f.y: number
y
/ 0.7));
const
const alpha: number
alpha
=
fragment: {
label: number;
glyph: number;
local: d.v2f;
color: d.v4f;
distance: number;
round: number;
reach: number;
}
fragment
.
color: d.v4f

Holds the label colour and opacity.

color
.
v4f.w: number
w
*
const shown: number
shown
*
import std
std
.
function saturate(value: number): number (+1 overload)
export saturate
saturate
(
fragment: {
label: number;
glyph: number;
local: d.v2f;
color: d.v4f;
distance: number;
round: number;
reach: number;
}
fragment
.
distance: number

Gives signed distance to the glyph edge in screen pixels, positive inside. It uses the median of the first three atlas channels.

distance
+ 0.5);
return
import d
d
.
const vec4f: d.Vec4f
(v0: AnyNumericVec3Instance, w: number) => d.v4f (+9 overloads)
vec4f
(
const ink: d.v3f
ink
*
const alpha: number
alpha
,
const alpha: number
alpha
);
};
const
const line: text.TextSet
line
=
import text
text
.
function create(root: TgpuRoot, font: font.Font, options: text.Options): text.TextSet

Creates labels drawn from a baked MSDF font atlas. The CPU lays out each string, and instanced glyph quads place it in world or screen space. Throws unless capacity and length are integers of at least 1.

@injects ― per-instance move, setLabel, setQuad, quadAt and erase, for kernels piped with it

@owns ― 2 buffers, a sampler: the quads and the labels

@binds ― 3 storage, 1 uniform: the drawing, the quads, the labels and the font's glyphs, and passes.pixels with text.screen or text.pinned

@binds ― 1 storage: move or setLabel, the labels

@binds ― 1 storage: setQuad, quadAt or erase, the quads

@records ― drawing, added to passes, after the passes that move the labels.

@param ― font the font it draws with, which it borrows

@param ― options.place a 'use gpu' function; it and shade carry the fills of what they read beside the camera, such as tgpu.fn(lifted).pipe(scene.inject())

@returns ― a set of labels you destroy before its font

create
(
const root: TgpuRoot
root
,
const sans: font.Font
sans
, {
Options.camera?: cameras.CameraBase | undefined

Supplies the camera for facing, flat and pinned, unless the pass description injects its own. Screen text needs no camera. Default none, which requires camera-dependent text to use a description that injects a camera.

camera
:
const view: cameras.OrbitCamera
view
,
Options.capacity: number

Sets the number of labels to an integer of at least 1.

capacity
: 1,
Options.length: number

Limits glyphs drawn per label to an integer of at least 1. Spaces use no glyph slot; glyphs beyond the limit are omitted.

length
: 48,
Options.place?: text.Place | undefined

Places glyph corners with facing, flat, pinned, screen or a custom function. Default facing.

place
:
import text
text
.
const screen: (position: d.v3f, offset: d.v2f) => d.v4f

Places text on screen with position in pixels from the top-left of the region the pass draws. em uses points, the pass's passes.pixels pixels per point, to preserve label size across pixel densities.

screen
,
Options.shade?: ((fragment: text.Fragment) => d.v4f) | undefined

Returns premultiplied colour for a fragment. Default the label colour over the glyph with one pixel of antialiasing at its edge.

shade
:
const typewriter: (fragment: text.Fragment) => d.v4f
typewriter
,
});
const line: text.TextSet
line
.
TextSet.write(label: number, text: string, options: Options): Layout

Lays out and uploads only the specified label. Use write(label, '') to empty it. Returns the layout, valid until the next write. Throws for a label outside the set or an em value that is not greater than 0.

write
(0, 'The tide is coming in.', {
Options.em: number

Sets positive units per em. World text uses world units. Text placed with screen or pinned uses points, scaled by the text set pixelRatio.

em
: 20,
Options.align?: "left" | "center" | "right" | undefined

Sets the anchor position along each line. Default center.

align
: 'left',
Options.baseline?: "alphabetic" | "top" | "cap" | "middle" | "bottom" | undefined

Sets the vertical anchor at the first line top, the top of its capitals, block middle, first baseline or last line bottom. Default middle.

baseline
: 'alphabetic' });
const typed: TgpuUniform<d.F32>
typed
.
TgpuBufferBindingBase<F32>.write(data: number, options?: BufferWriteOptions): void
write
(
const seconds: number
seconds
* 10); // ten glyphs a second

Text blends over the scene. Use blend: 'add' for glowing text, or blend: 'translucent' to draw into a description’s translucency targets in any order with other translucent drawings. It tests depth without writing it, so draw it after the scene. See Passes for target outputs.

Part
font.atlas An rgba8unorm texture with three sharp-distance channels and one rounded-distance channel.
font.glyphs A storage buffer of each glyph’s quad in em and its cell of the atlas, 32 bytes a glyph.
quads capacity * length entries of 16 bytes each, storing pen, glyph and em in a storage buffer.
labels A storage buffer of each label’s text.Label, 32 bytes.

Multiple text sets can share a font. Destroy the sets before the font. Creating a set from a destroyed font throws The font was destroyed.

  • typeset supports Latin text from left to right, with one glyph per character, advances and pair kerning. It does not support ligatures or contextual forms, which the bake disables. It also omits right-to-left and bidirectional text, scripts requiring shaping such as Arabic and Indic scripts, Chinese and Japanese character sets, width-based wrapping, ellipses, line spacing, hyphenation, tabs and fallback fonts.
  • For these capabilities on the web, use Glyph’s layout engine alongside this package.
  • The atlas uses raw RGBA bytes. In react-native-webgpu 0.17.0, createImageBitmap premultiplies alpha while decoding, corrupting distances where the fourth channel is below 1.
  • Serve the atlas compressed. Inter’s atlas is 617 KB, or 191 KB with gzip.
  • Label size belongs to layout; label rows have no scale field. Read a separate scale in a custom place, as in the swelling “+1” example.