threlte logo
@threlte/extras

<Text>

webgl <Text> works with WebGL only.

The <Text> component uses troika-three-text to render text.

<script lang="ts">
  import { Canvas } from '@threlte/core'
  import Scene from './Scene.svelte'
  import { Color, List, Pane, Slider, Text } from 'svelte-tweakpane-ui'

  const anchorXOptions = {
    left: 'left',
    center: 'center',
    right: 'right'
  } as const

  const anchorYOptions = {
    top: 'top',
    'top-baseline': 'top-baseline',
    middle: 'middle',
    'bottom-baseline': 'bottom-baseline',
    bottom: 'bottom'
  } as const

  const directionOptions = {
    auto: 'auto',
    ltr: 'ltr',
    rtl: 'rtl'
  } as const

  const textAlignOptions = {
    left: 'left',
    right: 'right',
    center: 'center',
    justify: 'justify'
  } as const

  const whiteSpaceOptions = {
    normal: 'normal',
    nowrap: 'nowrap',
    'pre-wrap': 'pre-wrap'
  } as const

  const overflowWrapOptions = {
    normal: 'normal',
    'break-word': 'break-word'
  } as const

  let options = $state({
    text: 'hello world',
    fontSize: 1,
    maxWidth: 20,
    letterSpacing: -0.1,
    lineHeight: 1.15,
    textIndent: 0,
    textAlign: 'center' as keyof typeof textAlignOptions,
    whiteSpace: 'normal' as keyof typeof whiteSpaceOptions,
    overflowWrap: 'normal' as keyof typeof overflowWrapOptions,
    direction: 'auto' as keyof typeof directionOptions,
    anchorX: 'center' as keyof typeof anchorXOptions,
    anchorY: 'middle' as keyof typeof anchorYOptions,
    curveRadius: 0,
    color: '#ffffff',
    fillOpacity: 1,
    outlineWidth: 0,
    outlineColor: '#000000',
    outlineOpacity: 1,
    outlineBlur: 0,
    outlineOffsetX: 0,
    outlineOffsetY: 0,
    strokeWidth: 0,
    strokeColor: '#808080',
    strokeOpacity: 1
  })
</script>

<Pane
  title="Text"
  position="fixed"
>
  <Text
    bind:value={options.text}
    label="text"
  />

  <Slider
    bind:value={options.fontSize}
    label="fontSize"
    min={0.1}
    max={4}
    step={0.1}
  />
  <Slider
    bind:value={options.maxWidth}
    label="maxWidth"
    min={1}
    max={40}
  />
  <Slider
    bind:value={options.letterSpacing}
    label="letterSpacing"
    min={-0.2}
    max={0.5}
    step={0.01}
  />
  <Slider
    bind:value={options.lineHeight}
    label="lineHeight"
    min={0.5}
    max={2.5}
    step={0.05}
  />
  <Slider
    bind:value={options.textIndent}
    label="textIndent"
    min={0}
    max={5}
    step={0.1}
  />
  <List
    bind:value={options.textAlign}
    label="textAlign"
    options={textAlignOptions}
  />
  <List
    bind:value={options.whiteSpace}
    label="whiteSpace"
    options={whiteSpaceOptions}
  />
  <List
    bind:value={options.overflowWrap}
    label="overflowWrap"
    options={overflowWrapOptions}
  />
  <List
    bind:value={options.direction}
    label="direction"
    options={directionOptions}
  />

  <List
    bind:value={options.anchorX}
    label="anchorX"
    options={anchorXOptions}
  />
  <List
    bind:value={options.anchorY}
    label="anchorY"
    options={anchorYOptions}
  />

  <Slider
    bind:value={options.curveRadius}
    label="curveRadius"
    min={-10}
    max={10}
    step={0.1}
  />

  <Color
    bind:value={options.color}
    label="color"
  />
  <Slider
    bind:value={options.fillOpacity}
    label="fillOpacity"
    min={0}
    max={1}
    step={0.01}
  />

  <Slider
    bind:value={options.outlineWidth}
    label="outlineWidth"
    min={0}
    max={0.5}
    step={0.01}
  />
  <Color
    bind:value={options.outlineColor}
    label="outlineColor"
  />
  <Slider
    bind:value={options.outlineOpacity}
    label="outlineOpacity"
    min={0}
    max={1}
    step={0.01}
  />
  <Slider
    bind:value={options.outlineBlur}
    label="outlineBlur"
    min={0}
    max={0.5}
    step={0.01}
  />
  <Slider
    bind:value={options.outlineOffsetX}
    label="outlineOffsetX"
    min={-0.5}
    max={0.5}
    step={0.01}
  />
  <Slider
    bind:value={options.outlineOffsetY}
    label="outlineOffsetY"
    min={-0.5}
    max={0.5}
    step={0.01}
  />

  <Slider
    bind:value={options.strokeWidth}
    label="strokeWidth"
    min={0}
    max={0.5}
    step={0.01}
  />
  <Color
    bind:value={options.strokeColor}
    label="strokeColor"
  />
  <Slider
    bind:value={options.strokeOpacity}
    label="strokeOpacity"
    min={0}
    max={1}
    step={0.01}
  />
