SERIES ARTICLE ·
From Wizard to Dragon – a Music Demo of Particles
How WebGL, GPU shaders and three live-generated tracks conduct a wizard made of millions of particles—until he transforms into a dragon at the drop.

Winamp's music visualizations used to be an essential part of listening to music for me. Even then, I was fascinated by the way sound could be translated into color and motion. Years later, I came across a modern WebGL demo online and that fascination returned. I wanted to revive some of that old feeling and find out for myself what is now possible directly in the browser. Gradually, the experiment became a wizard made of particles who conducts the music and transforms into a dragon at the drop.

The first particle experiment gradually became a figure with choreography of its own. Simulation, music and direction remain technically separate until the browser brings them together in one scene.

Three tracks are available at the start. They are not played back as audio files; the Web Audio API generates them directly in the browser. At the same time, each musical event is passed to the scene. A kick therefore moves not only the speaker cone, but also the camera, the wizard's robe and a wave of light.
A Figure Made of Up to 2.1 Million Particles
The wizard has no visible polygon model. An invisible, animated skeleton merely provides reference points for the robe, head, hat, arms and hands. The GPU distributes the particles across several emitters: some form the solid figure, while others become smoke, a train, arm trails, a wand or the glowing ring on the ground. At the beginning, the figure still lies on the floor as a golden disc and gradually rises from it.
The position and velocity of every point are stored in textures and updated with Three.js's GPUComputationRenderer. On desktop, the scene starts with 1024 by 1024 particles by default, or 1,048,576 points. On smartphones and tablets, it initially uses about 262,000 points and lowers the rendering resolution if performance remains poor.
How a Million Points Become a Figure
The simulation does not represent the particles as a million JavaScript objects. Instead, it stores their state in two floating-point textures: one contains position and age, while the other contains velocity and color value. Two fragment shaders read those textures in parallel, calculate the next state and write it into a second pair of textures. Source and destination are then swapped. This ping-pong process keeps the work on the GPU and avoids transferring every point back and forth between the processor and graphics card.
const gpu = new GPUComputationRenderer(size, size, renderer);
const position = gpu.addVariable('texturePosition', positionShader, positionStart);
const velocity = gpu.addVariable('textureVelocity', velocityShader, velocityStart);
gpu.setVariableDependencies(position, [position, velocity]);
gpu.setVariableDependencies(velocity, [position, velocity]);
// each frame: advance both states on the GPU
gpu.compute();A stable random value permanently assigns every point to an emitter and subtype. The shader therefore knows whether a point belongs to the robe, head, arm, smoke or wand trail. Short-lived shape particles are sampled again from the moving figure roughly four times per second; smoke and trails live longer and are allowed to drift away. At the drop, the same stable identity determines each particle's individual threshold for changing into the dragon. The wizard dissolves organically even though every point runs through the same shader.
Three Tracks Generated Live in the Browser
The current version contains three deliberately distinct pieces. Kick, hi-hats, clap or snare, bass, arpeggio and lead are built from oscillators, noise generators, filters and effects. Each track has its own tempo and musical idea, but follows the same dramatic structure.
| Track | Tempo | Character |
|---|---|---|
| Nachtfahrt | 126 BPM | 1980s action synth with a galloping bass line, arpeggio and heroic lead |
| Erdbeben | 140 BPM | Dubstep with a halftime feel, a supersaw melody and wobble bass in the drop |
| Sternenfeuer | 138 BPM | Anthemic trance with a rolling bass line, pluck arpeggio and supersaw lead |
Each arrangement spans 44 bars and lasts roughly 75 to 84 seconds, depending on its tempo. It moves through an intro, main section, breakdown, build-up, drop, a second main section and the outro. At the end, the wizard sinks back into the golden disc before the loop begins again.
A Sequencer Controls Sound and Image
The scene does not attempt to analyze finished audio after the fact. The sequencer already knows every step, note and section. While scheduling the audio, it also places events for the kick, clap, start of a bar, melody notes, build-up and drop into a queue. The animation loop retrieves those events in time with the actual audio output. This keeps sound and motion closely coupled.
if (k) {
kick(t, k);
push({ type: 'kick', t, v: k, beat: s % 4 === 0 });
}
const playTime = ctx.currentTime
- (ctx.outputLatency || 0)
- (ctx.baseLatency || 0);
while (queue.length && queue[0].t <= playTime) {
onEvent(queue.shift());
}| Event | Effect in the scene |
|---|---|
| Kick | A camera jolt, a wave across the floor, more movement in the robe and a bouncing hat |
| Melody note | The left hand follows the pitch; during the dragon sequence, accents control flight height and fire breath |
| Start of bar | A ring of light travels across the floor and the camera regularly changes its angle |
| Build-up | Arms, turbulence, brightness and camera proximity increase together |
| Drop | The wizard bursts upward and transforms into the dragon for eight bars |
From Wizard to Dragon
The transformation is the dramatic centerpiece. It would be less convincing if every particle jumped from one form to the other in the same frame, so each particle has its own threshold. At the drop, individual points switch first, followed by entire surfaces of the dragon shape. The wizard visibly dissolves while a head, body, wings, horns and fiery breath emerge.
uniform float uDragon;
bool inDragon(float h) {
return uDragon > 0.02 + 0.96 * fract(h * 29.71);
}The dragon's body follows the path previously traced by its head. That trail is sampled into 48 evenly spaced points along a spine. This creates a slender neck, a powerful torso and a long tapering tail without the need to animate a rigid dragon model. Colored bands travel across the body, the wings beat in rhythm and musical accents open the jaw for fire or smoke. During the final two beats, the dragon dives back to the ground; then the wizard rises from the disc once more.
What Holds the Effect Together
- The dark blue-violet sky changes with the music's filter curve and becomes warmer during the dragon phase.
- A second, softly rendered particle layer creates haze; bokeh lights, depth of field and bloom give the scene spatial depth.
- Chromatic aberration, vignette and film grain react more strongly to the kick, build-up and drop without completely overpowering the particles.
- Simple two-joint inverse kinematics keeps shoulders, elbows and hands connected even during rapid gestures.
- The camera slowly orbits the figure, changes its angle in time with the bars and adds short impulses on each kick.
- Distance is calculated from the viewing angle and aspect ratio so that the wizard and dragon remain as fully visible as possible even in a portrait-oriented browser.
What You Can Try for Yourself
You can switch between the three tracks at any time using the buttons. The stop button ends the music immediately and makes the wizard sink back into the disc; the space bar pauses and resumes playback. On mobile devices, the demo automatically adjusts the particle count, pixel density and framing to keep the performance as smooth and as fully visible as possible on smaller screens.
The dragon is not a second model. It is the same figure in a different state.
What the Particle Wizard demo taught me
The high particle count is striking, but it does not create the effect on its own. The choreography became the decisive element: the slow rise from the golden disc, the conducting hands, the changing camera angles and the transformation at the drop. Only then does the abstract smoke take on a readable role.
At the same time, the experiment shows how far a compact web application can go today. Shaders calculate movement and color, the Web Audio API assembles three complete pieces, and a shared event sequence conducts the music, figure, lighting and camera. In the production version, HTML, styling and program logic are separated, and even Three.js is served locally. It requires neither a game engine nor a prerendered video—only code that becomes audible and visible at the right moment.








