Three.js - Debug & Stats



Using Dat.GUI

It is hard to keep experimenting with the values of variables, like the cube’s position. In that case, suppose until you get something you like. It's a kind of slow and overwhelming process. Luckily, there is already a good solution available that integrates great with Three.js, dat.GUI. It allows you to create a fundamental user interface component that can change variables in your code.

Installation

To use dat.GUI in your project, download it here and add the <script> tag to the HTML file.

<script type='text/javascript' src='path/to/dat.gui.min.js'></script>

Or you can use CDN, add the following <script> tag inside your HTML.

<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.js"></script>

If you are using Three.js in a node app, install the npm package - dat.GUI and import it into your JavaScript file.

npm install dat.gui

OR

yarn add dat.gui
import * as dat from 'dat.gui'

Usage

First, you should initialize the object itself. It creates a widget and displays it on the screen top rightcorner.

const gui = new dat.GUI()

Then, you can add the parameter you want to control and the variable. For example, the following code is to control the y position of the cube.

gui.add(cube.position, 'y')

Example

Try adding other position variables. Refer to this working code example.

cube.html

<!DOCTYPE html>
<html lang="en">
   <head>
      <meta charset="UTF-8" />
      <meta http-equiv="X-UA-Compatible" content="ie=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <title>Three.js - Position GUI</title>
      <style>
         * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: -applesystem, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
            Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
         }
         html,
         body {
            height: 100vh;
            width: 100vw;
         }
         #threejs-container {
            position: block;
            width: 100%;
            height: 100%;
         }
      </style>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.js"></script>
   </head>
   <body>
      <div id="threejs-container"></div>
      <script type="module">
         // Adding UI to debug and experimenting different values
         // UI
         const gui = new dat.GUI()
         // sizes
         let width = window.innerWidth
         let height = window.innerHeight
         // scene
         const scene = new THREE.Scene()
         scene.background = new THREE.Color(0x262626)
         // camera
         const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 100)
         camera.position.set(0, 0, 10)
         // cube
         const geometry = new THREE.BoxGeometry(2, 2, 2)
         const material = new THREE.MeshBasicMaterial({
            color: 0xffffff,
            wireframe: true
         })
         gui.add(material, 'wireframe')
         const cube = new THREE.Mesh(geometry, material)
         scene.add(cube)
         gui.add(cube.position, 'x')
         gui.add(cube.position, 'y')
         gui.add(cube.position, 'z')
         // responsiveness
         window.addEventListener('resize', () => {
            width = window.innerWidth
            height = window.innerHeight
            camera.aspect = width / height
            camera.updateProjectionMatrix()
            renderer.setSize(window.innerWidth, window.innerHeight)
            renderer.render(scene, camera)
         })
         // renderer
         const renderer = new THREE.WebGL1Renderer()
         renderer.setSize(width, height)
         renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
         // animation
         function animate() {
            requestAnimationFrame(animate)
            cube.rotation.x += 0.005
            cube.rotation.y += 0.01
            renderer.render(scene, camera)
         }
         // rendering the scene
         const container = document.querySelector('#threejs-container')
         container.append(renderer.domElement)
         renderer.render(scene, camera)
         animate()
      </script>
   </body>
</html>

Output

You can customize the label displayed using the name attribute. To change the label on the variable line, use .name("your label").

gui.add(cube.position, 'y').name('cube-y')

You can set up min/max limits and steps for getting the slider. The following line allow values from 1 to 10, increasing the value by 1 at a time.

gui.add(cube.position, 'y').min(1).max(10).step(1)
// or
gui.add(cube.position, 'y', 1, 10, 1)

If there are many variables with the same name, you may find it difficult to differentiate among them. In that case, you can add folders for every object. All the variables related to an object be in one folder.

// creating a folder
const cube1 = gui.addFolder('Cube 1')
cube1.add(redCube.position, 'y').min(1).max(10).step(1)
cube1.add(redCube.position, 'x').min(1).max(10).step(1)
cube1.add(redCube.position, 'z').min(1).max(10).step(1)
// another folder
const cube2 = gui.addFolder('Cube 2')
cube2.add(greenCube.position, 'y').min(1).max(10).step(1)
cube2.add(greenCube.position, 'x').min(1).max(10).step(1)
cube2.add(greenCube.position, 'z').min(1).max(10).step(1)

