How to Use MathUtils.pingpong in Three.js

In Three.js, creating smooth, back-and-forth animations often requires oscillating values. This article explains how to utilize the MathUtils.pingpong function to generate these oscillating values over time, complete with practical code examples showing how to apply this motion to 3D object properties like position, rotation, and scale.

Understanding MathUtils.pingpong

The THREE.MathUtils.pingpong utility function is designed to bounce a value back and forth between 0 and a specified maximum limit. It behaves like a wave, constantly incrementing up to the limit and then reversing direction back to zero.

The syntax for the function is:

THREE.MathUtils.pingpong(x, length);

Implementing Pingpong in the Animation Loop

To create continuous oscillation over time, you should use MathUtils.pingpong inside your active render/animation loop alongside a THREE.Clock to track elapsed time.

Here is a basic implementation:

import * as THREE from 'three';

// Set up scene, camera, renderer, and mesh
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

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

camera.position.z = 5;

// Initialize the Clock to track elapsed time
const clock = new THREE.Clock();

function animate() {
    requestAnimationFrame(animate);

    const time = clock.getElapsedTime();

    // Oscillate the Y position between 0 and 2
    cube.position.y = THREE.MathUtils.pingpong(time, 2);

    renderer.render(scene, camera);
}

animate();

Controlling Speed and Range

By default, the oscillation speed is dictated directly by the elapsed seconds, and the value only moves in a positive range starting from 0. You can easily customize both the speed and the range of the oscillation.

Adjusting Speed

To speed up or slow down the animation, multiply the time variable before passing it into the function:

const speed = 2.0; // Double the speed
cube.position.y = THREE.MathUtils.pingpong(time * speed, 2);

Changing the Range (Offsetting)

Because pingpong always returns a value between 0 and length, you must apply mathematical offsets if you want the value to oscillate into negative space (e.g., between -1 and 1).

Subtract half of the maximum length from the final result to center the oscillation around zero:

const length = 2;
// Oscillates between 0 and 2, then subtracts 1 to oscillate between -1 and 1
cube.position.y = THREE.MathUtils.pingpong(time, length) - (length / 2);