"Encountered Uncaught RangeError in three.js: The render loop has exceeded the maximum call stack size

Recently delving into the world of JS and three.js, I've encountered an issue with a stack exceedance in a self-calling function that is invoked by three.js as part of its design. Strangely, this stack exceedance only occurs when I remove the call from the main function.

I started off with the cube example taken from the official Three.js documentation. My goal is to create dynamic animations where objects can be added or removed from the scene, and displayed on a specified canvas.

The original code snippet from Three.js looks like this:

        var scene = new THREE.Scene();
        var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);

        var renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

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

        camera.position.z = 5;

        var render = function () {
            requestAnimationFrame(render);

            cube.rotation.x += 0.1;
            cube.rotation.y += 0.1;

            renderer.render(scene, camera);
        };

        render();

As I studied the animation call render(); which recursively calls itself within requestAnimationFrame(), I attempted to structure my program for future flexibility but ran into issues:

function e3Dview() {
    // Setting up the e Canvas
    this.canvas = document.getElementById("eplot3D");

    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera(75, this.canvas.width / this.canvas.height, 0.1, 1000);

    renderer = new THREE.WebGLRenderer({ canvas: eplot3D });
    renderer.setSize(this.canvas.width, this.canvas.height);

    var geo = new THREE.BoxGeometry(1, 1, 1);
    var mat = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
    mat.wireframe = true;
    this.cube = new THREE.Mesh(geo, mat);
    scene.add(this.cube);

    camera.position.z = 5;

    controls = new THREE.OrbitControls(camera, renderer.domElement);

    // Executing the rendering loop
    this.renderloop();
};

e3Dview.prototype.renderloop = function() {
    requestAnimationFrame(this.renderloop());

    this.cube.rotation.x += 0.01;
    this.cube.rotation.y += 0.01;

    renderer.render(scene, camera);
};

e3Dview.prototype.sceneClear = function() {
     scene.children.forEach(function(object) {
        scene.remove(object);
    });
};

Upon moving the rendering loop outside the initial parent call, I encountered a "stackoverflow" error...

Uncaught RangeError: Maximum call stack size exceeded

This leads me to question why does the render function calling itself within requestAnimationFrame work fine, whereas doing the same outside results in a failed stack clearance?

What might I be overlooking in this situation?

Answer №1

The problem lies within this particular line of code:

requestAnimationFrame( this.renderloop() );

By calling `renderloop()` immediately instead of passing it as a callback function to `requestAnimationFrame`, you end up creating an infinite recursion loop.

You might be tempted to change it to the following, but that won't work either because the function prototype lacks binding to any object:

requestAnimationFrame( this.renderloop ); // This approach won't solve the issue.

One solution is to bind the function to the object's scope like this:

requestAnimationFrame( this.renderloop.bind(this) ); // Although effective, there may be performance drawbacks at 60FPS.

My suggestion is to move the `renderloop` function into the `e3Dview` constructor as a private method, as shown below:

function e3Dview(){
    var $this = this; // Store current object's scope for accessing properties within the callback method.

    ...

    function renderloop() {
        requestAnimationFrame( renderloop );

        $this.cube.rotation.x += 0.01;
        $this.cube.rotation.y += 0.01;

        renderer.render(scene, camera);
    }

    renderloop();
}

While not the most elegant solution, this is a common practice in such cases. If necessary, you could expose `renderloop` as a public function, although it may not be needed in this context.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is iterating through object with a hasOwnProperty validation necessary?

