# Virtual reality (360°)

Tip: open your browser's developer console and use the `player` and `THEOplayer` variables to interact with this demo.

| `vr.state`           | - |
| -------------------- | - |
| `vr.stereo`          | - |
| `vr.direction.yaw`   | - |
| `vr.direction.pitch` | - |
| `vr.direction.roll`  | - |
| `vr.verticalFOV`     | - |
| `vr.canPresentVR`    | - |

This example plays a 360° video. To mark a source as a 360° video, set `vr: { 360: true }` on the source description. Drag the video (or move your phone) to look around.

The box below the player reports the values of the [VR API](https://docs-preview.optiview.dolby.com/pr-894/theoplayer/v11/api-reference/web/interfaces/VR). They are updated using the `directionchange`, `stereochange` and `statechange` events of `player.vr`. Stereo mode is toggled with the button in the player's control bar, which only appears when `player.vr.canPresentVR` is true (for example, when a WebXR device such as a VR headset is available). Entering an immersive WebXR session must be triggered by a user gesture inside the player's page.

Code

demo.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Virtual reality (360°)</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet" />
    <style>
      html,
      body {
        margin: 0;
        padding: 0;
      }

      theoplayer-default-ui:not(:defined) {
        display: inline-block;
        box-sizing: border-box;
      }

      theoplayer-default-ui {
        width: 100%;
        aspect-ratio: 16 / 9;
        font-family: 'Noto Sans', sans-serif;
        background: #000;
      }
    </style>
    <script src="https://cdn.theoplayer.com/dash/theoplayer/THEOplayer.js"></script>
    <script nomodule src="https://unpkg.com/@theoplayer/web-ui@2/polyfills"></script>
    <script async src="https://unpkg.com/@theoplayer/web-ui@2"></script>
  </head>
  <body>
    <!--
      libraryLocation: For demonstration purposes, we use the theoplayer.com CDN.
      For production use, we recommend hosting THEOplayer yourself
      and changing this (as well as the <script> tags above)
      to point to THEOplayer's location on your own website.

      licenseUrl: Change this to point to your THEOplayer license file.
      Alternatively, replace it with a "license" property whose value is your THEOplayer license itself.
    -->
    <theoplayer-default-ui
      configuration='{"libraryLocation":"https://cdn.theoplayer.com/dash/theoplayer/","licenseUrl":"../../../../theoplayer-license.txt","vr":{"useWebXR":true}}'
      source='{"sources":{"src":"https://demo.theoplayer.com/hubfs/videos/natgeo/playlist.m3u8","type":"application/x-mpegurl"},"poster":"https://demo.theoplayer.com/hubfs/videos/natgeo/poster.jpg","vr":{"360":true},"metadata":{"title":"National Geographic 360\u00b0"}}'
    >
      <theoplayer-button
        id="stereo-toggle"
        slot="bottom-control-bar"
        aria-label="Toggle stereo (VR headset)"
        title="Toggle stereo (VR headset)"
        hidden
        >Stereo</theoplayer-button
      >
    </theoplayer-default-ui>
    <script src="../utils.js"></script>
    <script>
      onPlayerReady((player) => {
        const vr = player.vr;
        let pending = false;

        function reportState() {
          // Report at most once per animation frame, since the direction changes very often.
          if (pending) return;
          pending = true;
          requestAnimationFrame(() => {
            pending = false;
            sendToParent('vr', {
              state: vr.state,
              canPresentVR: vr.canPresentVR,
              stereo: vr.stereo,
              verticalFOV: vr.verticalFOV,
              direction: { ...vr.direction },
            });
          });
        }
        vr.addEventListener(['directionchange', 'stereochange', 'statechange'], reportState);

        const stereoButton = document.querySelector('#stereo-toggle');
        function updateStereoButton() {
          const canPresentVR = vr.canPresentVR;
          stereoButton.hidden = !canPresentVR;
          stereoButton.disabled = !canPresentVR;
          stereoButton.setAttribute('aria-pressed', String(vr.stereo));
          stereoButton.textContent = vr.stereo ? 'Stereo on' : 'Stereo';
        }
        stereoButton.addEventListener('click', () => {
          vr.stereo = !vr.stereo;
        });
        vr.addEventListener(['statechange', 'stereochange'], updateStereoButton);
        updateStereoButton();

        reportState();
      });
    </script>
  </body>
</html>
```