</Pane>

<div>
  <Canvas>
    <Scene {...options} />
  </Canvas>
</div>

<style>
  div {
    height: 100%;
    background-color: rgb(254 61 0 / 0.2);
  }
</style>
<script lang="ts">
  import { T } from '@threlte/core'
  import { Grid, OrbitControls, Text } from '@threlte/extras'

  interface Props {
    text: string
    fontSize: number
    maxWidth: number
    letterSpacing: number
    lineHeight: number
    textIndent: number
    textAlign: 'left' | 'right' | 'center' | 'justify'
    whiteSpace: 'normal' | 'nowrap' | 'pre-wrap'
    overflowWrap: 'normal' | 'break-word'
    anchorX: 'left' | 'center' | 'right'
    anchorY: 'top' | 'top-baseline' | 'middle' | 'bottom-baseline' | 'bottom'
    curveRadius: number
    color: string
    fillOpacity: number
    outlineWidth: number
    outlineColor: string
    outlineOpacity: number
    outlineBlur: number
    outlineOffsetX: number
    outlineOffsetY: number
    strokeWidth: number
    strokeColor: string
    strokeOpacity: number
  }

  let { ...textProps }: Props = $props()
</script>

<T.OrthographicCamera
  zoom={80}
  position={[0, 5, 10]}
  makeDefault
  oncreate={(ref) => {
    ref.lookAt(0, 0, 0)
  }}
/>

<OrbitControls
  autoRotate
  enableDamping
  enableZoom={false}
  autoRotateSpeed={0.3}
/>

<Text
  position.y={0.5}
  {...textProps}
/>

<Grid sectionColor="#FF3E00" />

Example

<script>
  import { Text } from '@threlte/extras'
  let value = ''
</script>

<input
  type="text"
  bind:value
/>
<Text text={value} />

<Text> is suspense-ready and will suspend while the font is loading. You can use the characters prop to preload a specific set of characters to prevent FOUC.

<script>
  import { Text, Suspense } from '@threlte/extras'
</script>

