HTML

Starter pack

So far the 3D experiences we built contained only a canvas HTML element where our Three.js 3D scenes were rendered.

There are two ways to make our experiences more interactive:

  • Build your HTML page like you usually do and add your 3D scene as a background in a fullscreen canvas. Then make it well integrated by connecting them through mouse events or page scroll. We will see how to achieve this in the scroll section in the animations lesson.
  • Adding HTML elements as if they were 3D objects in our 3D scenes. This is what we will see in this lesson.

Those two approaches are not mutually exclusive. You can combine them to create immersive experiences.

Ikea online showroom

For this lesson, let's imagine we want to create an online showroom for Ikea. The starter pack contains various 3D models of kitchen furniture (All coming from Quaternius on Poly Pizza, thank you 🙏).

3D Kitchen

This is what we have so far. A Japanese style kitchen.

We will try to add labels on the kitchen furniture to show the name and price of each item.

Before doing it, let me explain some parts of the starter pack.

To make the code generic, instead of declaring each item manually and re-write the loading and positioning logic, I created an array of items.

const sceneItems = [
  {
    model: "Japanese Door.glb",
    position: [0, -0.88, -1.2],
    scale: 1.8,
  },
  {
    model: "Counter Sink.glb",
    position: [1.3, 0, -2.8],
  },
  {
    model: "Chopping board.glb",
    position: [2.8, 1.9, -2.8],
    scale: 0.5,
  },
  {
    model: "Fridge.glb",
    position: [-2.1, 0, -3],
  },
  {
    model: "Table.glb",
    position: [-1, 0, 2],
    scale: [1, 1, 1],
  },
  {
    model: "Dango.glb",
    position: [-1.4, 1.64, 2],
    scale: 0.72,
    rotation: [0, Math.PI / 6, 0],
  },
];

Then I created a generic component called Item that takes the model path and other transform props (position, rotation, scale...):

const Item = ({ model, position, rotation, ...props }) => {
  const gltf = useGLTF(`models/${model}`);

  return (
    <group position={position} rotation={rotation}>
      <primitive object={gltf.scene} {...props} />
    </group>
  );
};

The component takes care of loading the 3D model and positioning it in the scene. I chose a group as a root element to be able later to add HTML elements as children.

Then we loop through the items array and render an Item component for each item:

export const Experience = () => {
  return (
    <>
      {sceneItems.map((item, index) => {
        return <Item key={index} {...item} />;
      })}
    </>
  );
};

HTML elements in 3D

Now that we have a generic component that can render any 3D model and position it in the scene, we can focus on our furniture labels.

Let's add more information to our items array by adding a label and price property:

const sceneItems = [
  {
    model: "Japanese Door.glb",
    position: [0, -0.88, -1.2],
    scale: 1.8,
    name: "Hemnes",
    price: 300,
  },
  {
    model: "Counter Sink.glb",
    position: [1.3, 0, -2.8],
    name: "Lillången",
    price: 450,
  },
  {
    model: "Chopping board.glb",
    position: [2.8, 1.9, -2.8],
    scale: 0.5,
    name: "Skogsta",
    price: 25,
  },
  {
    model: "Fridge.glb",
    position: [-2.1, 0, -3],
    name: "Lagan",
    price: 600,
  },
  {
    model: "Table.glb",
    position: [-1, 0, 2],
    scale: [1, 1, 1],
    name: "Lerhamn",
    price: 80,
  },
  {
    model: "Dango.glb",
    position: [-1.4, 1.64, 2],
    scale: 0.72,
    rotation: [0, Math.PI / 6, 0],
    name: "Dango",
    price: 4,
  },
];

Don't be offended by the name and prices, they're GitHub Copilot generated...

Now we can update our Item component to get the name and price props and render them as HTML elements:

End of lesson preview

To get access to the entire lesson, you need to purchase the course.