threlte logo
@threlte/xr

<TeleportControls>

<TeleportControls /> creates teleportation controls similar to many native XR experiences.

  <TeleportControls
    handedness={'left' | 'right'}
    maxDistance={10}
    on:teleport={(event) => {}} 
  >
    <T.Mesh teleportSurface>
      <T.CylinderGeometry args={[20, 0.01]} />
      <T.MeshStandardMaterial />
    </T.Mesh>
  </TeleportControls>

This component will set up a raycasting-based teleport behavior once the user pressed a thumbstick forward on the designated controller.

<TeleportControls /> can have an arbitrary number of children, but any child mesh with a teleportSurface attribute will be registered as a navigation mesh that can be teleported to.

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

<Canvas>
  <Scene />
</Canvas>

<VRButton />
<script lang='ts'>
  import * as THREE from 'three'
  import { T, useThrelte } from '@threlte/core'
  import { XR, TeleportControls, Controller, Hand, useTeleport } from '@threlte/xr'
  import { colorOptions } from './colors'

  const { camera, renderer } = useThrelte()
  const teleport = useTeleport()

  renderer.setClearColor(0x000000)

  camera.current.position.z = 1.75
  camera.current.lookAt(0, 1.75, 1)

  teleport(new THREE.Vector3(0.5, 0, 0.5))

  const pointOnCircle = (radius: number, theta: number) => {
    const x = radius * Math.cos(theta)
    const y = radius * Math.sin(theta)

    return { x, y }
  }

  const randomColor = () => {
    return colorOptions[Math.trunc(Math.random() * colorOptions.length)]!
  }

  const cylinders = Array.from({ length: 14 }).map((_, index) => {
    return {
      point: pointOnCircle(5, index / 2),
      color: randomColor(),
    }
  })
</script>

<XR>
  <Controller left />
  <Controller right />
  <Hand left />
  <Hand right />
</XR>

<TeleportControls>
  <T.Mesh
    teleportSurface
    receiveShadow
    rotation={[-Math.PI / 2, 0, 0]}
  >
    <T.CircleGeometry args={[20]} />
    <T.MeshStandardMaterial color='#BDC3C7' />
  </T.Mesh>

  {#each cylinders as { point, color }, index}
    <T.Mesh
      name='cylinder {index}'
      teleportSurface
      position={[point.x, index / 2, point.y]}
      castShadow
      receiveShadow
    >
      <T.CylinderGeometry args={[1, 1, 0.1]} />
      <T.MeshStandardMaterial {color} />
    </T.Mesh>
  {/each}
</TeleportControls>

<T.AmbientLight />

<T.DirectionalLight
  position={[5, 5, 1]}
  castShadow
  shadow.camera.top={50}
  shadow.camera.right={50}
  shadow.camera.left={-50}
  shadow.camera.bottom={-50}
  shadow.mapSize.width={1024}
  shadow.mapSize.height={1024}
/>

<T.PerspectiveCamera
  makeDefault
  position.y={1.8}
  position.z={15}
/>
// Flat UI colors
export const colorOptions = [
  '#1ABC9C',
  '#2ECC71',
  '#3498DB',
  '#9B59B6',
  '#34495E',
  '#16A085',
  '#27AE60',
  '#2980B9',
  '#8E44AD',
  '#2C3E50',
  '#F1C40F',
  '#E67E22',
  '#E74C3C',
  '#ECF0F1',
  '#95A5A6',
  '#F39C12',
  '#D35400',
  '#C0392B',
  '#BDC3C7',
  '#7F8C8D'
]

Component Signature

Props

name
type
required
default
description

handedness
'left' | 'right'
no
'right'
The controller handedness that the teleport controls is linked to.

maxDistance
number
no
20
The maximum radial teleporting distance from the user's current origin, in meters.

raycaster
THREE.Raycaster
no
new THREE.Raycaster()
The raycaster used for teleportation.

Events

name
payload
description

teleport
THREE.Vector3
Fired after a teleportation occurs with the new location as the payload.