Creating an APK from a WebXR app


Since recently it is possible to package progressive web apps into an APK using the tools provided by Oculus. This is a great way to get your app into the hands of users who are not able to use the web. And, it is just as easy as it sounds.

This tutorial is not a detailed walkthrough on how to create a WebXR app or how to create a PWA. It’s a quick overview of the steps you need to take to get your app packaged into an APK and deploy it to the Oculus Quest.

WebXR app

Let’s start with a simple example. I created a pretty empty A-Frame example that just shows a cube on a black background. To make everything work offline and to prevent cross-origin issues, I downloaded the A-Frame library and added it to the project.

Web manifest

The first step in converting the example is to add a webmanifest. Most of the file is pretty much the same for all Progressive Web Apps, with a few exceptions. The biggest and maybe the most important addition is "ovr_package_name":"". This is the name of the package that will be used to install the app on the Oculus Quest. Another thing to mention is the display property. Right now there are two valid values, standalone and minimal-ui. I noticed no difference on the Quest. I will leave it as standalone for now. I hope that soon Oculus figures out a way of opening the app in full immersive mode, maybe by using fullscreen or something. For now, you have to show the UI to enter VR. In the example below I use a reference of my app running at localhost. If you release the app on a server or use a different address on your machine, you need to change this line. Lastly, there has to be at least one image in the icons array, a 512x512 image. This image has to have its purpose set to any maskable. More sizes are recommended.

Service Worker

For this example, I used a service worker from the service workers cookbook at This service worker has a precache that loads the specified files as soon as possible and then updates the files in the background if possible. I’m not going into the details of how this works, because it is fully described at The only downside of this service worker is that an update is always 1 behind. I would advise you to have a look at the cookbook and find a way that best suits your needs.

To see if it works you can look it up in the Chrome Dev Tools, under the Application tab. You should see the service worker in the list of installed scripts.

updating the app

To finalize the implementation of the progressive web app, we need to add a few lines to the index.html example. In the head section, I added a link tag referring to the web manifest. I also added a small piece of script to load the service worker.

Building the APK

Now that we have a complete PWA, we need to package it into an APK. This is done using the CLI tool provided by Oculus, you download it here. I believe this also needs Java 1.7 or later to be installed. The Android SDK or at least the Android SDK Build tools is also needed. I installed the tool on my C: drive in tools/ovr-platform-util-pwa. If you run the following from the command line it will create an APK file from your PWA. /tools/ovr-platform-util-pwa/ovr-platform-util.exe create-pwa -o TheCube.apk --android-sdk %localappdata%/Android/Sdk --manifest-content-file manifest.webmanifest The paths used in the command above are relative to the location of the tool, and probably will be completely different on your machine.

Deploy using adb

The fastest and easiest way to get your freshly built APK on your Oculus Quest is to use the Android debug CLI tool, adb. There’s only a short line needed to deploy the APK to the Quest. adb install TheCube.apk

npm scripts

To speed things up, I added a few npm scripts to the package.json file. You don’t really have to use npm or any packages in your project, but just having these scripts around saves a lot of time. I added the following to the scripts section of the package.json file:

    "build": "/tools/ovr-platform-util-pwa/ovr-platform-util.exe create-pwa -o TheCube.apk --android-sdk %localappdata%/Android/Sdk --manifest-content-file manifest.webmanifest",
    "deploy":"/tools/scrcpy/adb.exe install TheCube.apk",

Now I can just run npm run build and npm run deploy to build the APK and deploy the app to the Quest when it is connected.

Keep in mind that the folders might be different on your machine.

Closing words

At the moment of writing this, it is not possible to release the app in the Oculus store yet. It is possible to sign the APK with a keystore, but it is not possible to do anything more with that than what I’ve shown above. When Oculus starts allowing PWA/APK apps to be uploaded to the store by the general public, I will write part 2 of this tutorial showing what steps to take there.

From KenShape To WebXR

There are a lot of ways of creating content for your WebXR apps. Today I want to introduce you to a very simple one and one of my favorites: KenShape. KenShape is a tool that at first looks like a pixel-art sprite editor. And the first step is pretty similar. But, by providing a depth value you can create a 3D solid object from it. Other than with a normal pixel-editor, you can use shapes other than squares to draw your models.

Before we go into details on the editor, I need to clarify one thing. KenShape is not a free tool, but it costs only $3.99 and in my opinion well worth the money.

Creating a model

When you start the tool you are presented with 3 options for sizes of your model. 16x16, 24x24, or 32x32 pixels. For this example, I create a 16x16 model. Keep in mind that the depth is 8 pixels or voxels for every size.

At this point, we are presented with a blank canvas on which we can start drawing our model. On the top, we see the drawing tools: a pen, for drawing pixels; a tool for drawing straight lines; and a tool to fill an entire area. The three options next to that let us mirror our drawing horizontally, vertically, or disable the mirroring.

On the left side of the canvas are the different shapes you can use to draw your model. By pressing the spacebar or by using the scroll wheel on your mouse you can rotate the shape.

On the right side, there are 16 colors you can use. Through the palette icon in the top right corner, you can load other palettes. I often use lospec to find palettes to use.

I decided to draw a little space gun model for this tutorial. I loaded a custom palette. As you can see there are a lot of different shapes in here, not only the squared pixels you would get in a normal pixel-art drawing.

Once we have drawn a model we can start adding depth to it. By adding the numbers 1 to 8 to our image we can extrude based on those values. The numbers mean we extrude each pixel, giving it depth. Keep in mind that when creating your own models, that they are always mirrored and that you can add details to the front/back or top/bottom unless you open the final model in another editor.

Now we’re done with modeling our gun we can review de model. I like it. So, next, let’s export it.

For use in a WebXR project (in this case, I use A-Frame) I export to GLTF. I leave the rest of the options as is. If you want to edit the model further in another program, you might want to create a texture and export it to FBX or something your tool of choice can import. By hitting export... you save your model as a GLTF. Time to use the model.

Using the model

Now that we have successfully created a model. It would be fun to use it in a simple A-Frame scene.

There’s one thing to keep in mind when working with these models. The scale is in meters, where every voxel is 1m by 1m by 1m. So we need to scale everything down significantly.

In the code example below I loaded the model using assets. This way it’s easy to reuse the same model multiple times and, in case of a change, you only have to change it in one location. I called the asset gun-model.

Then I added pretty much the same code twice. One for the left controller and one for the right controller. Since I wanted to show the same gun in both hands the code is equal. To get the position and the scale right it’s just trial and error. I made heavy use of the A-Frame inspector (of which I created a video a while back 😉).

closing words

I hope this tutorial will help you create your own content for your WebXR games. If you have any questions or ideas, hit my up at the WebXR Discord.

WebXR Discord

During the Covid-19 crisis all over the world, we noticed a lot of people from all over the world joining our meetups and members of our community hanging out in other meetups. We decided to try and bring everyone together in one Discord server. Here we can share links to our events and recordings, but also help each other out with issues when developing with A-frame, Babylon.js, or Unity and running your apps on devices like the Oculus Quest or Microsoft Hololens. Now we need you to join and help grow this community to become the official WebXR community.

Make sure to share the invite with your friends and colleagues: WebXR Discord,!