Build Immersive Web VR Panoramas with Three.js: A Step‑by‑Step Guide

This article explains the fundamentals of virtual reality, the WebGL and OpenGL APIs, and demonstrates how to create interactive VR panoramas for houses and cars using three.js, covering scene setup, cameras, renderers, geometry, textures, materials, lighting, and user interaction with concise code examples.

ELab Team
ELab Team
ELab Team
Build Immersive Web VR Panoramas with Three.js: A Step‑by‑Step Guide

Web VR Panorama Implementation

This article, authored by members of the ByteDance Education Frontend team, explores the principles of virtual reality and provides practical demos of Web VR panoramas using three.js.

Preface

VR concepts have become popular, with applications such as VR house tours, car showcases, and travel experiences. This guide investigates the implementation principles of Web VR panoramas and presents two demos to help developers apply these techniques in real projects.

What Is VR?

Virtual reality (VR) is a computer‑generated three‑dimensional environment that provides users with immersive visual and sensory experiences, allowing them to feel present in a simulated world.

Unlike augmented reality (AR), which overlays digital content onto the real world, VR requires a completely reconstructed environment.

Principles of Virtual Reality

Human depth perception relies on projecting a 3D world onto a 2D retina. By presenting images with correct perspective, viewers perceive depth. VR builds on this principle, and any medium that can simulate a 3D space—phones, computers, large screens, VR headsets, or even projected air—can serve as a VR platform.

Simulating 3D Space on the Web

To render 3D graphics on the web, we use the OpenGL API, which defines an abstract set of functions for drawing 2D and 3D shapes. In browsers, the JavaScript binding of OpenGL is WebGL.

What Is OpenGL?

OpenGL (Open Graphics Library) is a cross‑language, cross‑platform API for rendering 2D and 3D vector graphics.

It is widely used in CAD, scientific visualization, and game development.

Can We Use OpenGL on the Web?

Yes, via WebGL , which provides a JavaScript API for interactive 2D/3D graphics without plugins.

What Is WebGL?

WebGL is a JavaScript API that enables GPU‑accelerated rendering of interactive 2D and 3D graphics within any compatible web browser.

WebGL integrates with the HTML5 canvas element, allowing WebGL content to mix with other page elements.

// Create a canvas element
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);

// Get a WebGL rendering context
const gl = canvas.getContext('webgl');
// Use the WebGL API to draw graphics
// ...

Popular WebGL 3D engines such as three.js and babylon.js provide high‑level abstractions that simplify development.

Three.js Implementation of VR Panoramas

Basic Concepts

In three.js, rendering a 3D scene requires three core components:

Scene : a container for all objects except the renderer.

Camera : defines the viewpoint and projection.

Renderer : draws the scene onto a canvas.

Scene

A container that holds everything in the 3D world except the renderer. It uses a right‑handed coordinate system (X right, Y up, Z out of the screen).
Scene diagram
Scene diagram
// Create a three.js scene
const scene = new THREE.Scene();

Camera

Like a human eye, a camera can look in any direction and its parameters control field of view and depth.

three.js provides PerspectiveCamera and OrthographicCamera. Perspective projection mimics human vision, while orthographic projection keeps object size constant regardless of distance.

Camera types
Camera types

PerspectiveCamera (fov, aspect, near, far)

OrthographicCamera (left, right, top, bottom, near, far)

PerspectiveCamera Parameters

fov – field of view angle.

aspect – aspect ratio of the viewport.

near – nearest visible distance.

far – farthest visible distance.

Perspective parameters
Perspective parameters

OrthographicCamera Parameters

left, right, top, bottom – distances to the respective clipping planes.

near – start of the rendering range (often negative).

far – end of the rendering range (often positive).

Orthographic parameters
Orthographic parameters

Renderer

Renders the view from the camera onto the canvas.
const renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
const canvas = renderer.domElement;
document.body.appendChild(canvas);
renderer.render(scene, camera);

Geometry

