3dpolyx
Tutorials

3D on the web with Three.js: a cosy corner in one HTML file

Put real 3D models on a web page with a few lines of JavaScript. A beginner-friendly Three.js tutorial with full code.

4 min read
3D on the web with Three.js: a cosy corner in one HTML file
On this page

A spinning 3D model on a landing page grabs attention in a way a flat image cannot. The good news: GLB is the native format of the web 3D world, so showing our models in a browser takes surprisingly little code.

In this tutorial we will build a small interactive scene with Three.js: a reading corner with the Wooden Armchair, Ceramic Table Lamp, Oak Bedside Table and Fiddle Leaf Fig in Basket.

What you need

  • The GLB files of the models, placed in a models folder next to your HTML file.

  • A local web server. Browsers block loading files directly from disk, so run something like npx serve or the Live Server extension in VS Code.

  • No build tools: we will load Three.js from a CDN using an import map.

The full code

Create index.html with this content:

<!doctype html>
<html>
  <head>
    <style>body { margin: 0; background: #f3efe8; }</style>
    <script type="importmap">
      { "imports": {
        "three": "https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.module.js",
        "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.170.0/examples/jsm/"
      } }
    </script>
  </head>
  <body>
    <script type="module">
      import * as THREE from 'three';
      import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
      import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
      import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';

      const renderer = new THREE.WebGLRenderer({ antialias: true });
      renderer.setSize(innerWidth, innerHeight);
      renderer.setPixelRatio(devicePixelRatio);
      document.body.appendChild(renderer.domElement);

      const scene = new THREE.Scene();
      scene.background = new THREE.Color('#f3efe8');
      const pmrem = new THREE.PMREMGenerator(renderer);
      scene.environment = pmrem.fromScene(new RoomEnvironment()).texture;

      const camera = new THREE.PerspectiveCamera(40, innerWidth / innerHeight, 0.1, 100);
      camera.position.set(2.5, 1.6, 3);
      const controls = new OrbitControls(camera, renderer.domElement);
      controls.target.set(0, 0.6, 0);
      controls.enableDamping = true;

      const loader = new GLTFLoader();
      const place = (file, x, z, rotY = 0) =>
        loader.load('models/' + file, (gltf) => {
          gltf.scene.position.set(x, 0, z);
          gltf.scene.rotation.y = rotY;
          scene.add(gltf.scene);
        });

      place('wooden-armchair.glb', 0, 0, 0.4);
      place('oak-bedside-table.glb', 0.9, -0.2);
      place('ceramic-table-lamp.glb', 0.9, -0.2);
      place('fiddle-leaf-fig.glb', -0.9, -0.4);

      renderer.setAnimationLoop(() => { controls.update(); renderer.render(scene, camera); });

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

Rename your downloaded files to match the names in the code, or change the names in the code. Start your local server and open the page. You can rotate the scene with the mouse and zoom with the scroll wheel.

What the code does

Renderer and scene. We create a WebGL renderer that fills the window and a scene with a warm background colour.

Environment lighting. RoomEnvironment generates a soft studio-like light environment. PBR materials need an environment to reflect, and this one-liner gives our models believable reflections without loading an HDRI.

Camera and controls. A perspective camera looks at the corner, and OrbitControls lets visitors rotate and zoom. Damping makes the movement feel smooth.

Loading models. GLTFLoader reads each GLB, including its materials and textures, and gives us a scene object we can position and rotate. Our models use real-world scale in metres and have their origin at the base, so placing them on the floor is just y = 0.

Positioning tips

  • Stacking objects. The lamp needs to sit on top of the table. Raise it by the table height: replace 0 with the table's height in the position line, or compute it with new THREE.Box3().setFromObject(table).

  • Rotation. rotation.y is in radians: Math.PI / 2 turns a model 90 degrees.

  • Scale. All our models share the same scale, so you should not need to scale anything.

Making it production-ready

  • Shadows. Add a DirectionalLight with castShadow = true and set castShadow on the model meshes for grounded objects.

  • Loading screen. Use THREE.LoadingManager to show progress while models download.

  • Compression. For big scenes, tools like gltf-transform can compress geometry and textures further. Converting files for your own project is allowed under our licence.

  • Simpler option. If you only need a single model with rotate and zoom, Google's model-viewer web component shows a GLB with one HTML tag.

Your turn

Swap the reading corner for a kitchen, a playroom or a tiny village. Every model in the store is a GLB that works with this exact code.

Models from this article

All models →
Share

Hand-picked articles from the same cluster.