跪拜 Guibai
← All articles
Three.js · WebGL · Vue.js

Faking an Infinite Runner in Three.js with a Moving Ground Plane and Fog

By Tony费 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

The infinite-runner illusion is a common game and visualization pattern, but most tutorials move the camera or the character. Keeping both fixed and scrolling only the environment is simpler to control, avoids coordinate drift, and pairs naturally with fog to hide the seam — a compact, production-ready pattern for any WebGL project that needs an endless scrolling surface.

Summary

A character model plays its built-in running animation on a fixed spot while a massive 16,000-unit grass plane shifts backward every frame. Once the plane reaches a -500 offset, its position resets to zero, creating a seamless, infinite runway without moving the camera or the model. The grass texture tiles 64 times in each direction using RepeatWrapping to avoid visible stretching across the large surface.

Linear white fog with a near distance of 280 and a far distance of 1,050 fades the distant ground into white, masking the plane's hard edge and adding depth. A spotlight casts shadows, a hemisphere light fills the scene, and the renderer adapts to window resizes and device pixel ratios.

The full Vue component includes a resource disposal method that cancels the animation frame, traverses the scene to dispose of geometries and materials, and forces a WebGL context loss to prevent memory leaks when the component unmounts.

Takeaways
A 16,000×16,000 ground plane scrolls backward at 5 units per frame while the character stays in place, creating the illusion of forward motion.
The ground position resets to zero when it hits -500, forming a seamless loop that never exposes the plane's edge.
Grass texture repeats 64 times in both directions with RepeatWrapping to prevent stretching across the oversized plane.
Linear fog (near 280, far 1,050) fades the distant ground to white, hiding the plane's hard boundary and adding depth.
A full resource disposal routine cancels the animation frame, disposes of all geometries and materials, and forces WebGL context loss to avoid memory leaks on unmount.
The character's skeletal animation runs via AnimationMixer and clipAction, driven by a Three.js Clock for frame-rate-independent updates.
Conclusions

The decision to move the ground instead of the character or camera keeps the animation loop dead simple: one position update per frame with a hard reset, no accumulation of floating-point errors over long runs.

Using a 16,000-unit plane with a 500-unit scroll window is overkill for the visual effect, but it guarantees the reset is never visible even if the frame rate drops or the scroll speed changes — a brute-force approach that eliminates edge cases.

The fog does double duty: it's an aesthetic choice that also solves the engineering problem of hiding the plane's boundary, turning a visual limitation into a deliberate stylistic feature.

Concepts & terms
AnimationMixer
A Three.js object that plays and controls skeletal animations stored in a loaded model. It takes animation clips and updates bone transforms each frame based on a time delta.
RepeatWrapping
A texture wrapping mode in WebGL that repeats the texture image when UV coordinates exceed the 0–1 range, used here to tile a grass texture across a very large plane without loading a massive image.
Linear Fog
A fog model where visibility decreases linearly between a near and far distance from the camera. Objects closer than the near distance are fully clear; objects beyond the far distance are completely obscured.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