Frontend Development 12 min read

Implementing a 3D Automotive Radar Visualization with three.js

This article walks through building a 3D automotive radar effect using three.js, covering background image loading, fog integration, car model import, radar sector creation with CircleGeometry and Line2, dynamic tween animations, and road texture scrolling to simulate vehicle motion.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Implementing a 3D Automotive Radar Visualization with three.js

The article introduces a 3D automotive radar visualization project built with three.js, Node.js, and Vite, providing the source repository link.

Inspiration

Images of the desired effect are shown to illustrate the final appearance.

Technology Stack

three.js 0.157.0

nodejs v18.19.0

vite 4.3.2

Implementation Overview

Background Image

A custom background is created using THREE.TextureLoader and applied to a THREE.MeshPhongMaterial . The texture is repeated and added to the scene as a ground plane.

const createGround = () => {
  const textureLoader = new THREE.TextureLoader();
  const material = new THREE.MeshPhongMaterial({
    color: 0xFFFFFF,
    transparent: true,
    opacity: 1
  });
  ground = new THREE.Mesh(new THREE.BoxGeometry(18, 0, 640, 1, 1, 1), material);
  textureLoader.load(`${import.meta.env.VITE_ASSETS_URL}/assets/images/背景图.png`, function (texture) {
    texture.wrapS = THREE.RepeatWrapping;
    texture.wrapT = THREE.RepeatWrapping;
    texture.repeat.set(1, 12);
    ground.material.map = texture;
    ground.material.needsUpdate = true;
    ground.rotation.copy(new THREE.Euler(-Math.PI, -Math.PI * 0.5, 0));
    scene.add(ground);
  });
};

Fog is added to the scene via scene.fog to soften distant objects.

Fog Explanation

The Fog class defines atmospheric fog that affects all objects in the scene.

Loading the Car Model

const gltf = await loadGltf(`${import.meta.env.VITE_ASSETS_URL}/assets/models/car/scene.gltf`);
const model = gltf.scene;
const playerScale = 0.6;
model.scale.set(playerScale, playerScale, playerScale);
playerGroup.add(model);
scene.add(playerGroup);

Radar Wave Creation

The radar effect is a semi‑transparent sector built with THREE.CircleGeometry and a surrounding line created from Line2 . The method accepts radius and thetaLength parameters.

const createRadar = (radius: number, thetaLength: number) => {
  const geometry = new THREE.CircleGeometry(radius, 361, -thetaLength / 2, thetaLength);
  const material = new THREE.MeshBasicMaterial({
    color: 0xffffff,
    side: THREE.DoubleSide,
    transparent: true,
    opacity: 0.5,
  });
  const circle = new THREE.Mesh(geometry, material);
  circle.rotation.set(Math.PI * 0.5, 0, 0);
  const position = geometry.getAttribute('position');
  const { count } = position;
  const linePoints = [];
  for (let i = 1; i < count; i++) {
    const v3 = new THREE.Vector3().fromBufferAttribute(position, i);
    linePoints.push(...v3.toArray());
  }
  const line2Geometry = new LineGeometry();
  line2Geometry.setPositions(linePoints);
  const matLine = new LineMaterial({
    linewidth: 0.002,
    dashed: true,
    opacity: 1,
    color: 0xffffff,
    vertexColors: false,
  });
  const line2 = new Line2(line2Geometry, matLine);
  circle.add(line2);
  scene.add(circle);
};

Radar Diffusion Animation

The radar sector is animated by tweening radius and/or thetaLength using TWEEN.Tween . Different animation types ("radius", "thetaLength", or both) are supported.

const createRadar = (radius: number, thetaLength: number, index?: number, type?: "radius" | "thetaLength") => {
  const InitialThetaLength = Math.PI * 0.01;
  const InitialRadiue = 0.001;
  const tweenTime = (type === 'radius' ? 2 : thetaLength) * 1000;
  new TWEEN.Tween({ radius: InitialRadiue, thetaLength: InitialThetaLength })
    .to({ radius, thetaLength }, tweenTime)
    .delay(1000 * 5 - tweenTime)
    .start()
    .onUpdate(({ radius: r, thetaLength: t }) => {
      let newGeometry: THREE.CircleGeometry;
      if (type === 'radius') {
        newGeometry = new THREE.CircleGeometry(r, 361, -thetaLength / 2, thetaLength);
      } else if (type === 'thetaLength') {
        newGeometry = new THREE.CircleGeometry(radius, 361, -t / 2, t);
      } else {
        newGeometry = new THREE.CircleGeometry(r, 361, -t / 2, t);
      }
      circle.geometry.setAttribute('position', newGeometry.getAttribute('position'));
      const linePoints = geometryAttribute2Array(newGeometry);
      line2Geometry.setPositions(linePoints);
    })
    .onComplete(() => {});
};

Other Vehicles

Multiple additional car models are cloned, positioned on predefined lane offsets, and given a yoyo tween for slight back‑and‑forth motion.

let ZP = [0, -1.8, -3.8, 4.4, 6.3, 8.2];
for (let i = 0; i < 10; i++) {
  const OC = otherCarModel.clone();
  const x = getRandomIntegerInRange(-10, 0);
  const z = ZP[getRandomIntegerInRange(0, ZP.length - 1)];
  if (z <= 0) {
    OC.rotation.set(0, -Math.PI * 0.5, 0);
  }
  const group = new THREE.Group();
  group.add(OC);
  group.position.copy(new THREE.Vector3(x - i * size.x, 0, z));
  yoyoTweene(OC);
  otherCarGroup.add(group);
}

Vehicle Motion

Each vehicle receives a yoyo tween that moves it along the X axis to simulate uneven speed.

const yoyoTweene = (mesh: THREE.Object3D) => {
  const x = getRandomIntegerInRange(-3, 3);
  return new TWEEN.Tween({ x })
    .to({ x: 3 }, 3000)
    .yoyo(true)
    .repeat(Infinity)
    .start()
    .onUpdate(({ x }) => {
      if (mesh) mesh.position.setX(x);
    });
};

Ground Texture Scrolling

By adjusting the texture offset over time, the road appears to move backward, creating the illusion of forward vehicle motion.

const loopGround = () => {
  new TWEEN.Tween({ offsetX: 0.12 })
    .to({ offsetX: 1.12 }, 3000)
    .repeat(Infinity)
    .onUpdate(({ offsetX }) => {
      ground.material.map.offset.set(0, offsetX);
    })
    .start();
};

The article concludes with a note that the opposite‑direction lane vehicles appear to move backward, inviting readers to experiment with solutions.

frontendanimationJavaScriptThree.jsWebGLradar visualization
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.