In most browsers, tucked away behind a feature flag, is an exciting new API for creating VR experiences on the web. However, creating content for this new medium could potentially see you at the bottom of a steep learning curve — but does it have to?
There are a couple of options available for creating VR content on the web. The first requires a working knowledge of both the WebVR and WebGL APIs. The WebVR API is used to communicate with the VR hardware, querying for device capabilities, the users pose and exposing information such as projection matrices, which are required to render a frame to a WebGL context. Once you've probed the hardware, you'll need to handle all the actual scene rendering yourself through the WebGL API.
The issue I have with these options is the need to author completely separate content to create a VR experience. Shouldn't we be supporting VR in a progressive manner, enhancing our content to render on these devices using existing technology and techniques?
CSS and VR
CSS seems like a natural fit for VR. It provides much of what we need to create an interactive 3D experience. Content can be positioned in 3D space using transforms, media queries allow styles to be conditionally applied based on device configuration and pseudo-classes provide the means to style elements based on user interaction.
Unfortunately, it's not possible to take a DOM tree and render it directly to a VR headset using any available APIs. The WebVR API requires a WebGL context and, as I mentioned earlier, the developer is responsible for drawing to it using the WebGL API.
I'd like to see browsers handling the VR hardware communication and rendering the DOM internally, reducing complexity and allowing developers to create virtual experiences for their content using
@media rules and transforms — something along the lines of:
We've already established that CSS can render a 3D scene using transforms and respond to user interactions with psuedo-classes. The things CSS can't handle can either be implemented using existing APIs, or they can be emulated. For example, head tracking can be achieved by listening for the
deviceorientation event and stereo projection can be simulated by syncing two DOM trees with a
MutationObserver — it's even possible to implement a camera with the right DOM structure and some CSS.
Here's a full list of things that need to be implemented to create a simple VR experience:
cssvrmedia type for VR specific styles and apply them when needed.
- A camera so the user can look around in the virtual space.
- Head-tracking to sync the camera to the users physical pose.
:hoverto work in 3D space so the user can interact with content by looking at it.
clickevents when the user hovers over an element for a predetermined time.
- A stereoscopic viewport so users can experience the VR prototype in 3D.
I've published the CSSVR project source to GitHub. If you're interested, you can find detailed information about the implementations listed above.
Before we go any further, I want to emphasise that this is a proof of concept, and a relatively crude one at that! It's only compatible with VR headsets that hold a mobile device — like Google Cardboard — and lacks some of the features expected from a full VR experience, such as lens distortion. Also, some of the implementations in this prototype are only just good enough to test if the concept works.
Building a VR ready page is really simple; define some VR-specific styles and drop in the CSSVR simulator script:
If this page were loaded in a browser, any styles declared in the
cssvr media block would be ignored until the simulator is started:
Note: I recommend the use of something like Rich Tibbett's no-sleep to prevent the screen from sleeping while the device is docked in a headset.
Once the simulator is up and running, the browser will ignore any styles that target
screen media and only apply those in a
cssvr blocks. You can read more about the
start method and configuring the simulator in the project readme. When the simulator is stopped, the page will return to it's original state:
That's the basics covered. Next step is to populate the page with content and style the VR version of the content using a
@media block. Please see the following demo for a bare-bones example:
Building a VR experience is pretty pointless if users can't interact with the content you've created; once a VR headset is strapped on, traditional input methods (mice and fingers) are all but useless.
CSSVR attempts to implement user interaction by allowing users to "look at" content. The simulator calculates the vertex data for each element and then casts a ray from the centre of the viewport directly into the scene. Any elements that intersect with the ray are marked as a potential selection. If multiple elements are in the line of sight, the element closest to the user is used as the final selection. To assist with selecting elements, a cursor is rendered over the viewport.
CSSVR emulates the CSS
:hover pseudo-class so — just as you would when the mouse pointer is hovering over an element — you can style content differently when the user is looking at it:
It's also possible to trigger
click events by looking at elements. If the user looks at the same element for long enough, the simulator will indicate that an element is "clickable" by incrementally colouring the cursor segments. If the user remains on the element until all segments are coloured, a
click event is dispatched. For the CSSVR UI to work, the
vr-action attribute must be added to any elements that have
click event handlers. This is because it's impossible to determine if an element has any active event listeners.
You can see both these examples in action in the following demo:
If you'd like to try creating your own CSS based VR experiences, you can grab the code for this project from GitHub.