Skip to content

Clock: Consider changing getElapsedTime behavior #30497

@AlaricBaraou

Description

@AlaricBaraou

Description

Over the years, I came across multiple projects that were calling .getElapsedTime() and messing up their deltas instead of reading the .elapsedTime property.

The core problem is that .getElapsedTime() does more than just "get" a value - it has the side effect of updating the internal state by calling .getDelta(). As much as the doc is relatively clear about it, users would typically expect a getter method to be pure and not modify state.

Here is a potential solutions to address this issue.

Solution

Rename the methods to better reflect their behavior ( example below )

    // Rename to make it clear this updates state, a bit of a mouth full but you get the idea
    updateAndGetElapsedTime() {  // formerly getElapsedTime()
        this.updateDelta();      // formerly getDelta()
        return this.elapsedTime;
    }

    getElapsedTime() {
        console.warn('getElapsedTime is deprecated, read the .elapsedTime property')
        return this.elapsedTime;
    }

    getDelta() {
        console.warn('getDelta is deprecated, call updateDelta instead')
    }

    // Rename to make it clear this updates state
    updateDelta() {              // formerly getDelta()
        let diff = 0;
        if (this.autoStart && !this.running) {
            this.start();
            return 0;
        }
        if (this.running) {
            const newTime = now();
            diff = (newTime - this.oldTime) / 1000;
            this.oldTime = newTime;
            this.elapsedTime += diff;
        }
        return diff;
    }

Alternatives

Maybe make the doc clearer, but I've seen this error made by advanced three.js developer so I'm not sure it would help.

Additional context

The issue / misconception being present in community project like Drei and React three fiber and Tres made me feel like it was time to talk about this,

pmndrs/react-three-fiber#3455
pmndrs/drei#2341
https://github.com/Tresjs/tres/pull/928/files

This is also misused in the official examples.
Here each call will call getDelta while a single getDelta followed by reading .elapsedTime would be more appropriate

capsule.position.y = Math.sin( clock.getElapsedTime() * 0.5 + capsule.position.x );
capsule.rotation.z = Math.sin( clock.getElapsedTime() * 0.5 ) * Math.PI * 1;

Same for these that while harmless because they are called immediately after, make unnecessary call to getDelta internally.
const delta = clock.getDelta();
const time = clock.getElapsedTime();

const delta = clock.getDelta();
const time = clock.getElapsedTime() * 10;

colorOffset.value += clock.getDelta() * colorRotationSpeed.value * timeScale.value;
const elapsedTime = clock.getElapsedTime();

Happy to eventually make a PR once the way to go is decided.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions