threlte logo
@threlte/extras

<Text3DGeometry>

Render 3D text as a geometry using Three.js’ TextGeometry.

<script lang="ts">
  import { Canvas } from '@threlte/core'
  import { Checkbox, Pane, Slider, Textarea } from 'svelte-tweakpane-ui'
  import Scene from './Scene.svelte'
  import { MathUtils } from 'three'

  let options = $state({
    text: 'Hello\nWorld',
    bevelEnabled: true,
    bevelOffset: 0,
    bevelSegments: 20,
    bevelSize: 0.2,
    bevelThickness: 0.1,
    curveSegments: 12,
    depth: 1,
    size: 5,
    smooth: 0.1
  })
</script>

<Pane
  title="Text3DGeometry"
  position="fixed"
>
  <Textarea
    bind:value={options.text}
    label="text"
  />
  <Checkbox
    label="bevelEnabled"
    bind:value={options.bevelEnabled}
  />
  <Slider
    label="bevelOffset"
    bind:value={options.bevelOffset}
    min={0}
    max={2}
  />
  <Slider
    label="bevelSegments"
    bind:value={options.bevelSegments}
    step={1}
    min={0}
    max={50}
  />
  <Slider
    label="bevelSize"
    bind:value={options.bevelSize}
    min={0}
    max={2}
  />
  <Slider
    label="bevelThickness"
    bind:value={options.bevelThickness}
    min={0}
    max={2}
  />
  <Slider
    label="curveSegments"
    bind:value={options.curveSegments}
    step={1}
    min={0}
    max={50}
  />
  <Slider
    label="depth"
    bind:value={options.depth}
    min={0}
    max={5}
  />
  <Slider
    label="size"
    bind:value={options.size}
    min={0}
    max={10}
  />
  <Slider
    label="smooth"
    bind:value={options.smooth}
    min={0}
    max={MathUtils.degToRad(180)}
  />
</Pane>

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

<style>
  div {
    height: 100%;
  }
</style>
<script lang="ts">
  import { T } from '@threlte/core'
  import { Align, Environment, Float, OrbitControls, Text3DGeometry } from '@threlte/extras'

  interface Props {
    text: string
    bevelEnabled: boolean
    bevelOffset: number
    bevelSegments: number
    bevelSize: number
    bevelThickness: number
    curveSegments: number
    depth: number
    size: number
    smooth: number
  }

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

<Align>
  {#snippet children({ align })}
    <T.Mesh>
      <Text3DGeometry
        font="/fonts/Inter-semibold.blob"
        {...rest}
        oncreate={() => {
          align()
        }}
      />
      <T.MeshStandardMaterial
        color="#FD3F00"
        toneMapped={false}
        metalness={1.0}
        roughness={0.1}
      />
    </T.Mesh>
  {/snippet}
</Align>

<Environment url="/textures/equirectangular/hdr/shanghai_riverside_1k.hdr" />

<Float
  rotationIntensity={[0, 3, 0]}
  rotationSpeed={1}
  floatingRange={[-5, 5]}
  speed={1}
>
  <T.PerspectiveCamera
    makeDefault
    position.y={0}
    position.z={20}
    fov={90}
  >
    <OrbitControls
      enableDamping
      enablePan={false}
      enableZoom={false}
    />
  </T.PerspectiveCamera>
</Float>

Examples

Basic Example

Scene.svelte
<script lang="ts">
  import { T } from '@threlte/core'
  import { Text3DGeometry } from '@threlte/extras'
</script>

<T.Mesh>
  <Text3DGeometry text={'Hello World'} />
  <T.MeshStandardMaterial />
</T.Mesh>

Using a Custom Font

If no font property is provided, the default font “Helvetiker” will be used and loaded from the CDN JSDeliver.

If you want to use a custom font, you can generate a font using typeface.js. Provide the path to the resulting JSON file using the prop font.

Suspense-Ready

The component <Text3DGeometry> is suspense-ready. Using it in a <Suspense> boundary will suspend rendering until the provided font is loaded:

Scene.svelte
<script lang="ts">
  import { T } from '@threlte/core'
  import { Text3DGeometry, Suspense } from '@threlte/extras'
  import Fallback from './Fallback.svelte'
</script>

<Suspense>
  <T.Mesh>
    <Text3DGeometry
      font={'path-to-your-font'}
      text={'Hello World'}
    />
    <T.MeshStandardMaterial />
  </T.Mesh>

  {#snippet fallback()}
    <Fallback />
  {/snippet}
</Suspense>

Loading a Font Yourself

You can also load the font yourself and pass it to the component, like so:

Scene.svelte
<script lang="ts">
  import { T, useLoader } from '@threlte/core'
  import { Text3DGeometry } from '@threlte/extras'
  import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js'

  let font = useLoader(FontLoader).load('path-to-your-font')
</script>

{#if $font}
  <T.Mesh>
    <Text3DGeometry
      font={$font}
      text={'Hello World'}
    />
    <T.MeshStandardMaterial />
  </T.Mesh>
{/await}

Centering Text

You can center a text by using the <Align> component and calling the align slot prop when the text geometry is created.

Scene.svelte
<Align>
  {#snippet children({ align })}
    <T.Mesh>
      <Text3DGeometry
        font={'path-to-your-font'}
        text={`Hello!`}
        oncreate={align}
      />
      <T.MeshStandardMaterial />
    </T.Mesh>
  {/snippet}
</Align>

Smoothing Text

You can smooth the text by setting the smooth prop to a value above 0 to smooth all edges where the angle between faces is less than the smooth value.

Component Signature

Props

name
type
required
default
description

font
Font | string
yes
Either a loaded font or a path to a font file.

text
string
yes
The text to display.

bevelEnabled
boolean
no
false

bevelOffset
number
no
0

bevelSegments
number
no
3

bevelSize
number
no
8

bevelThickness
number
no
10

curveSegments
number
no
12

depth
number
no
1

extrudePath
Curve<Vector3>
no

height
number
no
50

size
number
no
100

smooth
number
no
0
Smooth all edges where the angle between faces is less than value

steps
number
no
1

UVGenerator
UVGenerator
no