All objects are composed of points forming faces, which together form geometry.

Common built‑in geometries include:

SphereGeometry
CylinderGeometry
BoxGeometry
ConeGeometry
TorusGeometry
TorusKnotGeometry
Geometry examples
Geometry examples

Texture

Applying an image to a geometry is like wrapping a piece of paper around a shape and drawing on it.

Example of applying a duck texture to a cube:

// Box geometry
const boxGeometry = new THREE.BoxGeometry(2, 2, 2);
// Load texture
const texture = new THREE.TextureLoader().load('textures/duck.png');
const material = new THREE.MeshBasicMaterial({ map: texture });
const cube = new THREE.Mesh(boxGeometry, material);
scene.add(cube);
Cube with duck texture
Cube with duck texture

Video textures can be applied similarly.

const video = document.getElementById('video');
const texture = new THREE.VideoTexture(video);
const material = new THREE.MeshBasicMaterial({ map: texture });
const cube = new THREE.Mesh(boxGeometry, material);
scene.add(cube);
Cube with video texture
Cube with video texture

Material

Materials define how surfaces interact with light, similar to using different types of paper for drawing.

Common materials used in the demos: MeshBasicMaterial – no lighting. MeshMatcapMaterial – simulates lighting without actual light sources. MeshPhongMaterial – shiny, suitable for ceramic or lacquer. MeshToonMaterial – cartoon style. MeshLambertMaterial – non‑shiny, good for wood or stone.

Lighting

Without explicit lights, a default ambient light is added.

Typical lights: AmbientLight – uniform illumination, no shadows. DirectionalLight – parallel light, simulating sunlight. PointLight – emits from a point in all directions, like a bulb. SpotLight – cone‑shaped light from a point.

Directional light example
Directional light example
Point light example
Point light example
Spot light example
Spot light example

VR House Panorama Implementation

Principle

Use a camera as the viewer’s eye.

Map six textures onto the faces of a cube to represent the interior of a house.

Place the camera inside the cube and move it to simulate walking.

Assets

House textures
House textures

Code Steps

Create scene, camera, and orbit controls.

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(1, 1, 4);
const controls = new THREE.OrbitControls(camera, renderer.domElement);

Load six textures and apply them to a cube.

const textures = getTexturesFromAtlasFile('textures/house.jpeg', 6);
const materials = [];
for (let i = 0; i < 6; i++) {
    materials.push(new THREE.MeshBasicMaterial({ map: textures[i] }));
}
const skyBox = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), materials);
scene.add(skyBox);
function getTexturesFromAtlasFile(atlasImgUrl, tilesNum) {
    const textures = [];
    for (let i = 0; i < tilesNum; i++) {
        textures[i] = new THREE.Texture();
    }
    new THREE.ImageLoader().load(atlasImgUrl, (image) => {
        const tileWidth = image.height;
        for (let i = 0; i < textures.length; i++) {
            const canvas = document.createElement('canvas');
            const context = canvas.getContext('2d');
            canvas.width = canvas.height = tileWidth;
            context.drawImage(image, tileWidth * i, 0, tileWidth, tileWidth, 0, 0, tileWidth, tileWidth);
            textures[i].image = canvas;
            textures[i].needsUpdate = true;
        }
    });
    return textures;
}

Flip the cube geometry so textures face inward.

skyBox.geometry.scale(1, 1, -1);

Move the camera inside the cube.

camera.position.set(0, 0, -0.01);

Listen for keyboard events to move the camera.

window.addEventListener('keydown', onKeydown);
function onKeydown(e) {
    switch (e.keyCode) {
        case 37: // left
            if (camera.position.x > -0.5) camera.position.x -= 0.01;
            break;
        case 38: // up
            if (camera.position.z > -0.5) camera.position.z -= 0.01;
            break;
        case 39: // right
            if (camera.position.x < 0.5) camera.position.x += 0.01;
            break;
        case 40: // down
            if (camera.position.z < 0.5) camera.position.z += 0.01;
            break;
    }
}
House panorama demo
House panorama demo

