Title: Three.js Debugging JavaScript Description: How to debug JavaScript with THREE.js TOC: Debugging JavaScript Most of this article is not directly about THREE.js but is rather about debugging JavaScript in general. It seemed important in that many people just starting with THREE.js are also just starting with JavaScript so I hope this can help them more easily solve any issues they run into. Debugging is a big topic and I probably can't begin to cover everything there is to know but if you're new to JavaScript then here's an attempt to give a few pointers. I strongly suggest you take some time to learn them. They'll help you enormously in your learning. ## Learn your Browser's Developer Tools All browsers have developer tools. [Chrome](https://developers.google.com/web/tools/chrome-devtools/), [Firefox](https://developer.mozilla.org/en-US/docs/Tools), [Safari](https://developer.apple.com/safari/tools/), [Edge](https://docs.microsoft.com/en-us/microsoft-edge/devtools-guide). In Chrome you can click the the `⋮` icon, pick More Tools->Developer Tools to get to the developer tools. A keyboard shortcut is also shown there.
` tags and put data in them. The most obvious way is to make some HTML elements ```html ++``` Style them so they stay on top of the canvas. (assuming your canvas fills the page) ```html ``` And then looking the elements up and setting their content. ```js // at init time const xElem = document.querySelector('#x'); const yElem = document.querySelector('#y'); const zElem = document.querySelector('#z'); // at render or update time xElem.textContent = someObject.position.x.toFixed(3); yElem.textContent = someObject.position.y.toFixed(3); zElem.textContent = someObject.position.z.toFixed(3); ``` This is more useful for real time values {{{example url="../threejs-debug-js-html-elements.html" }}} Another way to put data on the screen is to make a clearing logger. I just made that term up but lots of games I've worked on have used this solution. The idea is you have a buffer that displays messages for only one frame. Any part of your code that wants to display data calls some function to add data to that buffer every frame. This is much less work than making an element per piece of data above. For example let's change the HTML from above to just this ```htmlx:+y:+z:+``` And let's make simple class to manage this *clear back buffer*. ```js class ClearingLogger { constructor(elem) { this.elem = elem; this.lines = []; } log(...args) { this.lines.push([...args].join(' ')); } render() { this.elem.textContent = this.lines.join('\n'); this.lines = []; } } ``` Then let's make a simple example that every time we click the mouse makes a mesh that moves in a random direction for 2 seconds. We'll start with one of the examples from the article on [making things responsive](threejs-responsive.html) Here's the code that adds a new `Mesh` every time we click the mouse ```js const geometry = new THREE.SphereGeometry(); const material = new THREE.MeshBasicMaterial({color: 'red'}); const things = []; function rand(min, max) { if (max === undefined) { max = min; min = 0; } return Math.random() * (max - min) + min; } function createThing() { const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); things.push({ mesh, timer: 2, velocity: new THREE.Vector3(rand(-5, 5), rand(-5, 5), rand(-5, 5)), }); } canvas.addEventListener('click', createThing); ``` And here's the code that moves the meshes we created, logs them, and removes them when their timer has run out ```js const logger = new ClearingLogger(document.querySelector('#debug pre')); let then = 0; function render(now) { now *= 0.001; // convert to seconds const deltaTime = now - then; then = now; ... logger.log('fps:', (1 / deltaTime).toFixed(1)); logger.log('num things:', things.length); for (let i = 0; i < things.length;) { const thing = things[i]; const mesh = thing.mesh; const pos = mesh.position; logger.log( 'timer:', thing.timer.toFixed(3), 'pos:', pos.x.toFixed(3), pos.y.toFixed(3), pos.z.toFixed(3)); thing.timer -= deltaTime; if (thing.timer <= 0) { // remove this thing. Note we don't advance `i` things.splice(i, 1); scene.remove(mesh); } else { mesh.position.addScaledVector(thing.velocity, deltaTime); ++i; } } renderer.render(scene, camera); logger.render(); requestAnimationFrame(render); } ``` Now click the mouse a bunch in the example below {{{example url="../threejs-debug-js-clearing-logger.html" }}} ## Query Parameters Another thing to remember is that webpages can have data passed into them either via query parameters or the anchor, sometimes called the search and the hash. https://domain/path/?query#anchor You can use this to make features optional or pass in parameters. For example let's take the previous example and make it so the debug stuff only shows up if we put `?debug=true` in the URL. First we need some code to parse the query string ```js /** * Returns the query parameters as a key/value object. * Example: If the query parameters are * * abc=123&def=456&name=gman * * Then `getQuery()` will return an object like * * { * abc: '123', * def: '456', * name: 'gman', * } */ function getQuery() { return Object.fromEntries(new URLSearchParams(window.location.search).entries()); } ``` Then we might make the debug element not show by default ```html + ``` Then in the code we read the params and choose to un-hide the debug info if and only if `?debug=true` is passed in ```js const query = getQuery(); const debug = query.debug === 'true'; const logger = debug ? new ClearingLogger(document.querySelector('#debug pre')) : new DummyLogger(); if (debug) { document.querySelector('#debug').style.display = ''; } ``` We also made a `DummyLogger` that does nothing and chose to use it if `?debug=true` has not been passed in. ```js class DummyLogger { log() {} render() {} } ``` You can see if we use this url: threejs-debug-js-params.html there is no debug info but if we use this url: threejs-debug-js-params.html?debug=true there is debug info. Multiple parameters can be passed in by separating with '&' as in `somepage.html?someparam=somevalue&someotherparam=someothervalue`. Using parameters like this we can pass in all kinds of options. Maybe `speed=0.01` to slow down our app for making it easier to understand something or `showHelpers=true` for whether or not to add helpers that show the lights, shadow, or camera frustum seen in other lessons. ## Learn to use the Debugger Every browser has a debugger where you can pause your program step through line by line and inspect all the variables. Teaching you how to use a debugger is too big a topic for this article but here's a few links * [Get Started with Debugging JavaScript in Chrome DevTools](https://developers.google.com/web/tools/chrome-devtools/javascript/) * [Debugging in Chrome](https://javascript.info/debugging-chrome) * [Tips and Tricks for Debugging in Chrome Developer Tools](https://hackernoon.com/tips-and-tricks-for-debugging-in-chrome-developer-tools-458ade27c7ab) ## Check for `NaN` in the debugger or elsewhere `NaN` is short for Not A Number. It's what JavaScript will assign as a value when you do something that doesn't make sense mathwise. As a simple exampleOften when I'm making something and nothing appears on the screen I'll check some values and if I see `NaN` I will instantly have a place to start looking. As an example when I first started making the path for the [article about loading gLTF files](threejs-load-gltf.html) I made a curve using the `SplineCurve` class which makes a 2D curve. I then used that curve to move the cars like this ```js curve.getPointAt(zeroToOnePointOnCurve, car.position); ``` Internally `curve.getPointAt` calls the `set` function on the object passed as the second argument. In this case that second argument is `car.position` which is a `Vector3`. `Vector3`'s `set` function requires 3 arguments, x, y, and z but `SplineCurve` is a 2D curve and so it calls `car.position.set` with just x and y. The result is that `car.position.set` sets x to x, y to y, and z to `undefined`. A quick glance in the debugger looking at the car's `matrixWorld` showed a bunch of `NaN` values.Seeing the matrix had `NaN`s in it suggested something like `position`, `rotation`, `scale` or some other function that affects that matrix had bad data. Working backward from their it was easy to track down the issue. In top of `NaN` there's also `Infinity` which is a similar sign there is a math bug somewhere. ## Look In the Code! THREE.js is Open Source. Don't be afraid to look inside the code! You can look inside on [github](https://github.com/mrdoob/three.js). You can also look inside by stepping into functions in the debugger. ## Put `requestAnimationFrame` at bottom of your render function. I see this pattern often ```js function render() { requestAnimationFrame(render); // -- do stuff -- renderer.render(scene, camera); } requestAnimationFrame(render); ``` I'd suggest that putting the call to `requestAnimationFrame` at the bottom as in ```js function render() { // -- do stuff -- renderer.render(scene, camera); requestAnimationFrame(render); } requestAnimationFrame(render); ``` The biggest reason is it means your code will stop if you have an error. Putting `requestAnimationFrame` at the top means your code will keep running even if you have an error since you already requested another frame. IMO it's better to find those errors than to ignore them. They could easily be the reason something is not appearing as you expect it to but unless your code stops you might not even notice. ## Check your units! This basically means knowing for example when to use degrees vs when to use radians. It's unfortunate that THREE.js does not consistently use the same units everywhere. Off the top of my head the camera's field of view is in degrees. All other angles are in radians. The other place to look out is your world unit size. Until recently 3D apps could choose any unit size they wanted. One app might choose 1 unit = 1cm. Another might choose 1 unit = 1 foot. It's actually still true that you can chose any units you want for certain applications. That said, THREE.js assumes 1 unit = 1 meter. This is important for things like physically based rendering which uses meters to compute lighting effects. It's also important for AR and VR which need to deal with real world units like where your phone is or where the VR controllers are. ## Making a *Minimal, Complete, Verifiable, Example* for Stack Overflow If you decide to ask a question about THREE.js it's almost always required for you to provide an MCVE which stands for Minimal, Complete, Verifiable, Example. The **Minimal** part is important. Let's say you where having an issue with the path movement in the last example of the [loading a gLTF article](threejs-load-gltf.html). That example has many parts. Listing them out it has 1. A bunch of HTML 2. Some CSS 3. Lights 4. Shadows 5. DAT.gui code to manipulate shadows 6. Code to load a .GLTF file 7. Code to resize the canvas. 8. Code to move the cars along paths That's pretty huge. If your question is only about the path following part you can remove most of the HTML as you only need a `