Is there any benefit to using hasOwnProperty in a loop when an object will always have properties? Take this scenario: const fruits = { banana: 15, kiwi: 10, pineapple: 6, } for (let key in fruits) { if (fruits.hasOwnProperty(key)) { ...

Send functions from the back-end Node to the front-end Angular

Introduction I am currently working on a project that involves verifying email addresses within an API in order to grant users access to a restricted section. To accomplish this, I have developed backend node code with three functions built using Node and ...

The command 'node' is not being recognized as either an internal or external command, potentially due to it being an operable program or batch file. This issue only arises when attempting to

Whenever I try to npm install a package or check the node/npm version, it works fine. However, upon attempting to start the app with any scripts, I encounter the following error message. [EDITED] $ npm start > <a href="/cdn-cgi/l/email-protection" ...

How can Angular9 handle making two API calls simultaneously using Promise.all?

When making an API call, I utilized Promise.all in the following code: Promise.all(this.hostName.slice(0, this.Id.length).map((hostName) => { return this.serviceC.status(hostName) .then(res => { return new Prom ...

The authentication method "discord" is not recognized

Currently, I am working on implementing discord authentication using passport. Originally, everything was functioning correctly, but now it seems to have encountered an issue which I cannot identify. auth.js const express = require('express') co ...

Formatting dates for the bootstrap datepicker

Hi there! I am currently using a bootstrap datepicker and I am attempting to retrieve the value from the datepicker text box in the format of date-month-year for my controller. However, at the moment, I am only able to obtain values in the format Tue Oct 0 ...

How AngularFire automatically adds back a removed item in a Firebase array

I have been working on a way to remove an item from my $firebaseArray called "boxes". Here is the "remove" function: function remove(boxJson) { return boxes.$remove(boxJson); } Although it gets removed, I noticed that it immediately reappea ...

Is there a way to conditionally redirect to a specific page using NextAuth?

My website has 2 points of user login: one is through my app and the other is via a link on a third-party site. If a user comes from the third-party site, they should be redirected back to it. The only method I can come up with to distinguish if a user is ...

What is the best way to transfer text from a textbox to the clipboard with the help of jquery, javascript

Looking to add a copy to clipboard button on your website but want to avoid using Flash? Search for methods that don't require the outdated technology. Is there a way to achieve this without relying on Flash? ...

Error: 'fs' module not found in React.js and cannot be resolved

Encountering issues with tatum io v1 + react? Developers have acknowledged that it's a react problem which will be addressed in V2. In the meantime, you can utilize tatum io V1 with node js. I've included all dependencies that could potentially ...

Instructions for turning an HTML table cell into an editable text box

Essentially, I'm looking to enable users to click on the table and edit the text within it. I found inspiration from this Js Fiddle: http://jsfiddle.net/ddd3nick/ExA3j/22/ Below is the code I've compiled based on the JS fiddle reference. I tho ...

What steps do I need to take to develop a CLI application similar to ng, that can be installed globally on the system

Upon installing npm i ng -g How does the system determine the installation path? I am interested in creating an application that can be installed and executed similarly. ...

Tips for creating bootstrap tabs that only load content when clicked, instead of on page load

In my current scenario, I have a webpage with 6 tabs that load the results from a PHP file index function. Initially, everything worked seamlessly when the files were located on a USB connected directly to the server - the page loaded within seconds. Howev ...

Run code once ngResource callback is completed

In my controller, I have a function for creating resources with ngResource app.controller 'CalculationsCtrl', ($scope, Calculation)-> $scope.save = ()-> $scope.busy = true Calculation.create($scope.calculation, (successRes ...

Adjust the size of an Element with jQuery

I currently have a hamburger and menu positioned on the left side, with a width set to 0px. Is there a way for me to make the width of the menu change to 250px after clicking on the hamburger icon for the first time, and then change back to 0px after clic ...

Determining the moment a user exits a page on Next JS

Is there a way to track when the user exits a Next JS page? I have identified 3 possible ways in which a user might leave a page: Clicking on a link Performing an action that triggers router.back, router.push, etc... Closing the tab (i.e. when beforeunloa ...

To prevent multiple requests to the server, limit the number of requests received if a user clicks on a list too quickly

I need some assistance. I have been working on tracking user clicks. The issue is that I have a list where users can click to fetch data from the server. However, when multiple users click simultaneously, it results in multiple requests being sent to the s ...

ResizableBox is failing to render any content

My current project involves trying out react-resizable. Unfortunately, my basic example only shows the text XYZ in the browser without displaying any resizable box as expected. There are no error messages either. import { ResizableBox } from 'react-re ...

Can we leverage map/filter/reduce functions within a promise by encapsulating the result with Promise.resolve()?

Currently, my approach to doing loops inside a promise looks like this: asyncFunc() .then(() => { return new Promise((resolve) => { for (let i = 0; i < length; i++) { // do something if (j == length - 1) { ...

Ensuring the length of a Multidimensional Array

Today I decided to delve into coding with Google Sheets and JavaScript. As I was working on a project, I encountered a small issue: function myFunction() { var sheet = SpreadsheetApp.getActiveSheet(); var data = sheet.getDataRange().getValues(); fo ...