<Suspense>
  <Text
    text="HELLO WORLD"
    characters="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  />

  {#snippet fallback()}
    <!-- show fallback content while font data is loading -->
  {/snippet}
</Suspense>

Component Signature

<Text> extends < T . Mesh > and supports all its props, snippets, bindings and events.

Props

name
type
required
default
description

anchorX
number | 'left' | 'center' | 'right' | string
no
Defines the horizontal position in the text block that should line up with the local origin. Can be specified as a numeric x position in local units, a string percentage of the total text block width e.g. '25%', or one of the following keyword strings: 'left', 'center', or 'right'.

anchorY
number | 'top' | 'top-baseline' | 'middle' | 'bottom-baseline' | 'bottom' | string
no
Defines the vertical position in the text block that should line up with the local origin. Can be specified as a numeric y position in local units (note: down is negative y), a string percentage of the total text block height e.g. '25%', or one of the following keyword strings: 'top', 'top-baseline', 'middle', 'bottom-baseline', or 'bottom'.

characters
string | string[] | null
no
null
A string or array of string character sequences for which to pre-generate glyph SDF textures. Note that this will honor ligature substitution, so you may need to specify ligature sequences in addition to their individual characters to get all possible glyphs, e.g. ["t", "h", "th"] to get the "t" and "h" glyphs plus the "th" glyph.

clipRect
[number, number, number, number] | null
no
If specified, defines a [minX, minY, maxX, maxY] of a rectangle outside of which all pixels will be discarded. This can be used for example to clip overflowing text when whiteSpace='nowrap'.

color
ColorRepresentation
no
This is a shortcut for setting the color of the text's material. You can use this if you don't want to specify a whole custom material. Also, if you do use a custom material, this color will only be used for this particuar Text instance, even if that same material instance is shared across multiple Text objects.

colorRanges
{ [key: `${number}`]: ColorRepresentation; } | null
no
WARNING: This API is experimental and may change. This allows more fine-grained control of colors for individual or ranges of characters, taking precedence over the material's color. Its format is an Object whose keys each define a starting character index for a range, and whose values are the color for each range. The color value can be a numeric hex color value, a THREE.Color object, or any of the strings accepted by THREE.Color.

curveRadius
number
no
Defines a cylindrical radius along which the text's plane will be curved. Positive numbers put the cylinder's centerline (oriented vertically) that distance in front of the text, for a concave curvature, while negative numbers put it behind the text for a convex curvature. The centerline will be aligned with the text's local origin; you can use anchorX to offset it. Since each glyph is by default rendered with a simple quad, each glyph remains a flat plane internally. You can use glyphGeometryDetail to add more vertices for curvature inside glyphs.

depthOffset
number
no
This is a shortcut for setting the material's polygonOffset and related properties, which can be useful in preventing z-fighting when this text is laid on top of another plane in the scene. Positive numbers are further from the camera, negatives closer.

direction
'auto' | 'ltr' | 'rtl'
no
Sets the base direction for the text. The default value of "auto" will choose a direction based on the text's content according to the bidi spec. A value of "ltr" or "rtl" will force the direction.

fillOpacity
number
no
WARNING: This API is experimental and may change. The opacity of the glyph's fill from 0 to 1. This behaves like the material's opacity but allows giving the fill a different opacity than the strokeOpacity. A fillOpacity of 0 makes the interior of the glyph invisible, leaving just the strokeWidth. Defaults to 1.

font
null | string
no
null
URL of a custom font to be used. Font files can be any of the formats supported by OpenType (see https://github.com/opentypejs/opentype.js). Defaults to the Roboto font loaded from Google Fonts.

fontSize
number
no
The size at which to render the font in local units; corresponds to the em-box height of the chosen font.

glyphGeometryDetail
number
no
Controls number of vertical/horizontal segments that make up each glyph's rectangular plane. Defaults to 1. This can be increased to provide more geometrical detail for custom vertex shader effects, for example.

letterSpacing
number
no
Sets a uniform adjustment to spacing between letters after kerning is applied. Positive numbers increase spacing and negative numbers decrease it.

lineHeight
number | string
no
Sets the height of each line of text, as a multiple of the fontSize. Defaults to 'normal' which chooses a reasonable height based on the chosen font's ascender/descender metrics.

material
Material | Material[]
no
Defines a _base_ material to be used when rendering the text. This material will be automatically replaced with a material derived from it, that adds shader code to decrease the alpha for each fragment (pixel) outside the text glyphs, with antialiasing. By default it will derive from a simple white MeshBasicMaterial, but you can use any of the other mesh materials to gain other features like lighting, texture maps, etc. Also see the color shortcut property.

maxWidth
number
no
The maximum width of the text block, above which text may start wrapping according to the whiteSpace and overflowWrap properties.

outlineBlur
number | string
no
WARNING: This API is experimental and may change. A blur radius applied to the outer edge of the text's outline. If the outlineWidth is zero, the blur will be applied at the glyph edge, like CSS's text-shadow blur radius. Can be specified as either an absolute number in local units, or as a percentage string e.g. "12%" which is treated as a percentage of the fontSize. Defaults to 0.

outlineColor
ColorRepresentation
no
WARNING: This API is experimental and may change. The color of the text outline, if outlineWidth/outlineBlur/outlineOffsetX/Y are set. Defaults to black.

outlineOffsetX
number | string
no
WARNING: This API is experimental and may change. A horizontal offset for the text outline. Can be specified as either an absolute number in local units, or as a percentage string e.g. "12%" which is treated as a percentage of the fontSize. Defaults to 0.

outlineOffsetY
number | string
no
WARNING: This API is experimental and may change. A vertical offset for the text outline. Can be specified as either an absolute number in local units, or as a percentage string e.g. "12%" which is treated as a percentage of the fontSize. Defaults to 0.

outlineOpacity
number
no
WARNING: This API is experimental and may change. The opacity of the outline, if outlineWidth/outlineBlur/outlineOffsetX/Y are set. Defaults to 1.

outlineWidth
number | string
no
WARNING: This API is experimental and may change. The width of an outline/halo to be drawn around each text glyph using the outlineColor and outlineOpacity. Can be specified as either an absolute number in local units, or as a percentage string e.g. "12%" which is treated as a percentage of the fontSize. Defaults to 0, which means no outline will be drawn unless an outlineOffsetX/Y or outlineBlur is set.

overflowWrap
'normal' | 'break-word' | 'normal'
no
Defines how text wraps if the whiteSpace property is normal. Can be either 'normal' to break at whitespace characters, or 'break-word' to allow breaking within words. Defaults to 'normal'.

sdfGlyphSize
number | null
no
null
The size of each glyph's SDF (signed distance field) used for rendering. This must be a power-of-two number. Defaults to 64 which is generally a good balance of size and quality for most fonts. Larger sizes can improve the quality of glyph rendering by increasing the sharpness of corners and preventing loss of very thin lines, at the expense of increased memory footprint and longer SDF generation time.

strokeColor
ColorRepresentation | Color
no
WARNING: This API is experimental and may change. The color of the text stroke, if strokeWidth is greater than zero. Defaults to gray.

strokeOpacity
number
no
WARNING: This API is experimental and may change. The opacity of the stroke, if strokeWidth is greater than zero. Defaults to 1.

strokeWidth
number | string
no
WARNING: This API is experimental and may change. The width of an inner stroke drawn inside each text glyph using the strokeColor and strokeOpacity. Can be specified as either an absolute number in local units, or as a percentage string e.g. "12%" which is treated as a percentage of the fontSize. Defaults to 0.

text
string
no
The string of text to be rendered.

textAlign
'left' | 'right' | 'center' | 'justify'
no
The horizontal alignment of each line of text within the overall text bounding box.

textIndent
number
no
Indentation for the first character of a line; see CSS text-indent.

whiteSpace
'normal' | 'nowrap' | 'pre-wrap'
no
Defines whether text should wrap when a line reaches the maxWidth. Can be either 'normal' (the default), to allow wrapping according to the overflowWrap property, or 'nowrap' to prevent wrapping. Note that 'normal' here honors newline characters to manually break lines, making it behave more like 'pre-wrap' does in CSS.

Events

name
payload
description

onsync
void
Called after the text has been laid out and its geometry updated, for example to measure it.