How do I add labels to cube surfaces?

Hello everyone, I tried to add a label to this earring in my hand, but it does not work as I expected. As the cube rotates, the labels behind it should not be visible and the labels should move in the same direction as the cube.

My code:

import './style.css'
import * as THREE from 'three'
import * as TWEEN from '@tweenjs/tween.js'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

let camera,
    scene, 
    renderer, 
    mesh,
    lightPrimary, 
    lightSecondary, 
    raycaster, 
    mockTarget, 
    controls,
    cubeGeometry
const createWorld = () => {

  // Mesh
     cubeGeometry = new THREE.BoxGeometry(2.0, 2.0, 2.0)
    
    mesh = new THREE.Mesh(
        cubeGeometry,
        new THREE.MeshLambertMaterial( {
            map: new THREE.TextureLoader().load('/img/3.png'),
            color: 0xffffff, // Küp rengi
            flatShading: true
        })
  );
  scene.add(mesh);
  mesh.receiveShadow = true;

  // Light 

  lightPrimary = new THREE.PointLight(0xffffff, 1.0, 10.0);
  lightPrimary.position.set(2.0, 2.0, 2.0);
  lightPrimary.castShadow = true;
  
  lightSecondary = new THREE.PointLight(0x8888ff, 1.0, 10.0);
  lightSecondary.position.set(-2.0, 2.0, -2.0);
  lightSecondary.castShadow = true;
  
  scene.add(lightPrimary);
  scene.add(lightSecondary);

  const faces = cubeGeometry.index
};


let stopAnim = false

const animateCube = () => {
    const rotationSpeed = 0.002;
    mesh.rotation.x += rotationSpeed;
    mesh.rotation.y += rotationSpeed;
};
  

const init = () => {
  camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000.0);

  scene = new THREE.Scene();
  scene.background = new THREE.Color(0xffffff);

  scene.add(new THREE.HemisphereLight(0x9ED9F8, 0x005091, 0.5));

  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.shadowMap.enabled = true;
  renderer.setClearColor(0x333333, 1);

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

  document.body.appendChild(renderer.domElement);
  
  createWorld();
  camera.position.set(-5, 5, 7);

  controls = new OrbitControls(camera, renderer.domElement);
  controls.enableDamping = true
  controls.enableZoom = false;

  
  raycaster = new THREE.Raycaster();
  mockTarget = new THREE.Object3D();
  
  camera.getWorldPosition(mockTarget.position);
  camera.getWorldQuaternion(mockTarget.quaternion);
  controls.update()
  
  window.addEventListener("dblclick", (event) => {
    const mouse = new THREE.Vector2();

    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;

    raycaster.setFromCamera(mouse, camera);

    const intersects = raycaster.intersectObjects([mesh]);

    if (!intersects[0]) {
        return;
    } else {
        mesh.rotation.set(0, 0, 0);
        stopAnim = true
        const faceIndex = intersects[0].faceIndex;
        console.log(faceIndex % 6 + 1);
      }
      const {  object,normal } = intersects[0];
      const offset = 5.0;
  
   
      const targetPosition = new THREE.Vector3();
      object.getWorldPosition(targetPosition);
  
      mockTarget.position.copy(targetPosition);
      mockTarget.position.add(
        normal.clone().multiplyScalar(offset)
      );
      
      mockTarget.lookAt(object.position);
      mockTarget.rotateY(-Math.PI);
  
      setTimeout(() => {
          // Zoom in
          new TWEEN.Tween(camera.position)
            .to({ x: object.position.x, y: object.position.y, z: object.position.z}, 1000)
            .easing(TWEEN.Easing.Quadratic.Out)
            .onComplete(() => {
              window.location.href = 'wubba.html'
          })
            .start();
      }, 2000)
     
  });
  
}


const labelElements = [];

const createLabels = () => {
  const cubeSize = 2.0;
  const halfCubeSize = cubeSize / 2;

  const faceLabels = ["Front", "Back", "Top", "Bottom", "Right", "Left"];

  for (let i = 0; i < 6; i++) {
    const labelElement = document.createElement("div");
    labelElement.className = "label";
    labelElement.textContent = faceLabels[i];

    labelElement.style.position = "absolute";
    labelElement.style.color = "white";
    labelElement.style.fontSize = "16px";
    labelElement.style.fontWeight = "bold";
    labelElement.style.transform = "translate(-50%, -50%)";

    document.body.appendChild(labelElement);
    labelElements.push(labelElement);
  }
};

const updateLabels = () => {
  const cubeSize = 2.0;
  const halfCubeSize = cubeSize / 2;

  const cubeWorldPosition = new THREE.Vector3();
  mesh.getWorldPosition(cubeWorldPosition);

  const faceData = [
    { center: new THREE.Vector3(0, 0, halfCubeSize), normal: new THREE.Vector3(0, 0, 1) },
    { center: new THREE.Vector3(0, 0, -halfCubeSize), normal: new THREE.Vector3(0, 0, -1) },
    { center: new THREE.Vector3(0, halfCubeSize, 0), normal: new THREE.Vector3(0, 1, 0) },
    { center: new THREE.Vector3(0, -halfCubeSize, 0), normal: new THREE.Vector3(0, -1, 0) },
    { center: new THREE.Vector3(halfCubeSize, 0, 0), normal: new THREE.Vector3(1, 0, 0) },
    { center: new THREE.Vector3(-halfCubeSize, 0, 0), normal: new THREE.Vector3(-1, 0, 0) },
  ];

  const faceScreenPositions = faceData.map((face) => {
    const screenPosition = new THREE.Vector3();
    face.center.project(camera);
    screenPosition.x = (face.center.x + 1) / 2 * window.innerWidth;
    screenPosition.y = (-face.center.y + 1) / 2 * window.innerHeight;
    return { screenPosition, normal: face.normal };
  });

  // Etiketleri güncelle
  for (let i = 0; i < 6; i++) {
    const labelElement = labelElements[i];
    const { screenPosition, normal } = faceScreenPositions[i];

    const angleToCamera = screenPosition.angleTo(camera.position);

    labelElement.style.display = angleToCamera < Math.PI / 2 ? "block" : "none";

    labelElement.style.left = `${screenPosition.x}px`;
    labelElement.style.top = `${screenPosition.y}px`;

    labelElement.style.transform = `translate(-50%, -50%) rotate(${mesh.rotation.z}rad)`;
  }
};


const animate = () => {
  requestAnimationFrame(animate);
  if(!stopAnim) {
      animateCube(); // rotate
  }

  TWEEN.update(); 
  controls.update()
  updateLabels();

  renderer.render(scene, camera); 
  camera.position.lerp(mockTarget.position, 0.1);
  camera.quaternion.slerp(mockTarget.quaternion, 0.1);
}

init();
createLabels();

animate();

If you are desperate, you could use TextGeometry to label the cube. I have described a method of using TextGeometry to create one-sided 2D text. But it sounds like you wouldn’t need one-sided text, but could use TextGeometry and simply bury the text within each face of the cube.

But the more efficient method would be to (1) use transparent 2D text material either place it over each face (like this); (2) somehow combine the 2D text material with the material for each face and apply the material to each face; or (3) add the text to each texture in Blender.

For #1, I created the text by hand using Blender. For #2, you could combine the materials at the texture level using an html canvas.

I’m sure that others can think of other methods, including how to fix the method you are trying to use. I just thought you might be interested in some of the alternative methods that are available and that I have used.