@threlte/extras
<HUD>
Renders a heads-up-display (HUD). Each HUD creates a new scene rendered on top of the main scene with a separate Threlte context and camera.
The HUD component creates a partially new Threlte context, specifically a new scene and camera.
Everything else in useThrelte is preserved and reused.
<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 { interactivity, useCursor, useViewport } from '@threlte/extras'
  import { Mesh, Quaternion } from 'three'
  interface Props {
    quaternion: Quaternion
    onselect: (arg: string) => void
  }
  let { quaternion, onselect }: Props = $props()
  const viewport = useViewport()
  let meshes: [Mesh, Mesh, Mesh] = [null!, null!, null!]
  const boxCursor = useCursor('pointer')
  const torusCursor = useCursor('pointer')
  const torusKnotCursor = useCursor('pointer')
  interactivity()
  useTask(
    () => {
      for (const mesh of meshes) {
        mesh.quaternion.copy(quaternion)
      }
    },
    { autoInvalidate: false }
  )
  const boxHovering = boxCursor.hovering
  const torusHovering = torusCursor.hovering
  const torusKnotHovering = torusKnotCursor.hovering
</script>
<T.OrthographicCamera
  makeDefault
  zoom={80}
  position={[0, 0, 10]}
/>
<T.AmbientLight intensity={Math.PI / 2} />
<T.PointLight
  position={[10, 10, 10]}
  decay={0}
  intensity={Math.PI * 2}
/>
<T.Mesh
  bind:ref={meshes[0]}
  position={[$viewport.width / 2 - 1, $viewport.height / 2 - 1, 0]}
  onpointerenter={boxCursor.onPointerEnter}
  onpointerleave={boxCursor.onPointerLeave}
  onclick={() => onselect('box')}
  scale={$boxHovering ? 1.1 : 1}
>
  <T.BoxGeometry args={[0.5, 0.5, 0.5]} />
  <T.MeshToonMaterial color={$boxHovering ? 'hotpink' : 'gray'} />
</T.Mesh>
<T.Mesh
  bind:ref={meshes[1]}
  position={[$viewport.width / 2 - 2, $viewport.height / 2 - 1, 0]}
  onpointerenter={torusCursor.onPointerEnter}
  onpointerleave={torusCursor.onPointerLeave}
  onclick={() => onselect('torus')}
  scale={$torusHovering ? 1.1 : 1}
>
  <T.TorusGeometry args={[0.25, 0.1]} />
  <T.MeshToonMaterial color={$torusHovering ? 'hotpink' : 'gray'} />
</T.Mesh>
<T.Mesh
  bind:ref={meshes[2]}
  position={[$viewport.width / 2 - 3, $viewport.height / 2 - 1, 0]}
  onpointerover={torusKnotCursor.onPointerEnter}
  onpointerleave={torusKnotCursor.onPointerLeave}
  onclick={() => onselect('torusknot')}
  scale={$torusKnotHovering ? 1.1 : 1}
>
  <T.TorusKnotGeometry args={[0.215, 0.08, 256]} />
  <T.MeshToonMaterial color={$torusKnotHovering ? 'hotpink' : 'gray'} />
</T.Mesh>
    <script lang="ts">
  import { T, useTask, useThrelte } from '@threlte/core'
  import { Float, OrbitControls, HUD } from '@threlte/extras'
  import { Quaternion } from 'three'
  import HudScene from './HudScene.svelte'
  let selected = $state('box')
  let rotation = $state(0)
  const quaternion = new Quaternion()
  const { camera } = useThrelte()
  useTask(
    (delta) => {
      rotation += delta
      // Spin mesh to the inverse of the default cameras matrix
      quaternion.copy(camera.current.quaternion).invert()
    },
    { autoInvalidate: false }
  )
</script>
<T.PerspectiveCamera
  position={[11, 5, 11]}
  makeDefault
  fov={30}
>
  <OrbitControls enableZoom={false} />
</T.PerspectiveCamera>
<T.DirectionalLight position={[0, 10, 10]} />
<T.AmbientLight intensity={0.6} />
<T.GridHelper args={[5]} />
<HUD>
  <HudScene
    {quaternion}
    onselect={(arg) => {
      selected = arg
    }}
  />
</HUD>
<Float
  speed={8}
  rotation.y={rotation}
>
  {#if selected === 'box'}
    <T.Mesh
      position.y={0.8}
      scale={2}
    >
      <T.BoxGeometry args={[0.5, 0.5, 0.5]} />
      <T.MeshToonMaterial color="turquoise" />
    </T.Mesh>
  {:else if selected === 'torus'}
    <T.Mesh
      position.y={0.8}
      scale={1.8}
    >
      <T.TorusGeometry args={[0.25, 0.1]} />
      <T.MeshToonMaterial color="turquoise" />
    </T.Mesh>
  {:else if selected === 'torusknot'}
    <T.Mesh
      position.y={0.8}
      scale={1.8}
    >
      <T.TorusKnotGeometry args={[0.215, 0.08, 256]} />
      <T.MeshToonMaterial color="turquoise" />
    </T.Mesh>
  {/if}
</Float>
    Because creating a <HUD> is somewhat similar to creating a <Canvas>, it is recommended to use the same best practices and
place all objects you want in the HUD within a new Scene component:
        MyHUD.svelte
      
      <script>
  import Scene from './Scene.svelte'
</script>
<HUD>
  <Scene />
</HUD>
      
        Scene.svelte
      
      <script>
  import { T } from '@threlte/core'
</script>
<T.PerspectiveCamera
  makeDefault
  position={[0, 0, 0]}
  oncreate={(ref) => ref.lookAt(0, 0, 0)}
/>
<T.AmbientLight />
<T.Mesh>
  <T.BoxGeometry />
  <T.MeshStandardMaterial />
</T.Mesh>