VR Car Panorama Implementation

Principle

The challenge is to load a 3D car model, apply materials, and enable interactive color changes and wheel rotation.

Assets

Car model preview
Car model preview

Code Steps

Create scene, camera, and orbit controls.

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(4.25, 1.4, -4.5);
const controls = new THREE.OrbitControls(camera, container);

Load the GLTF car model and assign custom materials.

const bodyMaterial = new THREE.MeshPhysicalMaterial({ color: 0xff0000, metalness: 1.0, roughness: 0.5, clearcoat: 1.0, clearcoatRoughness: 0.03, sheen: 0.5 });
const detailsMaterial = new THREE.MeshStandardMaterial({ color: 0xffffff, metalness: 1.0, roughness: 0.5 });
const glassMaterial = new THREE.MeshPhysicalMaterial({ color: 0xffffff, metalness: 0.25, roughness: 0, transmission: 1.0 });
const dracoLoader = new THREE.DRACOLoader();
dracoLoader.setDecoderPath('draco/');
const loader = new THREE.GLTFLoader();
loader.setDRACOLoader(dracoLoader);
const wheels = [];
loader.load('glb/car.glb', function (gltf) {
    const carModel = gltf.scene.children[0];
    carModel.getObjectByName('body').material = bodyMaterial;
    carModel.getObjectByName('rim_fl').material = detailsMaterial;
    carModel.getObjectByName('rim_fr').material = detailsMaterial;
    carModel.getObjectByName('rim_rr').material = detailsMaterial;
    carModel.getObjectByName('rim_rl').material = detailsMaterial;
    carModel.getObjectByName('trim').material = detailsMaterial;
    carModel.getObjectByName('glass').material = glassMaterial;
    wheels.push(carModel.getObjectByName('wheel_fl'), carModel.getObjectByName('wheel_fr'), carModel.getObjectByName('wheel_rl'), carModel.getObjectByName('wheel_rr'));
    // Add shadow plane
    const mesh = new THREE.Mesh(new THREE.PlaneGeometry(0.655 * 4, 1.3 * 4), new THREE.MeshBasicMaterial({ map: shadow, blending: THREE.MultiplyBlending, toneMapped: false, transparent: true }));
    mesh.rotation.x = -Math.PI / 2;
    mesh.renderOrder = 2;
    carModel.add(mesh);
    scene.add(carModel);
});

Animate wheel rotation.

function render() {
    controls.update();
    const time = -performance.now() / 1000;
    for (let i = 0; i < wheels.length; i++) {
        wheels[i].rotation.x = time * Math.PI * 2;
    }
    grid.position.z = -(time) % 1;
    renderer.render(scene, camera);
    stats.update();
}

Provide color pickers to change body, details, and glass colors.

// HTML color pickers (simplified)
<div id="info">
    <span>Body <input id="body-color" type="color" value="#ff0000"></span>
    <span>Details <input id="details-color" type="color" value="#ffffff"></span>
    <span>Glass <input id="glass-color" type="color" value="#ffffff"></span>
</div>
// JavaScript listeners
const bodyColorInput = document.getElementById('body-color');
bodyColorInput.addEventListener('input', function () { bodyMaterial.color.set(this.value); });
const detailsColorInput = document.getElementById('details-color');
detailsColorInput.addEventListener('input', function () { detailsMaterial.color.set(this.value); });
const glassColorInput = document.getElementById('glass-color');
glassColorInput.addEventListener('input', function () { glassMaterial.color.set(this.value); });
Car panorama demo
Car panorama demo

Conclusion

This brief exploration demonstrates how to create Web VR panoramas with three.js. The same techniques can be extended to many other immersive 3D scenarios.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

frontend developmentThree.jsWebGLWebVR3d-graphicsVR Panorama
ELab Team
Written by

ELab Team

Sharing fresh technical insights

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.