Some Tips for the Web VR Beginners

Wood Neck
Wood Neck
Feb 19, 2020 · 7 min read
Photo by Scott Webb on Unsplash

While there are lots of frameworks like Three.js, Babylon.js, or A-Frame which all already support a great basis for the WebVR / WebXR features, sometimes you might need to use WebVR / WebXR API yourself.

I was the same while working on @egjs/view360. I managed to add some basic VR features, but there were some more things I have to consider.

In this article, I won’t focus on the actual implementation of WebVR / WebXR API but will focus on that “more things” to be covered. Also, as I haven’t done AR or VR controller related features yet, this article will focus only on the VR.

There’re some demos and source code of @egjs/view360 open on the Github, check if you’re interested.

Current status of the WebVR / WebXR API

First, you might want to ask: why there’re both WebVR & WebXR API exists? Which do I have to choose?

WebXR Device API is a more recent and refined API, emerged from WebVR API. While the older WebVR API was designed solely to support Virtual Reality (VR), WebXR provides support for both VR and Augmented Reality (AR) on the web, as they say on this MDN document.

WebXR API is “Working Draft”, WebVR API is marked as deprecated. By so, does WebVR have to be covered? Well, it depends on how many devices & browsers you want to cover. Consider those cases.

Photo by WikimediaImages from Pixabay

Google Cardboards

By its price and accessibility, the most common VR display might be Google Cardboard. By its nature, that it doesn’t have any electric device attached to it but just cardboard with lenses, Google Cardboard doesn’t care whether the web uses WebXR or WebVR API. It just needs a scene to be rendered in stereo and distorted to match its lenses. You don’t even have to use VR APIs at all if you’re targeting just the cardboards. You just simply render the same scene twice, half by half with some offset and displacement values you can grab from here. You can even customize the result with decent techniques like vertex displacement like the below. But be sure that if you do so, other displays except the cardboard will show the wrong result.

With polyfill (left), without and customized with vertex displacement (right)

Other VR displays

For the case of the other VR displays, like Samsung Gear VR and HTC Vive, they normally have the browsers in it, and that browser does matter for choosing WebVR / WebXR API. You can check the browsers that support WebVR API at

What about WebXR? According to MDN, Android Chrome after version 79 is the only browser that supports WebXR API at the moment. But I’ve found out that Oculus browser 7.0 and later also support WebXR by default (Link).

That means, when you choose to use only WebXR API, you can cover all the Android devices with Chrome, Samsung Gear VR, and Google Daydream. And with polyfill, iOS with Google Cardboard can be handled also.

If you want more browsers to be covered, like Firefox Reality, you can consider using the WebVR API. But be sure that many browsers may remove support for the WebVR API soon.

For a hint, three.js removed WebVR support at r112 and using only WebXR API from that point, while A-Frame still supports WebVR (it also has a dependency on webvr-polyfill).

Browser compatibility

If you’re supporting only the Chrome browser, things might be better. But if not, you have to consider a few things more.

One is device motion calibration. You can see that if you check A-Frame’s Hello WebVR demo in Android Samsung Browser, everything is on your backside after you enter VR. Also, Chrome’s native WebXR makes sure that you always see the front, which is -z direction of your scene whenever you enter VR, but that isn’t guaranteed in any other browsers.

So, if you don’t want browsers to do their own thing but if you want some consistent behavior, you have to calibrate the matrix value you got from WebVR / WebXR API. As this problem is dedicated to the yaw, which is y-axis rotation, you can add some offsets to it to settle this issue. Basically, grab the first matrix value from the API, and make sure that the front. If not, add some yaw values to make it look front. See below gist for implementation.

Full source at: Link

Don’t be confused by how it first uses (0, 0, 1) as camera direction and compares it to (0, 0, -1). The first vector (0, 0, 1) is in camera space and (0, 0, -1) is in object space.

iOS compatibility

Okay, here’s another hard part. Unfortunately, iOS can’t show VR content properly, even in the Chrome browser. The main issue is that they don’t provide native Fullscreen API and they’ve disabled permission for a device motion after iOS 12.2.

For Fullscreen API, the polyfill, either webvr or webxr, provides a good remedy for it by making canvas size fit viewport size. But, the address bar still appears and the user must scroll to get rid of it. And as iOS also lacks an orientation locking method, it really is almost impossible to show VR content on the web smoothly and properly at the moment.

For device motion permission, it was totally user-dependent until version 13. So the developers should show some modals like below.

Users had to enable motion & orientation by themselves until iOS13

With the arrival of iOS 13, permission querying is now possible. It still needs to be triggered by user action like click, but least you can grant permission programmatically. You can do such as below.


You might have to consider polyfills as iOS and Android except for Chrome lacks support for the VR. There’re some things you have to consider when using polyfills.

WebVR vs WebXR

Suppose you’re supporting both APIs, you might wonder which polyfill you have to use. WebXR API is definitely a more recent one, but webvr-polyfill has an advantage that it has a smaller file size.

I think webxr-polyfill is better, even if you ain’t use the AR feature. When it comes to controllers, WebXR has better XRInputSource API while WebVR has to use the Gamepad API. Needless to say that webxr-polyfill is more actively maintained.

Viewer parameters

Normally, viewers like HTC Vive or Samsung Gear VR have their own properties inside. So basically we don’t have to consider that anyway after we submit our WebGL frame rendered.

But in the case of Google Cardboard, you have to consider that. There’re really lots of vendors that provide their own cardboards with different properties like FOV, distortion coefficient. To support those custom properties, normally there’s QR code containing property values at the bottom of the cardboard.

If you open Android Chrome version 79+ and enter any WebXR enabled page, you can notice that it has a nice native selector and QR code reader for choosing cardboard display parameters.

If you click the cog at the top and press back button, you can access VR settings page
And at the “Headset” section, you can scan the QR code at the bottom of the cardboard to read viewer specific properties

In the case of the polyfill, it supports 2 default viewer parameters with the selector, which is Google Cardboard(I/O 2014) and Google Cardboard(I/O 2015). Almost every third-party cardboards seem to follow properties from those two viewers, but if you want more you can use their “ADDITIONAL_VIEWERS” and “DEFAULT_VIEWER” option.

Unfortunately, I couldn’t find any library that translates those QR codes into viewer parameters. You might want to check this gist or Protocol Buffers of device params that I found about how to decode that QR code. And also this cool page containing lots of viewers with their own QR codes.


There ain’t lots of VR stuffs out there on the web, maybe it’s because VR’s still in the developing stage for the web. But you know, it’s an era that anyone can see VR content with just 5 bucks or so. So grab your keyboard, and let’s make the web more immersive.

You can check our Github repo, or demos if you want to see what I’ve written above. Thanks.


Here’re some references you might be interested in.

NAVER FE Platform

NAVER Front End Story