threlte logo
Basics

Render Modes

Threlte offers three different render modes to optimize the performance and power usage of your Threlte app. Ideally, you only want to render the scene when it is necessary, such as when the camera moves or when objects are added or removed from the scene. The render mode determines how and when the scene is rendered.

In the default 'on-demand' mode, Threlte is able to determine when a re-render is necessary by observing components. When setting the render mode to 'manual' you must manually trigger a re-render. You can tell Threlte to continuously render the scene in the 'always' mode.

Mode 'on-demand'

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

<div class="wrapper">
  <Canvas>
    <Scene />

    <RenderIndicator />
  </Canvas>

  <div class="description">
    <p>
      <strong>Click and drag</strong> to rotate the camera.
    </p>
    <p>
      <strong>Hover</strong> over the sphere to scale it up.
    </p>
  </div>
</div>

<style>
  div.wrapper {
    height: 100%;
  }

  div.description {
    position: absolute;
    bottom: 10px;
    left: 10px;
    z-index: 10;
    color: #fe3d00;
  }
</style>
<script lang="ts">
  import { useStage, useTask, useThrelte } from '@threlte/core'
  import { Pane, WaveformMonitor } from 'svelte-tweakpane-ui'

  const { shouldRender, renderStage } = useThrelte()

  const afterRenderStage = useStage('after-render', {
    after: renderStage
  })

  let log = Array(100).fill(0)

  useTask(
    () => {
      log = update(log)
    },
    {
      autoInvalidate: false,
      stage: afterRenderStage
    }
  )

  function update(log: number[]) {
    log.shift()
    log.push(shouldRender() ? 1 : 0)
    return log
  }
</script>

<Pane
  title="Rendering Activity"
  position="fixed"
>
  <WaveformMonitor
    value={log}
    min={-1}
    max={2}
  />
</Pane>
<script lang="ts">
  import { T } from '@threlte/core'
  import { Grid, OrbitControls, interactivity } from '@threlte/extras'
  import { spring } from 'svelte/motion'

  interactivity()

  const scale = spring(1)
</script>

<T.PerspectiveCamera
  makeDefault
  position={[10, 10, 10]}
  on:create={({ ref }) => {
    ref.lookAt(0, 0, 0)
  }}
>
  <OrbitControls />
</T.PerspectiveCamera>

<T.DirectionalLight
  position={[3, 10, 7]}
  intensity={Math.PI}
/>

<T.AmbientLight intensity={0.3} />

<T.Group
  scale={$scale}
  on:pointerenter={() => scale.set(1.5)}
  on:pointerleave={() => scale.set(1)}
>
  <T.Mesh position.y={1}>
    <T.SphereGeometry args={[1]} />
    <T.MeshStandardMaterial
      color="#FE3D00"
      toneMapped={false}
    />
  </T.Mesh>
</T.Group>

<Grid
  cellColor="#FE3D00"
  sectionColor="#FE3D00"
/>

In the mode 'on-demand', Threlte renders the scene only when the current frame is invalidated. This may happen automatically when changes are detected or the frame is manually invalidated. This is the default mode and the recommended way of working with Threlte.

Automatic Invalidation

Threlte is able to automatically invalidate the current frame by observing component props and the mounting and unmounting of components. This means that when you e.g. change the position of a <T.Mesh> via component props, Threlte will automatically invalidate the current frame and request a new frame.

<script>
  import { T } from '@threlte/core'

  let x = 0

  const move = () => {
    x += 1
  }
</script>

<T.Mesh position.x={x} />

Manual Invalidation

In some cases, you may want to manually invalidate the current frame because Threlte is not able to detect changes. To do this, you can use the invalidate function from the useThrelte hook.

<script>
  import { T, useThrelte } from '@threlte/core'
  import { Mesh } from 'three'

  const { invalidate } = useThrelte()

  const mesh = new Mesh()

  export const moveMesh = () => {
    // moving the mesh manually
    mesh.position.x = 1
    // invalidate the current frame
    invalidate()
  }
</script>

<T is={mesh} />

useTask

The useTask hook is by default configured to automatically invalidate the current frame on every frame. This means that you can use it to animate your scene without having to manually invalidate the current frame.

import { useTask } from '@threlte/core'

useTask(() => {
  // useTask will automatically invalidate the current
  // frame, so you don't have to do it manually.
})

Sometimes you may want to manually invalidate the current frame from within a task. To do this, you can use the invalidate function from the useThrelte hook and set the autoInvalidate option to false:

import { useTask, useThrelte } from '@threlte/core'

const { invalidate } = useThrelte()

useTask(
  () => {
    // Because `autoInvalidate` is set to `false`, the current
    // frame will not be invalidated automatically and you can
    // conditionally invalidate the current frame.
    invalidate()
  },
  { autoInvalidate: false }
)

Mode 'manual'

In the manual mode, you must manually trigger a re-render:

const { advance } = useThrelte()
advance()

This mode is useful when you want to have full control over when the scene is rendered. For example, you may want to render the scene only when the user interacts with the scene.

Mode 'always'

In the 'always' mode, Threlte continuously renders the scene. This mode is the easiest to use, but it is also the most resource intensive and should only be used when necessary.

Setting the Render Mode

<Canvas> Prop

You can set the render mode by setting the property renderMode on the <Canvas> component:

<Canvas renderMode="on-demand" />

useThrelte Hook

You can also set the render mode from anywhere within your Threlte app using the useThrelte hook:

const { renderMode } = useThrelte()
renderMode.set('on-demand')

The renderMode property can be changed at any time, but it will only take effect on the next frame.

Render Modes and Custom Rendering

By default, Threlte will automatically render the scene for you. In some cases, you may want to render the scene yourself, for example when using post processing.

  1. Set autoRender to false on the <Canvas> component. This will prevent Threlte from automatically rendering the scene and you can render the scene yourself.
<Canvas autoRender={false} />
  1. Set up a task that renders the scene. There are two ways to do this:
  • Add a task to Threlte’s default renderStage. Tasks in that stage will be executed after tasks in Threlte’s mainStage and only when a re-render is necessary based on the current render mode. This is the recommended approach.
import { useTask, useThrelte } from '@threlte/core'

const { renderStage } = useThrelte()

useTask(
  () => {
    // render here
  },
  { stage: renderStage, autoInvalidate: false }
)
  • Use shouldRender from the hook useThrelte. This function will evaluate to true based on the current render mode. This allows for more fine-grained control over when to render and is useful when you want to render in a task that is not in Threlte’s default renderStage.
import { useThrelte, useTask } from '@threlte/core'

const { shouldRender } = useThrelte()

useTask(
  () => {
    if (shouldRender()) {
      // render here
    }
  },
  { autoInvalidate: false }
)