threlte logo
Basics

Loading Assets

There are a number of different asset types you might like to use in your Threlte application. Three.js has a ton of different loaders that Threlte integrates well with. You’re recommended to use the useLoader hook which will cache any asset it loads and wrap the asset in an asyncWritable for convenience. Caching assets will reduce network requests, bandwidth, and memory usage, which will improve the performance of your application.

This section assumes you placed your assets in your public folder or in a place in your application where you can import them easily.

Models

Models come in many different formats. For example, .gltf’s are mostly json files. You can use three’s GLTFLoader to load a .gltf model.

<script>
  import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
  import { useLoader } from '@threlte/core'

  const gltf = useLoader(GLTFLoader).load('/assets/model.gltf')
</script>

{#if $gltf}
  <T is={$gltf.scene} />
{/if}

The <GLTF/> component is roughly equivalent to the example above.

Convenient: useGltf

@threlte/extras provides a handy hook for loading .gltf models called useGltf:

<script>
  import { useGltf } from '@threlte/extras'
</script>

{#await useGltf('/assets/model.gltf') then gltf}
  <T is={gltf.scene} />
{/await}

Adjusting Parts and Multiple Copies

There are some challenges you’ll probably run into when working with models in Threlte:

  • Your model may have multiple parts that you’d like to adjust individually but there’s no easy way to declaritively achieve that with only one <T/> component.
  • The model is cached which is great but you can’t seem to place multiple copies in your scene.

To address both of these issues, you can use Threlte’s CLI tool @threlte/gltf to generate a svelte component for your model. The generated component has <T/> components for all of your models parts. Adjust the component to your liking, then import and reused it as much as you’d like.

Animations

Three.js uses AnimationMixer to drive animations. Threlte provides a convenient useGltfAnimations hook for gltf’s. See the three.js examples for how to setup a model for your animation needs.

Threlte has a few animation examples to help you get started.

Textures

The TextureLoader is another loader from three that is used for textures.

<script>
  import { TextureLoader } from 'three'
  import { useLoader } from '@threlte/core'

  const texture = useLoader(TextureLoader).load('/assets/texture.png')
</script>

{#if $texture}
  <T.MeshStandardMaterial map={$texture} />
{/if}

Convenient: useTexture

@threlte/extras provides a handy hook for loading textures called useTexture:

<script>
  import { useTexture } from '@threlte/extras'
</script>

{#await useTexture('/assets/texture.png') then texture}
  <T.MeshStandardMaterial map={texture} />
{/await}

Multiple textures

Sometimes you’ll want your materials to be composed of multiple textures. useLoader provides a way to load multiple textures at once and spread the loaded textures on a material.

Loading two textures for the map and normalMap channels can be done like this:

const textures = useLoader(TextureLoader).load({
  map: '/assets/texture.png',
  normalMap: '/assets/normal.png'
})

or with the useTexture hook:

const textures = useTexture({
  map: '/assets/texture.png',
  normalMap: '/assets/normal.png'
})

Then spread on a material:

{#if $textures}
  <T.MeshStandardMaterial {...$textures} />
{/if}

If multiple textures are given, the promise only resolves once all textures have loaded.

Applying different textures to different faces

To declaratively apply two different textures to two different faces of a BoxGeometry, set the attach prop to a function.

<T.Mesh>
  <T.BoxGeometry />
  <T.MeshStandardMaterial
    map={texture1}
    attach={({ parent, ref }) => {
      if (Array.isArray(parent.material)) parent.material = [...parent.material, ref]
      else parent.material = [ref]
    }}
  />
  <T.MeshStandardMaterial
    map={texture2}
    attach={({ parent, ref }) => {
      if (Array.isArray(parent.material)) parent.material = [...parent.material, ref]
      else parent.material = [ref]
    }}
  />
</T.Mesh>

Alternatively, mix declarative and normal three.js code like so for the same result:

<script>
  // imports and other code
  const customMaterials = [
    new MeshStandardMaterial({ map: texture1 }),
    new MeshStandardMaterial({ map: texture2 })
  ]
</script>

<T.Mesh>
  <T.BoxGeometry />
  <T
    is={customMaterials}
    attach="material"
  />
</T.Mesh>

Other Assets

Threlte provides many components to help get started with other assets (like audio) but we don’t have convenient components and hooks for all of them yet. Checkout three.js examples to see what models, techniques and effects you can acheive, then use those examples as a guide for your own custom components.

Async Loading

The return value from useLoader is an AsyncWritable custom store. Its value will be undefined until the asset has loaded.

Since the underlying store’s value is a promise, you can use it within svelte’s await blocks:

{#await $texture then value}
  <T.MeshStandardMaterial map={value} />
{/await}

These hooks can be used similarly to Svelte’s onMount hook however assets can also be loaded after initialization by separating out the load call:

<script>
  import { AudioLoader } from 'three'
  import { useLoader } from '@threlte/core'

  // Instantiate the loader at component initialization
  const loader = useLoader(AudioLoader)

  const onSomeEvent = async () => {
    // Load the asset when needed
    const audioBuffer = await loader.load('/assets/sound.mp3')
  }
</script>

Another set of tools to help orchestrate loading multiple assets before displaying something is the suspense component and hooks.

Context Awareness

The useLoader hook, and other hooks like useTexture, use svelte contexts. The assets loaded with them are only available for child components of your <Canvas> component.