Example

Now, check the following example.

gui-folders.html

<!DOCTYPE html>
<html lang="en">
   <head>
      <meta charset="UTF-8" />
      <meta http-equiv="X-UA-Compatible" content="ie=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <title>Three.js - More variables</title>
      <style>
         * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: -applesystem, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
               Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
         }
         html,
         body {
            height: 100vh;
            width: 100vw;
         }
         #threejs-container {
            position: block;
            width: 100%;
            height: 100%;
         }
      </style>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.js"></script>
   </head>
   <body>
      <div id="threejs-container"></div>
      <script type="module">
         // Adding folders to distinguish between variables

         // controls
         const gui = new dat.GUI()
         // sizes
         let width = window.innerWidth
         let height = window.innerHeight
         // scene
         const scene = new THREE.Scene()
         scene.background = new THREE.Color(0x262626)
         // camera
         const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 100)
         camera.position.set(0, 0, 10)
         const camFolder = gui.addFolder('Camera')
         camFolder.add(camera.position, 'z').min(10).max(60).step(10)
         // cube
         const geometry = new THREE.BoxGeometry(2, 2, 2)
         const material = new THREE.MeshBasicMaterial({
            color: 0xffffff,
            wireframe: true
         })
         const cubeColor = {
            color: 0xffffff
         }
         const materialFolder = gui.addFolder('Material')
         materialFolder.add(material, 'wireframe')
         materialFolder.addColor(cubeColor, 'color').onChange(() => {
            // callback
            material.color.set(cubeColor.color)
         })
         materialFolder.open()
         const cube = new THREE.Mesh(geometry, material)
         scene.add(cube)
         const cubeFolder = gui.addFolder('Cube')
         // for position
         const posFolder = cubeFolder.addFolder('position')
         posFolder.add(cube.position, 'x', 0, 5, 0.1)
         posFolder.add(cube.position, 'y', 0, 5, 0.1)
         posFolder.add(cube.position, 'z', 0, 5, 0.1)
         posFolder.open()
         // for scale
         const scaleFolder = cubeFolder.addFolder('Scale')
         scaleFolder.add(cube.scale, 'x', 0, 5, 0.1).name('Width')
         scaleFolder.add(cube.scale, 'y', 0, 5, 0.1).name('Height')
         scaleFolder.add(cube.scale, 'z', 0, 5, 0.1).name('Depth')
         scaleFolder.open()
         cubeFolder.open()
         // responsiveness
         window.addEventListener('resize', () => {
            width = window.innerWidth
            height = window.innerHeight
            camera.aspect = width / height
            camera.updateProjectionMatrix()
            renderer.setSize(window.innerWidth, window.innerHeight)
            renderer.render(scene, camera)
         })
         // renderer
         const renderer = new THREE.WebGL1Renderer()
         renderer.setSize(width, height)
         renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
         // animation
         function animate() {
         requestAnimationFrame(animate)
            cube.rotation.x += 0.005
            cube.rotation.y += 0.01
            renderer.render(scene, camera)
         }
         // rendering the scene
         const container = document.querySelector('#threejs-container')
         container.append(renderer.domElement)
         renderer.render(scene, camera)
         animate()
      </script>
   </body>
</html>

Output

You can also add some callback functions. onChange is triggered once the value is changed.

gui.add(cube.position, 'y').onChange(function () {
   // refresh based on the new value of y
   console.log(cube.position.y)
})

Let's see another example of changing color using dat.gui and callbacks.

// parameter
const cubeColor = {
   color: 0xff0000,
}
gui.addColor(cubeColor, 'color').onChange(() => {
   // callback
   cube.color.set(cubeColor.color)
})

The above callback onChange notifies Three.js to change the cube color when the color from cubeColor changes.

We are going to use this dat.gui a lot from now. Make sure you get used to it by experimenting with the "Hello Cube!" app.

  • Stats − Statistics play an important role in large-scale applications.

Advertisements