Syncing 2D HTML (DOM) with 3D

Back to Modules

Syncing 2D HTML (DOM) with 3D in Three.js

Integrating 2D HTML DOM elements with a 3D Three.js scene is a common requirement for creating rich, interactive web experiences. This allows you to leverage the full power of HTML and CSS for UI elements while still having a dynamic 3D environment. There are several approaches to achieve this, each with its own advantages and limitations.

1. Overlaying HTML Elements on Top of the WebGL Canvas

This is the simplest and most common method for displaying UI elements (like buttons, text, or forms) over your Three.js scene.

2. Using Three.js CSS Renderers (CSS2DRenderer and CSS3DRenderer)

Three.js provides specialized renderers that allow you to integrate HTML DOM elements directly into your 3D scene, making them appear as if they are part of the 3D world.

These renderers are powerful but have limitations, such as HTML elements not being occluded by WebGL objects.

3. Manual Synchronization (World to Screen Coordinates)

For more fine-grained control or when you need HTML elements to precisely follow 3D objects without using the CSS renderers, you can manually convert 3D world coordinates to 2D screen coordinates.

Example: Overlaying HTML on Canvas (Conceptual)

This is a conceptual example of overlaying a simple HTML div on top of a Three.js canvas. The HTML element's position is fixed relative to the viewport.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>HTML Overlay</title>
    <style>
        body { margin: 0; overflow: hidden; }
        #threejs-container { position: relative; width: 100vw; height: 100vh; }
        canvas { display: block; }
        #overlay-text {
            position: absolute;
            top: 20px;
            left: 20px;
            color: white;
            font-family: sans-serif;
            background: rgba(0, 0, 0, 0.5);
            padding: 10px;
            border-radius: 5px;
            z-index: 10; /* Ensure it's above the canvas */
        }
    </style>
</head>
<body>
    <div id="threejs-container">
        <div id="overlay-text">Hello from HTML Overlay!</div>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
        const container = document.getElementById('threejs-container');
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        container.appendChild(renderer.domElement);

        const geometry = new THREE.BoxGeometry(1, 1, 1);
        const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
        const cube = new THREE.Mesh(geometry, material);
        scene.add(cube);

        camera.position.z = 5;

        function animate() {
            requestAnimationFrame(animate);
            cube.rotation.x += 0.01;
            cube.rotation.y += 0.01;
            renderer.render(scene, camera);
        }
        animate();

        window.addEventListener('resize', () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        });
    </script>
</body>
</html>