This project renders a full 3D scene in the browser using Three.js. There is no CSS animation involved. Every element is calculated mathematically on the GPU and drawn to a canvas on each frame.
The scene has three layers. A central 3D heart model loaded from a GLB file. Thousands of smaller heart-shaped particles orbiting around it. And a slow drift of snow particles behind everything for depth.
The orbiting particles are rendered using THREE.InstancedBufferGeometry which batches all of them into a single GPU draw call. Each particle gets its own speed, color and random seed passed as buffer attributes. Their movement is calculated inside a GLSL vertex shader using a uTime uniform and a parametric heart curve formula. That is why the particle cloud looks fluid rather than mechanical.
When the user clicks play the Web Audio API loads an MP3 and THREE.AudioAnalyser reads the average frequency on every frame. That number feeds directly into the animation loop and nudges particle speed and camera movement in sync with the music. The camera also follows the mouse using linear interpolation so it drifts smoothly rather than snapping.
You will learn how Three.js structures a 3D scene using a scene object, a camera and a renderer working together inside an animation loop. That pattern is the foundation of every Three.js project and this one gives you a complete working example with real complexity built on top of it.
You will understand what GLSL shaders are and why particle animation belongs on the GPU. The vertex shader controls where each point lands on screen. The fragment shader controls what color it gets. You will also learn the difference between a uniform and an attribute. A uniform like uTime is the same for every particle on a given frame. An attribute like speed or random seed is unique to each particle. That distinction is fundamental to writing any custom shader.
You will learn instanced rendering and why it matters for performance. You will also learn how to build an audio-reactive pipeline by reading frequency data from the Web Audio API and wiring it into a live animation loop.
Get an AI-generated overview or a file-by-file code breakdown. Ask questions about the project below the overview.
This project creates an interactive 3D web animation featuring a central, glowing heart model surrounded by thousands of dynamically animated heart-shaped particles and swirling "snow" particles. Its primary purpose is to deliver a visually engaging experience, enhanced by optional background music that makes the animation react to audio frequencies. Key features include a responsive 3D scene, mouse-driven camera parallax, and a "this is for you" message, all designed to evoke a warm, heartfelt sentiment.
The animation is built using Three.js, a powerful JavaScript 3D library, to render the scene on an HTML <canvas>. The core visual effects for the particles are defined using custom GLSL (OpenGL Shading Language) shaders, embedded directly within the HTML. These shaders dictate how each particle moves, changes size, and is colored over time, creating intricate, evolving patterns.
<canvas class="webgl"></canvas>
<h1 class="h1text">this is for you</h1>
<button id="play-music" type="button" aria-label="Play music">
<svg fill="currentColor" viewBox="0 0 512 512" width="100" title="music">
<!-- SVG path for music icon -->
</svg>
</button>This HTML snippet sets up the main canvas for the 3D rendering, a text overlay, and a button to control the background music.
The JavaScript code orchestrates the entire experience within a World class. It loads a central 3D heart model (a .glb file) and efficiently renders thousands of smaller particles using THREE.InstancedBufferGeometry. This technique allows many identical objects to be drawn with unique properties (like color, scale, and speed) using a single draw call, significantly boosting performance.
this.model = await this.loadObj("https://assets.codepen.io/74321/heart.glb");
this.model.scale.set(0.01, 0.01, 0.01);
this.model.material = new THREE.MeshMatcapMaterial({ /* ... */ });
this.scene.add(this.model);This JavaScript code loads the main 3D heart model, scales it, applies a material, and adds it to the Three.js scene.
The animation's dynamic nature is heavily reliant on the GLSL shaders. For instance, the main heart particles' movement is calculated in the vertexShader based on uTime (uniform time) and per-particle random attributes, making them follow a complex, heart-shaped path. The fragmentShader then determines their color and adds a glowing effect.
// From vertexShader
float sign = 2.0* (step(random, 0.5) -.5);
float t = sign*mod(-uTime * aSpeed* 0.005 + 10.0*aSpeed*aSpeed, M_PI);
// ... calculate heart shape offset ...
vec4 modelPosition = modelMatrix * vec4(displacedPosition.xyz, 1.0);This snippet from the vertex shader illustrates how uTime and unique per-particle attributes (random, aSpeed) are used to calculate the animated position of each particle, forming the dynamic heart shape.
A key interactive element is the music playback. When the user clicks the music button, an MP3 file is loaded, and THREE.AudioAnalyser is used to extract real-time frequency data from the audio. This data (this.data) then directly influences the animation loop, causing the particle speeds and camera movements to subtly react to the rhythm and intensity of the music, creating a synchronized audio-visual experience.
this.sound.setBuffer(buffer);
this.sound.setLoop(false);
this.sound.setVolume(0.5);
this.sound.play();
this.analyser = new THREE.AudioAnalyser(this.sound, 32);
// ...
this.data = this.analyser.getAverageFrequency();This JavaScript code loads and plays the background music, then sets up an AudioAnalyser to extract frequency data that will drive the visual responsiveness of the animation.
Have a question about how this project works? Ask below and get an answer based on the project code.
Structured technical breakdown for beginners. Anyone can generate or regenerate; your version is saved in this browser (local storage).
No breakdown yet. Generate one to see a file-by-file explanation. Your version is saved in this browser.
Comments
No comments yet. Sign in to leave a comment.
Sign in to leave a comment.