Vladimir Vukićević

I Make Fun Stuff

First Steps for VR on the Web

There has been a lot of excitement around Virtual Reality recently, with good reason. Display devices such as the Oculus Rift and input devices such as the Leap Motion, PrioVR, Sixense Stem and many others are creating a strong ecosystem where a high-quality, consumer-level VR experience can be delivered.

The opportunity for VR on the Web is particularly exciting. The Web is a vibrant, connected universe where many different types of experiences can be created and shared. People can be productive, have fun and learn all from within their browser. It is, arguably, an early version of the Metaverse — the browser is the portal through which we access it. It’s not perfect, though, and lacks many of the “virtual” and “immersive” aspects. Given that, could we not expand the Web to include the immersive elements of a fully three-dimensional virtual universe? Is it possible for the Web to evolve to become the Metaverse that Stephenson envisioned?

Today, we want to take some of the first steps in finding an answer to this question. We are adding native support for VR devices to early experimental builds of Firefox, so that Web developers can start experimenting with adding VR interactivity to their websites and content. This is only the first of many steps that well be taking over the coming weeks and months.

The initial technical vision for VR on the Web includes:

  • Rendering Canvas (WebGL or 2D) to VR output devices
  • Rendering 3D Video to VR output devices (as directly as possible)
  • Rendering HTML (DOM+CSS) content to VR output devices – taking advantage of existing CSS features such as 3D transforms
  • Mixing WebGL-rendered 3D Content with DOM rendered 3D-transformed content in a single 3D space
  • Receiving input from orientation and position sensors, with a focus on reducing latency from input/render to final presentation

In particular, Web content should not need to be aware of the particulars of the VR output device, beyond that there is one and it has certain standard rendering characteristics (e.g. a specific projection matrix that’s needed). For example, in the case of the Oculus Rift, content should not need to apply the Rift-specific distortion rendering effect.

This initial step has the seed technical functionality needed to support the first type of VR content listed above: receiving sensor input and rendering Canvas/WebGL content to VR. Over the coming weeks, I’ll be expanding the scope and support of VR in the Web platform so that all of the above will be possible. In addition, my colleague at Mozilla, Josh Carpenter, will be working at the problem from a user experience and design angle, to figure out what some best practices might be for bringing VR to Web content – and what the experience of browsing a VR Web might feel like, what creating VR Web content could be like, and what it could mean to browse the current Web using a VR interface.

If you’re interested in joining the discussion or providing feedback, please join us on the web-vr-discuss mailing list. As with all technology experimentation, I look forward to seeing what Web developers do with this functionality!


This is an early preview build of work-in-progress code. It is intended for developers looking to experiment with VR on the Web. Windows and OS X builds are available below, with Linux coming soon. Only the Oculus Rift is currently supported, though other devices will come soon (including Google’s Cardboard and similar phone harnesses!).

How to report bugs: Because this code is not yet in the upstream Mozilla repository, please report any VR-specific issues to me via GitHub issues on my gecko-dev repo. Once the code is merged into the main repository, bugs will be tracked using Bugzilla as usual. Expect bugs and crashes, which are part of the fun!

I’ve modified the three.js cubes sample to add support for VR. Press “f” to toggle fullscreen using VR. If no Oculus Rift is plugged in, a DK1 will be simulated, but there will be no orientation input.

The features proposed here are all subject to rapid change; they are not indicative of any standard, and may even change or break from release to release of any upcoming builds. This code is currently not in the main Firefox source tree, though that process has started. The goal of this release is to allow for some initial experimentation and feedback. The API supported in this release allows for:

  • Making an element fullscreen, with VR post-processing. For this release, the element is expected to contain left/right split content.
  • Querying recommended field of view settings per eye, and setting per-eye FOV
  • Querying current sensor state (orientation, position)

Not implemented yet, but next on the list and coming very soon:

  • Synchronization between requestAnimationFrame and sensor timings (i.e. the orientation value that you currently query is instantaneous, and not tied to what it is predicted to be when the frame is rendered)
  • Automatic window positioning on HMD when fullscreen is applied. (You need to move the browser window to the HMD display.)
  • Browser-supported left/right rendering of CSS content.

Technical details on usage:

The mozGetVRDevices entry point will provide a list of all VR devices to a given callback:

navigator.mozGetVRDevices(vrCallback);

The callback will accept an array of devices, which currently will be either a HMDVRDevice or a PositionSensorVRDevice. You’ll want to find a HMD device, and then find its associated sensor:

var vrHMD, vrSensor;
function vrDeviceCallback(vrdevs) {
  // First, find a HMD -- just use the first one we find
  for (var i = 0; i < vrdevs.length; ++i) {
    if (vrdevs[i] instanceof HMDVRDevice) {
      vrHMD = vrdevs[i];
      break;
    }
  }

  if (!vrHMD)
   return;

  // Then, find that HMD's position sensor
  for (var i = 0; i < vrdevs.length; ++i) {
    if (vrdevs[i] instanceof PositionSensorVRDevice &&
        vrdevs[i].hardwareUnitId == vrHMD.hardwareUnitId)
    {
      vrSensor = vrdevs[i];
      break;
    }
  }

  if (!vrHMD || !vrSensor) {
    alert("Didn't find a HMD and sensor!");
    return;
  }

  startRendering();
}

Once you have a vrHMD, you can query its configuration (initially initialized to the recommended FOV values):

leftFOV = vrHMD.getCurrentEyeFieldOfView("left");
rightFOV = vrHMD.getCurrentEyeFieldOfView("right");

leftTranslation = vrHMD.getEyeTranslation("left");
rightTranslation = vrHMD.getEyeTranslation("right");

Sample HTML might look like this:

<div id="container">
  <canvas id="content" width="1920" height="1080"></canvas>
</div>

To request full screen of the container with VR distortion applied, call:

document.addEventListener("mozfullscreenchange", fullScreenChange, false);

var container = document.getElementById("container");
container.mozRequestFullScreen({ vrDisplay: vrHMD });

If vrHMD is null or not present, this will act as a regular full screen request. Once you receive the fullscreenchange event, start doing left/right rendering:

function fullScreenChange() {
  if (document.mozFullScreenElement && vrHMD) {
    // reconfigure for two-camera left/right split rendering
    vrEnabled = true;
  } else {
    // reconfigure for non-VR single camera renering
    vrEnabled = false;
  }
}

When rendering a frame, you can query the current orientation and position from the sensor:

var state = vrSensor.getState();
if (state.orientation) {
  ... use state.orientation.xyzw as a quaternion ...
}
if (state.position) {
  ... use state.position.xyz as position ...
}

In the cubes demo, note that the entire contents of the “container” div are made full screen – it can include content other than just a WebGL canvas. While there is not yet support for the browser itself doing appropriate left/right rendering of CSS 3D transformed content, you are not prevented from experimenting with this!

Comments