Orbit controls cube is not visible

Hello, three.js community! I need some help.

I am a beginner and am trying to make a basic cube that you could rotate using the orbit controls. I looked at different examples of this exact same project, but I have no idea what I might be missing. When I console.log the scene’s children elements, the cube is included in the children. However, in the viewport, the canvas is there, but not the cube.

Here is what the viewport looks like: (The gray rectangle is the canvas)

Below is my code:
index.html:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="./style.css">
    <title>Vite App</title>
  </head>
  <body>
    <script type="module" src="/main.js"></script>
    <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>
  </body>
</html>

main.js:

import * as THREE from 'three';
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js';

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

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

const fov = 75;
const aspect = window.innerwidth/window.innerHeight;
const near = 0.1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(5, -5, 10)

const controls = new OrbitControls( camera, renderer.domElement );
controls.enable = true;
controls.enableRotate = true;
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.screenSpacePanning = false;
controls.minDistance = 100;
controls.maxDistance = 500;
controls.maxPolarAngle = Math.PI / 2;

controls.update();

const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
const material = new THREE.MeshPhongMaterial({color: 0x44aa88});
const cube = new THREE.Mesh(geometry, material);
cube.position.set(0, 0, 0);
scene.add(cube)

camera.lookAt(cube.position)

const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(0, 2, 10);
scene.add(light);

function animate() {
  requestAnimationFrame(animate);

  controls.update();

  renderer.render(scene, camera);
}
renderer.render(scene, camera);
animate();

If you have any suggestions as to why the cube might not be appearing, please let me know.

Thank you!

Is there an error shown in your browsers developer tools console or network tabs?

Also, if you are using Vite to bundle, then you don’t need this line in your index.html
<script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>
That line is useful if you are using import maps which are not necessary if you are bundling.

Also, it appears that you may be using Vite,
Your folder structure is very important when using Vite.
Show a screen grab of your folder structure.

Was there ever a point in your project where it did work?
You should go back to that point and the re-introduce one change at a time until it breaks, and then try to understand why.

When I test your app I get exactly the same result. Did you copy that from somewhere? Maybe there is invalid formatting somewhere if this is a copied example. I had that once, too.

Please try the following. Delete everything in your main. Choose select all and delete. Than copy and paste this minimum setup in your main.

import * as THREE from 'three';


let renderer, scene, camera, controls;

init();
animate();

function init(){
   renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
   renderer.setPixelRatio(window.devicePixelRatio); 
   renderer.setSize(window.innerWidth,window.innerHeight);
   document.body.appendChild(renderer.domElement); 
   scene = new THREE.Scene();
   scene.background = new THREE.Color(0x00001f);

   camera = new THREE.PerspectiveCamera(50.0, window.innerWidth/window.innerHeight, 0.5, 10000);
   camera.position.set(0, 0, 3);

   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 ); 
}  

function animate() {
   requestAnimationFrame(render);
   render(); 
}

function render(){
   renderer.render(scene, camera);
}

If you see a green cube, you can add OrbitControls and change the setup bit by bit.

1 Like

Thank you, the green cube managed to work. I will work on the OrbitControls part now. Sometimes in people’s examples online I see them use main(), and init(), are they required for three js to work properly? I thought it just depends on one’s preference for setting up the code.

Good news, I’ve added OrbitControls and it works as it should be! The cube is a little zoomed out, but I think I can find a solution to that.

Here is the new code:

import * as THREE from 'three';
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js';

let renderer, scene, camera, controls;

init();
animate();

function init(){
   renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
   renderer.setPixelRatio(window.devicePixelRatio); 
   renderer.setSize(window.innerWidth,window.innerHeight);
   document.body.appendChild(renderer.domElement); 
   scene = new THREE.Scene();
   scene.background = new THREE.Color(0x00001f);

   camera = new THREE.PerspectiveCamera(50.0, window.innerWidth/window.innerHeight, 0.5, 10000);
   camera.position.set(0, 2, 3);

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


   controls = new OrbitControls( camera, renderer.domElement );
   controls.enable = true;
   controls.enableRotate = true;
   controls.enableDamping = true;
   controls.dampingFactor = 0.05;
   controls.screenSpacePanning = false;
   controls.minDistance = 100;
   controls.maxDistance = 500;
   controls.maxPolarAngle = Math.PI / 2;

   controls.update()
}  

function animate() {
   render(); 
   requestAnimationFrame(animate);
   controls.update();
   
}

function render(){
   renderer.render(scene, camera);
}

When I moved render() above requestAnimationFrame() and controls.update(), I could replace the requestAnimationFrame(render) with requestAnimationFrame(animate) without the canvas breaking.

Thank you for your help, Attila!

1 Like