diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml
index 2d62eff2..48bf2d54 100644
--- a/.github/workflows/static.yml
+++ b/.github/workflows/static.yml
@@ -132,6 +132,7 @@ jobs:
mkdir -p public/examples/hit-testing
mkdir -p public/examples/uikit
mkdir -p public/examples/portal
+ mkdir -p public/examples/heads-up-display
cp -r ./examples/minecraft/dist/* ./public/examples/minecraft
cp -r ./examples/pingpong/dist/* ./public/examples/pingpong
cp -r ./examples/rag-doll/dist/* ./public/examples/rag-doll
@@ -144,6 +145,7 @@ jobs:
cp -r ./examples/hit-testing/dist/* ./public/examples/hit-testing
cp -r ./examples/uikit/dist/* ./public/examples/uikit
cp -r ./examples/portal/dist/* ./public/examples/portal
+ cp -r ./examples/heads-up-display/dist/* ./public/examples/heads-up-display
- name: Upload Artifact
uses: actions/upload-artifact@v4
diff --git a/docs/advanced/performance.md b/docs/advanced/performance.md
index 205533f8..9725dcf9 100644
--- a/docs/advanced/performance.md
+++ b/docs/advanced/performance.md
@@ -1,7 +1,7 @@
---
title: Performance
description: Important considerations for building performant immersive web applications with react-three/xr
-nav: 22
+nav: 23
---
All performance optimizations for non-immersive 3D web applications are also applicable for immersive XR web applications. Relevant guides on the topic of performance for 3D web applications are the [R3F performance guide](https://docs.pmnd.rs/react-three-fiber/advanced/scaling-performance), [R3F performance pitfalls](https://docs.pmnd.rs/react-three-fiber/advanced/pitfalls), and the [Threejs tips and tricks](https://discoverthreejs.com/tips-and-tricks/#performance). In general, it is good to check if your web application's performance is GPU- or CPU-bound to select the correct optimization techniques.
diff --git a/docs/handles/handle-component.md b/docs/handles/handle-component.md
index 0308ba23..910e4980 100644
--- a/docs/handles/handle-component.md
+++ b/docs/handles/handle-component.md
@@ -1,7 +1,7 @@
---
title: Handle Component
description: The Handle and HandleTarget components and their properties
-nav: 24
+nav: 25
---
The `Handle` component is the core component of the `@react-three/handle` library, which is built on the `HandleStore`. This store provides developers with more control over the current state and user interactions.
diff --git a/docs/handles/introduction.md b/docs/handles/introduction.md
index c3b87d01..b6292faf 100644
--- a/docs/handles/introduction.md
+++ b/docs/handles/introduction.md
@@ -1,7 +1,7 @@
---
title: Handles
description: Easily build 3D interactions using the concept of handles
-nav: 23
+nav: 24
---
Handles are everywhere, from scrollbar thumbs to window bars to door handles.
diff --git a/docs/handles/prebuild-handles.md b/docs/handles/prebuild-handles.md
index c5583c4c..d85078e5 100644
--- a/docs/handles/prebuild-handles.md
+++ b/docs/handles/prebuild-handles.md
@@ -1,7 +1,7 @@
---
title: Prebuilt Handles for Editor Use Cases
description: The Handle and HandleTarget components and their properties
-nav: 25
+nav: 26
---
The `Handle` component allows for the orchestration of multiple handles together to achieve interactions typically found in 3D editors, such as the `TransformControls` offered by Three.js. Since these prebuilt handles are commonly used, `@react-three/handles` includes two of them: `TransformHandles` (based on TransformControls from Three.js) and `PivotHandles` (based on PivotControls from @react-three/drei). These prebuilt handles can be used in XR and non-XR environments, are highly configurable for use cases including multi-user editing, can be utilized through virtual screens, and respect the event system of the scene, preventing accidental interactions with multiple objects at once.
diff --git a/docs/handles/screen-handle-components.md b/docs/handles/screen-handle-components.md
index 3f00c45a..bce68edb 100644
--- a/docs/handles/screen-handle-components.md
+++ b/docs/handles/screen-handle-components.md
@@ -1,7 +1,7 @@
---
title: Screen Handles
description: Orbit and Map Handles as replacements for Orbit and Map controls
-nav: 26
+nav: 27
---
Screen handles are available for screen-based devices like smartphones and PCs and allow users to move the camera by dragging, swiping, and scrolling on the screen. Three.js directly offers `OrbitControls` and `MapControls`, which are built for this purpose, and we are building on top of their success. The main difference is that the `OrbitHandles` and `MapHandles` we provide use the event system of the scene, which means that interactions with objects in the scene prevent dragging the camera. Furthermore, the event system can forward the screen inputs on a virtual screen to a virtual camera inside a virtual scene, which is showcased in the [editor example](https://pmndrs.github.io/xr/examples/editor/).
diff --git a/docs/migration/from-natuerlich.md b/docs/migration/from-natuerlich.md
index ba363b2b..b4b3b016 100644
--- a/docs/migration/from-natuerlich.md
+++ b/docs/migration/from-natuerlich.md
@@ -1,7 +1,7 @@
---
title: from Natuerlich
description: Migrate your application from natuerlich
-nav: 24
+nav: 25
---
@react-three/xr is inspired by natuerlich, and therefore, many things are similar, especially the way interactions are handled. However, a few things have been changed and renamed.
diff --git a/docs/migration/from-react-three-xr-5.md b/docs/migration/from-react-three-xr-5.md
index 011184d6..43611052 100644
--- a/docs/migration/from-react-three-xr-5.md
+++ b/docs/migration/from-react-three-xr-5.md
@@ -1,7 +1,7 @@
---
title: from @react-three/xr v5
description: Migrate your application from @react-three/xr v5
-nav: 23
+nav: 24
---
The goal of @react-three/xr v6 is to align this library closer to the react-three ecosystem. We, therefore, focussed on supporting the react-three/fiber event handlers. Another focus of v6 is to reduce boilerplate and provide more defaults while also giving developers more access to the lower-level WebXR primitives. In combination, these changes allow developers to build XR experiences that interoperate with the whole react-three ecosystem using only a few lines of code.
diff --git a/docs/tutorials/HUD_example_3rd_person.gif b/docs/tutorials/HUD_example_3rd_person.gif
new file mode 100644
index 00000000..2d59b74a
Binary files /dev/null and b/docs/tutorials/HUD_example_3rd_person.gif differ
diff --git a/docs/tutorials/HUD_example_POV.png b/docs/tutorials/HUD_example_POV.png
new file mode 100644
index 00000000..bc740842
Binary files /dev/null and b/docs/tutorials/HUD_example_POV.png differ
diff --git a/docs/tutorials/anchors.md b/docs/tutorials/anchors.md
index 32b5cdd8..540d298b 100644
--- a/docs/tutorials/anchors.md
+++ b/docs/tutorials/anchors.md
@@ -1,7 +1,7 @@
---
title: Anchors
description: How to create and manage anchors in your AR experience?
-nav: 17
+nav: 18
---
Anchors allow to anchor virtual objects into the physical world in AR experiences. `react-three/xr` offers a multitude of ways to create and manage anchors. A simple solution is `useXRAnchor`, which works similarly to `useState` as it returns the current anchor and a function to request a new anchor as a tuple.
diff --git a/docs/tutorials/custom-inputs.md b/docs/tutorials/custom-inputs.md
index 989be9fc..a3d3afd2 100644
--- a/docs/tutorials/custom-inputs.md
+++ b/docs/tutorials/custom-inputs.md
@@ -1,7 +1,7 @@
---
title: Custom Hands/Controllers/...
description: Customize interactions and style of inputs such as hands, controllers, and more
-nav: 16
+nav: 17
---
@react-three/xr provides a set of default hands, controllers, transient pointers, gazes, and screen input that can be configured and completely exchanged with your own implementation. The following example shows how to configure the ray color of the ray pointer in the users hand.
diff --git a/docs/tutorials/dom-overlay.md b/docs/tutorials/dom-overlay.md
index 941964bc..5da288df 100644
--- a/docs/tutorials/dom-overlay.md
+++ b/docs/tutorials/dom-overlay.md
@@ -1,7 +1,7 @@
---
title: Dom Overlay
description: How to add HTML elements for hand-held AR experiences with Dom overlay?
-nav: 18
+nav: 19
---
For hand-held AR experiences, such as those using a Smartphone, WebXR offers the dom overlay capability, allowing developers to use HTML code overlayed over the experience. In case scene 3D overlays or overlays in non-handheld AR/VR experiences are needed, check out [pmndrs/uikit](https://github.com/pmndrs/uikit).
diff --git a/docs/tutorials/gamepad.md b/docs/tutorials/gamepad.md
index d3a14c42..5d371a27 100644
--- a/docs/tutorials/gamepad.md
+++ b/docs/tutorials/gamepad.md
@@ -1,7 +1,7 @@
---
title: Gamepad
description: How to use the XRControllers gamepad?
-nav: 13
+nav: 14
---
All XR controllers are part of the state inside the xr store. The existing controllers can be read using the `useXR` hook. Alternatively, a specific xr controller can be retrived using `useXRInputSourceState("controller", "left")`.
diff --git a/docs/tutorials/guards.md b/docs/tutorials/guards.md
index ab7c0f75..f3e5be15 100644
--- a/docs/tutorials/guards.md
+++ b/docs/tutorials/guards.md
@@ -1,7 +1,7 @@
---
title: Guards
description: Render and show parts of your application conditionally using guards
-nav: 20
+nav: 21
---
Guards allow to conditionally display or include content. For instance, the `IfInSessionMode` guard allows only displaying a background when the session is not an AR session. The `IfInSessionMode` can receive either a list of `allow` session modes or a list of `deny` session modes.
diff --git a/docs/tutorials/heads-up-display.md b/docs/tutorials/heads-up-display.md
new file mode 100644
index 00000000..f40f1fb4
--- /dev/null
+++ b/docs/tutorials/heads-up-display.md
@@ -0,0 +1,308 @@
+---
+title: Heads Up Displays
+description: How to show static information in XR using a "Heads Up Display"
+nav: 12
+---
+
+A heads up display is a fairly easy thing to implement in **React Three XR**. Because it's such a popular feature, this tutorial outlines a simple and effective method for adding a heads up display to your React Three XR applications.
+
+
+### Setup
+
+First thing to do is to create a new React Three XR project, and import [UIKit](https://pmndrs.github.io/uikit/docs/getting-started/introduction). We will use UIKit to create the elements of our heads up display.
+
+`npm i @react-three/uikit`
+
+Next, we need a basic scene to start out with. Put the following code in your `app.tsx` file:
+
+```tsx
+import { Box } from '@react-three/drei'
+import { Canvas } from '@react-three/fiber'
+import { createXRStore, XR } from '@react-three/xr'
+import * as THREE from 'three'
+import './styles.css'
+
+const store = createXRStore()
+
+const axisColor = new THREE.Color('#9d3d4a')
+const gridColor = new THREE.Color('#4f4f4f')
+
+export function App() {
+ return (
+
+
+
+
+ )
+}
+```
+
+And the following in your `styles.css` file:
+
+```css
+html {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+body {
+ margin: 0;
+}
+
+.App {
+ font-family: sans-serif;
+ text-align: center;
+ width: 100vw;
+ height: 100vh;
+}
+
+button {
+ position: absolute;
+ background: black;
+ border-radius: 0.5rem;
+ border: none;
+ font-weight: bold;
+ color: white;
+ padding: 1rem 2rem;
+ cursor: pointer;
+ font-size: 1.5rem;
+ bottom: 1rem;
+ left: 50%;
+ box-shadow: 0px 0px 20px rgba(0, 0, 0, 1);
+ transform: translate(-50%, 0);
+}
+```
+
+### Creating the Heads Up Display Component
+
+Now that we have the basic scene, it's on to the fun bit! Create a new file called `HUD.tsx`, and fill it with a new function component.
+
+```tsx
+export function HUD() {
+ return (
+ <>
+ >
+ )
+}
+```
+
+With our component created, it's time to get to work on our heads up display! For that we need to think a little bit about how the heads up display actually works. Essentially, our plan here is to take bits of UI in our scene and parent them to the xr camera so that when the user moves their head, the UI moves in sync in front of them. From the user's perspective, the UI will appear basically 2D in front of them. Like so:
+
+
+
+Though in reality, if the user could see themselves in 3rd person, it would look more like this:
+
+
+
+With our plan figured out, let's add some UI to the scene, and parent it to the camera. We'll start by importing UIKit, and placing the ``, a `` for all of our HUD elements, and a UIKit container in the return of our function component.
+
+```tsx
+import { Container } from '@react-three/uikit'
+import { XROrigin } from '@react-three/xr'
+
+export function HUD() {
+ return (
+ <>
+
+
+
+
+
+ >
+ )
+}
+```
+
+With that done, we need something to display in our heads up display. A common element in heads up displays is a clock, so that seems like the perfect thing to include in our component. Here is a simple hook that will display the current time, updating every second. Create a new file called `useGetTime.ts` and add the following code:
+
+```tsx
+import { useEffect, useState } from 'react'
+
+export const useGetTime = () => {
+ const [time, setTime] = useState(() => new Date())
+
+ useEffect(() => {
+ const id = setInterval(() => setTime(new Date()), 1000)
+ return () => clearInterval(id)
+ }, [])
+
+ return time
+}
+```
+
+Now let's use the hook in our HUD component. Import the hook, and then call it at the top of the component. Add a UIKit `` component inside our ``, and then display the returned time from our hook inside the `` component
+
+```tsx
+// Import Text
+import { Container, Text } from '@react-three/uikit'
+import { XROrigin } from '@react-three/xr'
+// Import our new hook
+import { useGetTime } from './useGetTime'
+
+export function HUD() {
+ // Get the time
+ const time = useGetTime()
+ return (
+ <>
+
+
+
+ {/*Display the time here*/}
+
+ {time.toLocaleTimeString()}
+
+
+
+ >
+ )
+}
+```
+
+We have our UI figured out, the last part of our puzzle is to attach it to the camera. To do that, we are going to need to get references to both our camera and group using a `useRef` hook, and then use a `useEffect` hook to parent our `` to the camera. We'll also do some positioning of the group to make sure that the UI shows up in the top left of the user's view and isn't blurry.
+
+> [!TIP]
+> If your UI looks blurry, it's probably too close to the camera. Try moving it further away.
+
+```tsx
+import { Container, Text } from '@react-three/uikit'
+import { XROrigin } from '@react-three/xr'
+import { useGetTime } from './useGetTime'
+// Import useEffect and useRef
+import { useEffect, useRef } from 'react'
+
+export function HUD() {
+ const time = useGetTime()
+ // Create refs to our group and XROrigin
+ const groupRef = useRef(null)
+ const cameraRef = useRef(null)
+
+ // Attach the group to the camera on mount
+ useEffect(() => {
+ if (!groupRef.current || !originRef.current?.children[0]) return
+
+ // Store references in local variables inside the useEffect in case they change
+ const contextSafeGroupRef = groupRef.current
+ // Drill down to get the actual camera
+ const contextSafeCameraRef = originRef.current?.children[0]
+
+ // Position the group in front of the camera, and to the left a bit
+ contextSafeGroupRef.position.set(-0.2, 0.2, -0.8)
+ // Reset any rotations
+ contextSafeGroupRef.quaternion.identity()
+
+ contextSafeCameraRef.add(contextSafeGroupRef)
+ return () => {
+ contextSafeCameraRef.remove(contextSafeGroupRef)
+ }
+ }, [])
+
+ return (
+ <>
+ {/* Add the refs to the components */}
+
+
+
+
+ {time.toLocaleTimeString()}
+
+
+
+ >
+ )
+}
+```
+
+We're almost done, we have just one more small issue to work out. Remember earlier when we said that the UI would appear basically 2D in front of the user, but that it's actually still just 3D objects in the scene? This becomes a problem when the user moves too close to other objects in the scene. It's possible that the heads up display elements will be occluded in that case. To fix this, we can turn off depth testing on our `` component (and any other elements that you wish to add to your HUD). Turning off depth testing tells Three.js to always render that object on top of everything else in the scene. For good measure, we can also add a `renderOrder` of 1000 to our `` to make sure that it gets rendered last.
+
+```tsx
+import { Container, Text } from '@react-three/uikit'
+import { XROrigin } from '@react-three/xr'
+import { useEffect, useRef } from 'react'
+import * as THREE from 'three'
+import { useGetTime } from './useGetTime.js'
+
+export function HUD() {
+ const time = useGetTime()
+ const groupRef = useRef(null)
+ const originRef = useRef(null)
+
+ useEffect(() => {
+ if (!groupRef.current || !originRef.current?.children[0]) return
+ const contextSafeGroupRef = groupRef.current
+ const contextSafeCameraRef = originRef.current?.children[0]
+
+ contextSafeGroupRef.position.set(-0.2, 0.2, -0.8)
+ contextSafeGroupRef.quaternion.identity()
+
+ contextSafeCameraRef.add(contextSafeGroupRef)
+ return () => {
+ contextSafeCameraRef.remove(contextSafeGroupRef)
+ }
+ }, [])
+
+ return (
+ <>
+
+ {/* Render order of 1000 */}
+
+ {/* Turn off depth testing */}
+
+ {/* Turn off depth testing */}
+
+ {time.toLocaleTimeString()}
+
+
+
+ >
+ )
+}
+```
+
+With that our heads up display is complete! Just add the `` component to the scene that we created at the beginning
+
+```tsx
+import { Box } from '@react-three/drei'
+import { Canvas } from '@react-three/fiber'
+import { createXRStore, XR } from '@react-three/xr'
+import * as THREE from 'three'
+import './styles.css'
+
+const store = createXRStore()
+
+const axisColor = new THREE.Color('#9d3d4a')
+const gridColor = new THREE.Color('#4f4f4f')
+
+export function App() {
+ return (
+
+
+
+
+ )
+}
+```
+
+Feel free to experiment with adding more elements to your heads up display. [Link to the complete example here](https://pmndrs.github.io/xr/examples/heads-up-display). Happy coding!
\ No newline at end of file
diff --git a/docs/tutorials/hit-test.md b/docs/tutorials/hit-test.md
index ff746325..488017eb 100644
--- a/docs/tutorials/hit-test.md
+++ b/docs/tutorials/hit-test.md
@@ -1,7 +1,7 @@
---
title: Hit Test
description: How to add hit testing capabilities to your AR experiences
-nav: 19
+nav: 20
---
Hit testing is a technique that allows developers to check for intersections with real-world surfaces in AR experiences. `@react-three/xr` provides hooks and components for setting up hit testing. This tutorial covers all the hit testing hooks available in React Three XR and demonstrates how to use them effectively.
diff --git a/docs/tutorials/layers.md b/docs/tutorials/layers.md
index 119b6a8a..4497fb9e 100644
--- a/docs/tutorials/layers.md
+++ b/docs/tutorials/layers.md
@@ -1,7 +1,7 @@
---
title: Layers
description: How to use display images, videos, and custom renders at high quality on quad, cylinder, and equirect shapes?
-nav: 15
+nav: 16
---
Layers allow to render videos, images, and complete scenes with higher performance and higher quality while preserving battery life and latency for quad, cylinder, and equirect shapes using the WebXR Layer API. Layers are perfect for use cases that display flat, high-quality content, such as videos, images, and user interfaces. The following example illustrates how to create a layer that renders a video.
diff --git a/docs/tutorials/secondary-input-sources.md b/docs/tutorials/secondary-input-sources.md
index d7776869..e6d6ed34 100644
--- a/docs/tutorials/secondary-input-sources.md
+++ b/docs/tutorials/secondary-input-sources.md
@@ -1,7 +1,7 @@
---
title: Secondary Input Sources
description: How to use primary and secondary input sources (multiple controllers and hands) simultaneously?
-nav: 14
+nav: 15
---
Most standalone XR headsets support hand and controller tracking. While typical XR experiences often support both input methods, they only use the primary inputs, which refers to one input per hand and limits the inputs to `2`. However, the headset often also tracks the secondary input sources. By enabling the `secondaryInputSources` flag when creating an xr store, we can access the secondary input sources and use them to track real-world objects, for example.
diff --git a/docs/tutorials/teleport.md b/docs/tutorials/teleport.md
index 52b50724..c5b43a3d 100644
--- a/docs/tutorials/teleport.md
+++ b/docs/tutorials/teleport.md
@@ -1,7 +1,7 @@
---
title: Teleportation
description: Allow users to move through the scene using teleportation
-nav: 12
+nav: 13
---
First we need to enable the teleport pointer inside the hands and controllers.
diff --git a/examples/heads-up-display/.gitignore b/examples/heads-up-display/.gitignore
new file mode 100644
index 00000000..53c37a16
--- /dev/null
+++ b/examples/heads-up-display/.gitignore
@@ -0,0 +1 @@
+dist
\ No newline at end of file
diff --git a/examples/heads-up-display/HUD.tsx b/examples/heads-up-display/HUD.tsx
new file mode 100644
index 00000000..7f52c476
--- /dev/null
+++ b/examples/heads-up-display/HUD.tsx
@@ -0,0 +1,38 @@
+import { Container, Text } from '@react-three/uikit'
+import { XROrigin } from '@react-three/xr'
+import { useEffect, useRef } from 'react'
+import * as THREE from 'three'
+import { useGetTime } from './useGetTime.js'
+
+export function HUD() {
+ const time = useGetTime()
+ const groupRef = useRef(null)
+ const originRef = useRef(null)
+
+ useEffect(() => {
+ if (!groupRef.current || !originRef.current?.children[0]) return
+ const contextSafeGroupRef = groupRef.current
+ const contextSafeCameraRef = originRef.current?.children[0]
+
+ contextSafeGroupRef.position.set(-0.2, 0.2, -0.8)
+ contextSafeGroupRef.quaternion.identity()
+
+ contextSafeCameraRef.add(contextSafeGroupRef)
+ return () => {
+ contextSafeCameraRef.remove(contextSafeGroupRef)
+ }
+ }, [])
+
+ return (
+ <>
+
+
+
+
+ {time.toLocaleTimeString()}
+
+
+
+ >
+ )
+}
diff --git a/examples/heads-up-display/app.tsx b/examples/heads-up-display/app.tsx
new file mode 100644
index 00000000..4d21b421
--- /dev/null
+++ b/examples/heads-up-display/app.tsx
@@ -0,0 +1,30 @@
+import { OrbitControls, Plane } from '@react-three/drei'
+import { Canvas } from '@react-three/fiber'
+import { createXRStore, XR } from '@react-three/xr'
+import * as THREE from 'three'
+import { HUD } from './HUD.js'
+import './styles.css'
+
+const store = createXRStore()
+
+const axisColor = new THREE.Color('#9d3d4a')
+const gridColor = new THREE.Color('#4f4f4f')
+
+export function App() {
+ return (
+