threlte logo
@threlte/extras

<InstancedMesh>

The component <InstancedMesh> is wrapping the Three.js object InstancedMesh and provides instanced rendering support. Use <InstancedMesh> if you have to render a large number of objects with the same geometry and material but with different world transformations and colors. The usage of <InstancedMesh> will help you to reduce the number of draw calls and thus improve the overall rendering performance in your application.

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

<div>
  <Canvas>
    <Scene />
  </Canvas>
</div>

<style>
  div {
    height: 100%;
  }
</style>
<script lang="ts">
  import { T, useTask } from '@threlte/core'
  import { Instance, InstancedMesh } from '@threlte/extras'

  let dn = Date.now()
  useTask(() => (dn = Date.now()))
</script>

<InstancedMesh>
  <T.SphereGeometry args={[0.5]} />
  <T.MeshStandardMaterial color="white" />

  <Instance
    position.x={-2}
    position.y={Math.sin(dn / 1000 + 40)}
  />
  <Instance
    position.x={-1}
    position.y={Math.sin(dn / 1000 + 10)}
  />
  <Instance
    position.x={0}
    position.y={Math.sin(dn / 1000 + 5)}
  />
  <Instance
    position.x={1}
    position.y={Math.sin(dn / 1000 + 200)}
  />
  <Instance
    position.x={2}
    position.y={Math.sin(dn / 1000 + 550)}
  />
</InstancedMesh>

<T.DirectionalLight
  position.y={10}
  position.z={5}
/>

<T.AmbientLight intensity={0.1} />

Usage

An <InstancedMesh> is used in conjunction with the <Instance> component:

<InstancedMesh>
  <T.BoxGeometry />
  <T.MeshStandardMaterial />

  <Instance />
  <Instance />
</InstancedMesh>

It’s also possible to nest other objects in an <InstancedMesh> component:

<InstancedMesh>
  <T.BoxGeometry />
  <T.MeshStandardMaterial />

  <Instance />
  <Instance />

  <GLTF />
</InstancedMesh>

Provide an id to use multiple <InstancedMesh> components:

<InstancedMesh id="tree">
  <T is={treeGeometry} />
  <T.MeshStandardMaterial map={treeTexture} />

  <InstancedMesh id="leaf">
    <T is={leafGeometry} />
    <T.MeshStandardMaterial map={leafTexture} />

    <T.Group position.x={1}>
      <Instance id="tree" /> // Instance of InstancedMesh with id="tree"
      <Instance id="leaf" /> // Instance of InstancedMesh with id="leaf"
    </T.Group>

    <T.Group position.x={-2}>
      <Instance id="tree" />
      <Instance id="leaf" />
    </T.Group>
  </InstancedMesh>
</InstancedMesh>

Instance count

Use the property limit to set the maximum amount of <Instance> components (defaults to 1000). The property limit will be used to initialize the internally used Float32Array. Use the property range to optionally limit the amount of drawn instances.

<InstancedMesh
  limit={10000}
  range={100}
>
  <T.BoxGeometry />
  <T.MeshStandardMaterial />

  <Instance />
  <Instance />
</InstancedMesh>

Events

Mouse around in the example below.

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

  let paused = $state(false)
</script>

<Pane
  position="fixed"
  title="Instanced Colors"
>
  <Checkbox
    bind:value={paused}
    label="paused"
  />
</Pane>

<Canvas>
  <Scene {paused} />
</Canvas>
import { Color } from 'three'
import { Tween } from 'svelte/motion'
import { cubicOut } from 'svelte/easing'

export default class {
  y = new Tween(0, { easing: cubicOut, duration: 250 })
  scale = $derived(this.y.current + 1)
  startColor = new Color()
  endColor = new Color()
  color = $derived(this.startColor.clone().lerpHSL(this.endColor, this.y.current))
  constructor(
    startColor: Color,
    endColor: Color,
    public x: number,
    public z: number
  ) {
    this.startColor.set(startColor)
    this.endColor.set(endColor)
  }
}
<script lang="ts">
  import BallInstance from './BallInstance.svelte'
  import { Color } from 'three'
  import { DirectionalLight } from 'three'
  import { Instance, InstancedMesh, interactivity } from '@threlte/extras'
  import { T, useTask, useThrelte } from '@threlte/core'

  let { paused = false }: { paused?: boolean } = $props()

  const width = 10
  const limit = width * width
  const gap = 2.5
  const offset = (width * gap) / 2

  const startColor = new Color('blue')
  const endColor = new Color('yellow')
  const instances: BallInstance[] = []
  for (let i = 0; i < limit; i += 1) {
    const x = (i % width) * gap - offset
    const z = Math.floor(i / width) * gap - offset
    instances.push(new BallInstance(startColor, endColor, x, z))
  }

  const { size } = useThrelte()
  const zoom = $derived($size.width / (1.5 * gap * width))

  interactivity({
    filter(items) {
      // only report the first intersection
      return items.slice(0, 1)
    }
  })

  const light = new DirectionalLight()
  const lightRadius = 10
  const lightHeight = 5

  let time = 0
  const { start, stop } = useTask(
    (delta) => {
      time += delta
      const x = lightRadius * Math.cos(time)
      const z = lightRadius * Math.sin(time)
      light.position.set(x, lightHeight, z)
      light.lookAt(0, 0, 0)
    },
    { autoStart: false }
  )

  $effect(() => {
    if (!paused) {
      start()
    }
    return () => {
      stop()
    }
  })
</script>

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

<InstancedMesh
  {limit}
  range={limit}
>
  <T.SphereGeometry />
  <T.MeshToonMaterial />

  {#each instances as instance}
    <Instance
      rotation.x={0.5 * Math.PI}
      position.x={instance.x}
      position.y={instance.y.current}
      scale={instance.scale}
      position.z={instance.z}
      color={instance.color}
      onpointerenter={() => {
        instance.y.set(1)
      }}
      onpointerleave={() => {
        instance.y.set(0)
      }}
    />
  {/each}
</InstancedMesh>

<T is={light} />

Instances also support interactivity events.

<InstancedMesh>
  <T.BoxGeometry />
  <T.MeshStandardMaterial />

  <Instance onclick={onClick} />
</InstancedMesh>

Nesting

Instances can be nested in other objects and all parent transformations apply as usual:

<InstancedMesh>
  <T.BoxGeometry />
  <T.MeshStandardMaterial />

  <T.Group rotation.z={DEG2RAD * 180}>
    <Instance />

    <T.Group position.y={2}>
      <Instance />
    </T.Group>
  </T.Group>
</InstancedMesh>

Component Signature

<InstancedMesh> extends <T . InstancedMesh> and supports all its props, slot props, bindings and events.

Props

name
type
required
default
description

id
string
no
default

limit
number
no
1000
Limits the amount of possible <Instance> components.

range
number
no
1000
Limits the amount of drawn <Instance> components.

update
boolean
no
true
Whether the THREE.InstancedMesh should be updated.