threlte logo
@threlte/core

useRender

This hook allows you to execute code on every invalidated frame and after all useFrame hooks have been executed. You receive the state (the same as useThrelte) and a clock delta in seconds. Typically this hook is used to implement advanced rendering techniques such as post processing or custom render passes.

As soon as this hook is used anywhere in the component tree, the default render loop is disabled. You need to take care of rendering yourself.

You may pass additional options to this hook. The property order is useful if you need to order the sequence of useRender callbacks across the component tree where callbacks are ordered from low to high.

type ThrelteUseRenderOptions = {
  order?: number
}

Basic Rendering

Renderer.svelte
<script>
  import { useRender } from '@threlte/core'

  useRender(({ camera, renderer, scene }, delta) => {
    renderer.render(scene, camera.current)
  })
</script>

The state available in the callback is the same as the one available with useThrelte. Some properties (such as the property camera) use a CurrentWritable store. which is a custom Threlte store. It’s a regular writable store that also has a current property which is the current value of the store. It’s useful for accessing the value of a store in a non-reactive context, such as in loops.

Post Processing

This example demonstrates how to use useRender to implement post processing effects using the library postprocessing.

When using SvelteKit (or more broadly, SSR) be sure to add ssr: { noExternal: [ 'postprocessing' ] } to your vite.config.js.

Renderer.svelte
<script>
  import { useThrelte, useRender } from '@threlte/core'
  import {
    EffectComposer,
    EffectPass,
    RenderPass,
    SMAAEffect,
    SMAAPreset,
    BloomEffect,
    KernelSize
  } from 'postprocessing'

  const { scene, renderer, camera, size } = useThrelte()

  // To use the EffectComposer we need to pass arguments to the
  // default WebGLRenderer: https://github.com/pmndrs/postprocessing#usage
  const composer = new EffectComposer(renderer)

  const setupEffectComposer = (camera) => {
    composer.removeAllPasses()
    composer.addPass(new RenderPass(scene, camera))
    composer.addPass(
      new EffectPass(
        camera,
        new BloomEffect({
          intensity: 1,
          luminanceThreshold: 0.15,
          height: 512,
          width: 512,
          luminanceSmoothing: 0.08,
          mipmapBlur: true,
          kernelSize: KernelSize.MEDIUM
        })
      )
    )
    composer.addPass(
      new EffectPass(
        camera,
        new SMAAEffect({
          preset: SMAAPreset.LOW
        })
      )
    )
  }

  // We need to set up the passes according to the camera in use
  $: setupEffectComposer($camera)
  $: composer.setSize($size.width, $size.height)

  useRender((_, delta) => {
    composer.render(delta)
  })
</script>