I'm working on a Three.js project with a Tamagotchi-style animated character using GLTF animations and an animation mixer. The project includes a robot, a hatching box, and interactive controls.
Everything runs smoothly until the browser tab is idle for a few minutes. When I return to the window the scene appears frozen and the robot stops animating.
My animation loop is in the class Experience.js
:
this.time.on('tick', () => {
this.update();
});
update() {
const deltaTime = this.time.delta;
this.world.update(deltaTime);
this.camera.update();
this.renderer.update();
}
In the class World.js
the update is:
update(deltaTime) {
if (this.box) {
this.box.update(deltaTime)
}
if (this.robot) {
this.robot.update(deltaTime)
}
}
Inside the Box.js
:
update(deltaTime) {
if (this.mixer) {
this.mixer.update(deltaTime)
}
}
In the Robot.js
:
update(deltaTime) {
if (this.tamagotchiController) {
this.tamagotchiController.update(deltaTime)
}
}
Animation updating in TamagotchiController.js
:
update(deltaTime) {
if (this.mixer) {
this.mixer.update(deltaTime);
}
}
Finally the Camera.js
update is:
update() {
if (this.controls) {
this.controls.update()
}
}
and the Renderer.js
:
update() {
this.instance.render(this.scene, this.camera.instance)
}
The live project is here
I have no idea what is going wrong because no errors appear on the console.
Here is the Time.js
used in the project:
import EventEmitter from './EventEmitter.js'
export default class Time extends EventEmitter {
constructor() {
super()
// Setup
this.start = Date.now()
this.current = this.start
this.elapsed = 0
this.delta = 0.016 // Initialize with a typical frame time in seconds
window.requestAnimationFrame(() => {
this.tick()
})
}
tick() {
const currentTime = Date.now()
this.delta = (currentTime - this.current) / 1000 // Convert milliseconds to seconds
this.current = currentTime
this.elapsed = (this.current - this.start) / 1000 // Convert milliseconds to seconds
this.trigger('tick')
window.requestAnimationFrame(() => {
this.tick()
})
}
}
Thanks for the reply. It turns out that the problem was not related to the Time.js
class or the other classes' update methods. I'm working on a Tamagotchi-style robot character using the Expressive Robot from the Three.js examples and an animation mixer. The robot performs actions like feeding, playing, and cleaning. I wrote methods that create waste objects generated periodically, which the user can clean like the classic Tamagotchi game.
The scene froze after the browser tab was idle because the reset()
method (of the class creating the waste) restarted the waste creation loop without clearing the previous interval. This caused multiple intervals to stack, creating excessive waste objects and freezing the scene.
Old code (from reset()
):
this.wasteObjects.forEach(waste => this.scene.remove(waste));
this.wasteObjects = [];
this.startWasteCreation(); // Restarted without clearing old interval
The problem was that startWasteCreation()
was called without clearing the previous interval. So overlapping intervals created too many waste objects over time, causing performance issues.
New code:
if (this.wasteCreationInterval) {
clearInterval(this.wasteCreationInterval); // Clear previous interval
}
this.wasteObjects.forEach(waste => this.scene.remove(waste));
this.wasteObjects = [];
this.startWasteCreation(); // Restart safely
Now the scene no longer freezes after the tab is idle.