That’s not a question which can be answered globally, imo. The range of possible client devices is just too broad.
As a general, pragmatic strategy I’d suggest you define a frame rate, which you don’t want to fall below, like, for instance, 30 fps.
Maybe you can query the client’s capabilities and implement an adaptive approach in your app, by dropping successively some of your features in the sequence from “mere eye candy” to “indispensable core navigation”, until your minimum fps threshold is not breached anymore…
Features you might want to make “droppable” could include:
shadow casting and receiving
random object movements (sometimes barely noticeable creeping)
texture map lower resolutions (mip-maps?)
implemeting LODs, for large scenes
As a “tangible” feedback:
my 2014 iMac 5K
constantly runs its fans, with the GPU at a minimum load of 30% and CPU at 20%, even on your landing page without any user input like mouse movements, clicks, keystrokes. My machine maintains 60 fps throughout, but this load on idling is definitely too much, imo.
Thank you very much for the answer. Very useful and clear. The only part that remains unclear for me is “query the client’s capabilities”. I found this advice recommending to rely on FPS instead GPU/CPU/Memory usage. Is it right?
This gets me into a logical problem: I have a tick function in my app with the calculated delta time. I could use this information to implement the LOD system. But to calculate delta time I need, first of all, to load all the content and run the application in the browser, even on low-performance machines.
For example, to simplify, just related to texture resolution, something like:
// load hi-res textures at start
let lodProfile = 1
if (deltaTime >= 32)
// dispose loaded textures
// load the textures corresponding to the current LOD profile
I can’t give you a piece of code on that, but Three.js supports a “stats.module.js” which gives you a live bar-graph-like history of the current and past frame rate. See this example on three.js. The frame rate is nothing more (or less) than the inverse of “time per frame”, like you are suggesting yourself. You could compare in its source code how they collect that information.
LOD stands for “level of detail”, which is a technique used to draw objects which are far away from the viewer/camera with low(er) detail than objects which are close to the camera. There is an example on three.js on how to do that. It has nothing to do with the overall time consumption of the scene as a whole but rather with a depth sort.
You are right in your observation that my initial proposal bears the risk of overloading weak clients initially by relaxing only “after the fact”. You could of course go the other way around: start easy, and increase load/features as long as you don’t see a drop in frame rate below your self-defined threshold.