Many pages in the documentation contain live examples, which are really useful initially to understand the concepts behind the described classes.
But even after I'm finished playing with them, they continue to continuously re-render (sometimes even the same still frame) and waste energy by hogging the CPU and GPU. This is especially noticeable on a laptop, where the used energy raises the temperature and reduces battery life. Even more critical, this will sooner or later trigger CPU throttling, which interferes with estimating the performance of my own code while leaving the documentation open in another browser window.
The simplest solution would be to add a button to pause the render loop of the examples.
A more elegant solution would be to render on-demand, when the user changes one of the controls. The render loop would only run free when one of the controls explicitly enabled an animation. This is less likely to be copy-paste-able into every example, like a generic pause button.
My ideal solution would have the documentation available as documents, as opposed to being locked up in a SPA. The problem would then not even arise unless I enable JavaScript for the threejs.org domain. Unfortunately, I don't know how much effort this is. This could be as trivial as replacing the # in the URLs with /. Or it could require changing the entire concept of how the documentation is presented.
Could you elaborate how are you leaving the docs page running? In another window behind the window you're using.. in a minimized window.. or in another tab?
A more elegant solution would be to render on-demand, when the user changes one of the controls.
In many examples this is the case, i.e. if the result only changes on interaction, we only re-render after interaction. I think we are open to fixing examples that needlessly re-render, but if the example is demonstrating timed animation that's another matter.
My ideal solution would have the documentation available as documents, as opposed to being locked up in a SPA.
Do you mean, you wish the documentation didn't have live examples in it, like this page? Or that you wish the documentation were in some medium other than a website? I don't think I understand how demos on pages you haven't opened would affect you while you read the documentation. The site is not a SPA in the traditional sense — demos are in iframes, they won't be playing in the background once you navigate to another iframe.
Could you elaborate how are you leaving the docs page running? In another window behind the window you're using.. in a minimized window.. or in another tab?
In another window. Luckily, browsers drastically drop the frame rate of requestAnimationFrame() when minimized or when showing a different tab.
In many examples this is the case, i.e. if the result only changes on interaction, we only re-render after interaction. I think we are open to fixing examples that needlessly re-render, but if the example is demonstrating timed animation that's another matter.
That's great news! The example where I noticed the problem of re-rendering the same frame is SkinnedMesh. Even though is has a checkbox "Animate Bones" to control an animation, it's render function starts with:
function render() {
requestAnimationFrame( render );
The other part of the problem are all the *Geometry pages. They immediately start rotating their model, without providing any way to stop the animation.
Do you mean, you wish the documentation didn't have live examples in it, like this page? Or that you wish the documentation were in some medium other than a website?
I mean that reading the documentation should be possible without enabling JavaScript. (See Progressive enhancement for the general philosophy behind that.) Regarding this particular issue, being functional without JavaScript would allow any user to stop all the live examples at once with a click or two.
For me, there was a more or less defined point in time, where I stopped reading the documentation for understanding, and started using it more to look up details while writing my own code. That would be the point where I would like to just disable all the live examples at once. Actually, that's also the point at which the CPU usage first started annoying me. The live examples were certainly worth it before that.
Unfortunately, disabling JavaScript with the current documentation results in a mostly empty page. Not even the list of topics is shown on the left side.
Sorry, but I don't think we should complicate things and instead keep the documentation simple. Additional controls that allow to start/stop the animation seem over-engineering to me. The fact that animated demos are more expensive to render than on-demand rendering is an acceptable tradeoff.
I mean that reading the documentation should be possible without enabling JavaScript.
Support the documentation with disabled JS is another topic and unrelated to the issue's title. This request can only be solved by developing a new documentation from scratch. And I doubt it's worth doing so.
I think we could get rid of the animation and only render when controls update.
I'm fine with that, too 👍
Support the documentation with disabled JS is another topic and unrelated to the issue's title. This request can only be solved by developing a new documentation from scratch. And I doubt it's worth doing so.
Yes. I mainly mentioned it here because if addressing progressive enhancement were somewhere on the roadmap, this issue would be solved as part of that, and there would be no need to develop a dedicated solution which solves only this problem.
I think we could get rid of the animation and only render when controls update.
I've realized today that this will cause some serious refactoring in the material, geometry and bones browser since rendering when the controls update is not enough. It's also necessary to render when the scene configuration changes via dat.gui. Especially the material browser will need a lot of new code in order to detect all redraw events.
In that regard, the code will be easier if the docs just keep the animation loop.
What if we give the scene's iframe a "Play Example" button and don't load the example content until the user clicks it?
I wonder if the reported battery issue is a problem if the users scrolls down a bit so the actual WebGL scene is not visible in the viewport anymore. I mean if the user actually reads the documentation of properties and methods, the WebGL scene should not be in the viewport.
A quick test in Firefox (79.0 64-bit) seems to indicate that the scene being not visible in the viewport had no impact on this. The Performance tab of Dev Tools showed requestAnimationFrame being called while it was both on and off screen.
In contrast, the same test in Chrome (84.0.4147.135 64-bit) showed _no_ frames being rendered while the iframe was out of the viewport (performance test tab is not the same between these two, but enough to see what was going on under the hood).
In short, browser behavior is a factor here. I didn't check to see if Firefox has an option to change this behavior.
I personally think this is best solved on a case by case basis for end users.
I have implemented similar in the past, and once you get into it you realize that making a heuristic for determining whether to re-render, is pretty tricky... For instance controls damping wont damp to absolute zero, so it's easy to get truncated camera panning and animation tweens if you don't tune the values carefully.
For the samples, they could perhaps use a different form of setAnimationLoop designed for demos, that only renders if the canvas is in the foreground, or stops rendering after a certain amount of time without input.
Perhaps a OnDemandRenderLoop.js type thing that installs the appropriate event handlers for the Page Visibility API https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API, or checks Document.hidden / Document.visibilityState before calling the render function, could work here.
I agree this is an important issue and that it is best to be solved on a case by case basis. Some examples have to be rendered continuously, while others are just wasting energy.
I was recently developing some optimizations for TrackballControls while running trackball example for testing. Not only this made my laptop very hot, it also exhausted my battery quickly. Inspired by this experience I tried to optimize trackball example in #20335. Rendering on control change event is straightforward, but in this particular example I noticed a couple of issues:
Examples that use controls with inertia/damping require controls.update() function to be called continuously until inert energy is exhausted. This can be achieved by keeping an animation loop exclusively for controls.update(). @Mugen87 found this confusing.
Running controls.update() from animation loop while simultaneously interacting with controls causes change event to fire at 120Hz - twice per frame (once from pointer event handler, and another time from controls.update() invocation)
What would be the best solution in this case? Should we disable inertia/damping or develop a more robust solution?
A solution that I found that worked elegantly in one of my prototypes was to create an internal animation loop that all sorts of tools can add/remove themselves into. For example TrackballControls can do internally something like this pseudocode:
if ( hasInertia ) AnimationLoop.add( this.update );
// ...
update() {
// ...
if ( ! hasInertia ) AnimationLoop.remove( this.update );
this.dispatchEvent( changeEvent );
}
This allows tools to jump in and out animation loop as they need without user having to worry about rAF, update frequency etc. This AnimationLoop can be a singleton and does not have to run continuously all the time - it can be optimized to start/stop only when there is something in the queue.
Most helpful comment
A quick test in Firefox (79.0 64-bit) seems to indicate that the scene being not visible in the viewport had no impact on this. The Performance tab of Dev Tools showed
requestAnimationFramebeing called while it was both on and off screen.In contrast, the same test in Chrome (84.0.4147.135 64-bit) showed _no_ frames being rendered while the iframe was out of the viewport (performance test tab is not the same between these two, but enough to see what was going on under the hood).
In short, browser behavior is a factor here. I didn't check to see if Firefox has an option to change this behavior.