Use HTML Video Stream as Three.js Texture

Rendering live video onto 3D geometry is a powerful way to create immersive, interactive WebGL experiences. This article provides a direct, step-by-step guide on how to capture an active HTML <video> element—whether from a local file, stream, or webcam—and apply it as a real-time animated texture onto a 3D object using the Three.js VideoTexture class.

1. Setup the HTML Video Element

To use a video as a texture, you first need an HTML <video> element. You can define this in your HTML file. It is often hidden using CSS so that only the 3D canvas is visible to the user.

<video id="my-video" src="path/to/video.mp4" autoplay loop muted playsinline style="display:none;"></video>

Note: The muted, autoplay, and playsinline attributes are crucial. Modern browsers block autoplaying videos that have audio enabled or are not triggered by a user interaction.

2. Initialize the Video Element in JavaScript

Select the video element in your JavaScript code and ensure it is playing.

const video = document.getElementById('my-video');
video.play().catch(error => {
    console.log("Autoplay was prevented. A user interaction is required to play the video:", error);
});

3. Create the Three.js VideoTexture

Three.js provides a built-in VideoTexture class. This class automatically uploads the current frame of the video to the GPU on every render tick, eliminating the need to manually update the texture.

import * as THREE from 'three';

// Create the video texture
const videoTexture = new THREE.VideoTexture(video);

// Set appropriate filters for texture scaling
videoTexture.minFilter = THREE.LinearFilter;
videoTexture.magFilter = THREE.LinearFilter;
videoTexture.colorSpace = THREE.SRGBColorSpace; // Ensures correct color reproduction

4. Apply the Texture to a 3D Object

Now, assign the VideoTexture to the map property of a material, and apply that material to a mesh.

// Create a 3D geometry (e.g., a flat screen)
const geometry = new THREE.PlaneGeometry(16, 9);

// Create a material and map the video texture to it
const material = new THREE.MeshBasicMaterial({ map: videoTexture, side: THREE.DoubleSide });

// Combine geometry and material into a mesh
const screenMesh = new THREE.Mesh(geometry, material);

// Add the mesh to your Three.js scene
scene.add(screenMesh);

5. Update the Animation Loop

Because THREE.VideoTexture internally handles the texture updates, your standard Three.js render loop does not require any special video handling code. Just ensure your video element is playing and the scene is rendering.

function animate() {
    requestAnimationFrame(animate);

    // Optional: Rotate the mesh to show it is in 3D space
    screenMesh.rotation.y += 0.01;

    renderer.render(scene, camera);
}

animate();

Using a Webcam or MediaStream instead of a File

If you want to apply a live webcam feed instead of a pre-recorded file, replace the video element’s source with a MediaStream using the browser’s getUserMedia API:

if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
    const constraints = { video: { width: 1280, height: 720, facingMode: 'user' } };
    
    navigator.mediaDevices.getUserMedia(constraints)
        .then((stream) => {
            video.srcObject = stream;
            video.play();
        })
        .catch((error) => {
            console.error('Unable to access the camera/webcam.', error);
        });
}