Creating a WebVR Minecraft‑style Voxel Builder with A‑Frame Using Only HTML
This tutorial shows how to build a cross‑platform WebVR voxel‑building demo, similar to Minecraft, using A‑Frame and just a handful of HTML elements, covering scene setup, assets preloading, entity‑component patterns, custom components, mixins, hand controllers, teleportation, and device‑agnostic interaction.
Kevin Ngo, a Mozilla VR developer and core contributor to A‑Frame, demonstrates how to construct a WebVR version of Minecraft that runs on HTC Vive, Oculus Rift, GearVR, Cardboard, desktop and mobile devices using only eleven HTML elements.
WebVR, originally created by Mozilla, brings immersive VR experiences to the open web, allowing content to be linked via URLs and leveraging WebGL, WebAssembly and Service Workers for performance.
A‑Frame is a web framework for building VR experiences based on HTML and the Entity‑Component System (ECS) pattern. A minimal scene can be created with a script tag loading A‑Frame and an <a-scene> element.
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>To add a ground plane, an <a-cylinder> is used with a texture preloaded via <a-assets> :
<a-assets>
<img id="groundTexture" src="https://cdn.aframe.io/a-painter/images/floor.jpg">
</a-assets>A sky background is added with <a-sky> , using a half‑sphere to match the ground radius:
<a-assets>
<img id="skyTexture" src="https://cdn.aframe.io/a-painter/images/sky.jpg">
</a-assets>The ECS paradigm is illustrated by comparing a simple <a-box> with an equivalent <a-entity> that defines geometry and material components:
<a-box color="red" depth="0.5" height="0.5" width="0.5"></a-box>A custom random-color component is registered to assign a random color to an entity on initialization:
AFRAME.registerComponent('random-color', {
init: function () {
this.el.setAttribute('material', 'color', getRandomColor());
}
});
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}Mixins are used to define a reusable voxel entity that combines geometry, material, the random‑color component and a snap‑to‑grid component:
<a-mixin id="voxel"
geometry="primitive: box; height: 0.5; width: 0.5; depth: 0.5"
material="shader: standard"
random-color
snap="offset: 0.25 0.25 0.25; snap: 0.5 0.5 0.5"></a-mixin>Hand controllers are added: the left hand ( teleHand ) uses hand-controls with teleport-controls for parabolic teleportation, and the right hand ( blockHand ) uses hand-controls with controller-cursor to emit ray‑casts.
<a-entity id="teleHand" hand-controls="left" teleport-controls="type: parabolic; collisionEntities: [mixin='voxel'], #ground"></a-entity>The teleport component is configured to allow movement onto both the ground and existing voxels, using a parabolic arc for visual feedback.
Voxel creation is handled by the intersection-spawn component attached to the right hand. When a click event occurs, a new entity with the voxel mixin is created at the intersection point and snapped to the grid.
<a-entity id="blockHand" hand-controls="right" controller-cursor intersection-spawn="event: click; mixin: voxel"></a-entity>For desktop and mobile devices, the same intersection-spawn logic is reused with an <a-cursor> attached to the camera, enabling click‑based voxel placement without VR controllers.
<a-camera>
<a-cursor intersection-spawn="event: click; mixin: voxel"></a-cursor>
</a-camera>The final example combines all pieces into a single HTML file that runs on browsers supporting WebVR. Links to the full source on GitHub and live CodePen demos are provided for further exploration.
Hujiang Technology
We focus on the real-world challenges developers face, delivering authentic, practical content and a direct platform for technical networking among developers.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.