Frontend Development 21 min read

Advanced Three.js Hero Section with Shaders, Particles, Lighting, and UI

This tutorial walks through building an advanced hero section using Three.js, covering prerequisites, loading and animating a robot model, configuring lighting, creating particle effects, adding UI elements, applying a radial gradient background, implementing parallax interaction, and rendering flowing lines for a polished WebGL experience.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Advanced Three.js Hero Section with Shaders, Particles, Lighting, and UI

Before diving into advanced Three.js and Shader (GLSL) topics, ensure you are familiar with core Three.js concepts such as Scene, Camera, Renderer, Geometry, Material, and Mesh, and have a solid grasp of GLSL syntax for vertex and fragment shaders.

0. Prerequisites

Familiarity with Three.js basics and GLSL shader programming is required. Recommended resources include the Three.js journey course by Bruno Simon and various 3D model repositories.

1. Hero Section Overview

The hero section provides an immediate visual impact, often featuring a static robot that gently oscillates, surrounded by particles of varying sizes and a sweeping line across the screen.

2. Basic Scene Setup

Set up the scene by creating a static robot model, adding three particle size groups, and a line that moves from left to right.

2.1 Basic Scene Construction

Refer to previous articles for detailed scene setup or use AI tools to reproduce the layout.

2.2 Robot Resource Acquisition

Obtain 3D robot assets from sites like MeshyAI, Hyper3D, or Tencent Hunyuan, then animate them using Mixamo by uploading an FBX or OBJ file.

this.loader = new GLTFLoader();
this.loader.load('path/to/robot.glb', (gltf) => {
  this.model = gltf.scene;
  // Adjust model size
  this.model.scale.set(0.02, 0.02, 0.02);
  // Set model position
  this.model.position.set(0, -2.81, 0);
  // Add model to scene
  this.scene.add(this.model);
});

2.3 Loading the Robot Model

Use GLTFLoader to load robot.glb , then adjust its scale and position to fit the hero layout.

Step 2: Play Robot Animation

Configure an idle animation using AnimationMixer and play the first available clip.

// Set up animation mixer
this.animation = {
  mixer: new THREE.AnimationMixer(this.model),
  actions: {},
  animations: this.resources.items.sintRobot.animations
};
// Create actions for each clip
this.animation.animations.forEach(clip => {
  this.animation.actions[clip.name] = this.animation.mixer.clipAction(clip);
});
// Play the first animation
if (this.animation.animations.length > 0) {
  this.currentAction = this.animation.actions[this.animation.animations[0].name];
  this.currentAction.play();
}

Step 3: Update Animation Frames

Update the mixer inside requestAnimationFrame to ensure smooth playback.

if (this.animation.mixer) {
  this.animation.mixer.update(this.time.delta);
}

2.4 Lighting Setup

Three directional lights and an ambient light are added to enhance depth and realism.

// Directional Light 1
this.directionalLight1 = new THREE.DirectionalLight();
this.directionalLight1.position.set(1, 2, 2);
this.directionalLight1.color.setRGB(0.13, 0.09, 0.15);
this.directionalLight1.intensity = 3;
this.scene.add(this.directionalLight1);

// Directional Light 2
this.directionalLight2 = new THREE.DirectionalLight();
this.directionalLight2.position.set(-1, 3, 1);
this.directionalLight2.color.setRGB(0.45, 0.36, 0.22);
this.directionalLight2.intensity = 4.1;
this.scene.add(this.directionalLight2);

// Ambient Light
this.ambientLight = new THREE.AmbientLight();
this.ambientLight.color.setRGB(0.309, 0, 0.964);
this.ambientLight.intensity = 2;
this.scene.add(this.ambientLight);

3. Particle Effects

Create two sampling planes at z = 0.5 and z = -0.5, then generate particles using MeshSurfaceSampler to avoid intersecting the robot.

// Create sampling planes
const planeGeometry = new THREE.PlaneGeometry(2 * this.sizes.aspect, 2, 1, 1);
const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xFFFFFF, wireframe: true });
this.plane = new THREE.Mesh(planeGeometry, planeMaterial);
const clonedPlane = this.plane.clone();
clonedPlane.position.set(0, 0, -0.5);
this.scene.add(clonedPlane);
this.plane.position.set(0, 0, 0.5);
this.scene.add(this.plane);
this.plane.updateMatrixWorld();
clonedPlane.updateMatrixWorld();
// Initialize particle system with the two planes
this.geometryParticles = new ParticleSystem([this.plane, clonedPlane]);
this.plane.visible = false;
clonedPlane.visible = false;

Sample particle positions, noises, speeds, and sizes, then render them with a ShaderMaterial that manipulates vertex positions over time.

4. UI Integration

Add UI components on top of the canvas and set the Three.js renderer to use a transparent background.

this.instance = new THREE.WebGLRenderer({
  canvas: this.canvas,
  antialias: true,
  alpha: true // Enable transparent background
});
this.instance.setClearColor(0x000000, 0); // Fully transparent

Apply a radial gradient to the page background via CSS to enhance visual depth.

5. Parallax Effect

Capture normalized mouse coordinates and smoothly interpolate the camera's lookAt target to create a subtle parallax interaction.

window.addEventListener('mousemove', (event) => {
  this.normalizedMouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  this.normalizedMouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
});

const lookAtTarget = new THREE.Vector3(
  this.scene.position.x + this.normalizedMouse.x * this.parallax.factor * 0.25,
  this.scene.position.y + this.normalizedMouse.y * this.parallax.factor * 0.15,
  this.scene.position.z
);
if (!this.currentLookAtTarget) this.currentLookAtTarget = lookAtTarget.clone();
this.currentLookAtTarget.lerp(lookAtTarget, 0.07);
this.camera.lookAt(this.currentLookAtTarget);

6. Flowing Lines

Generate a curve, sample points, assign per‑vertex alpha values, and render with a custom ShaderMaterial that uses additive blending for a glowing line effect.

// Create line geometry from curve points
this.lineGeometry = new THREE.BufferGeometry().setFromPoints(this.curvePoints);
// Add alpha attribute for fading
const alphas = new Float32Array(this.curvePoints.length);
for (let i = 0; i < this.curvePoints.length; i++) {
  const t = i / (this.curvePoints.length - 1);
  alphas[i] = Math.sin(t * Math.PI) * this.parameters.lineOpacity;
}
this.lineGeometry.setAttribute('alpha', new THREE.BufferAttribute(alphas, 1));
// Shader material handling per‑vertex alpha
this.lineMaterial = new THREE.ShaderMaterial({
  vertexShader: `
    attribute float alpha;
    varying float vAlpha;
    void main() {
      vAlpha = alpha;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: `
    uniform vec3 color;
    varying float vAlpha;
    void main() {
      gl_FragColor = vec4(color, vAlpha);
    }
  `,
  uniforms: { color: { value: new THREE.Color(this.parameters.lineColor) } },
  transparent: true,
  depthWrite: false,
  blending: THREE.AdditiveBlending
});

7. Final Thoughts

With AI lowering the barrier to 3D asset creation, front‑end developers will increasingly incorporate sophisticated WebGL experiences. Three.js remains a top choice for WebGL development due to its ease of use, extensive features, and strong community support.

frontend developmentThree.jsWebGLShaderParticle System
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.