HOW TO PROGRAM A
COMET PARTICLE SYSTEM IN THREE.JS
Create stunning comet-tail effects in Three.js with custom geometry, materials, and animation. The comet streaking behind this page is the live demo — copy the code or launch it in one click.
LIVE DEMO
The comet streaking behind this page is a live Three.js particle system. Launch the full-screen demo to orbit around it — drag to rotate, scroll to zoom. On a phone, drag with one finger.
Drag to orbit · scroll to zoom · Esc to exit
FEATURED IN GAME
EducationalPlanet Quest: Solar System Explorer
See this comet particle effect in a real game: pilot your ship past the Sun, all 8 planets and a blazing comet in a free 3D space adventure that teaches the solar system.
Play Planet QuestWANT TO BLOW STUFF UP?
From a comet tail to a full detonation: build a 3D rockfire explosion in Three.js — flying debris rocks, fire, smoke, a shockwave ring, and camera shake. The whole blast in one copy-paste file, with a live demo you can orbit.
See the Rockfire Explosion TutorialIntroduction
Three.js is a JavaScript library that opens up a world of possibilities for creating mesmerizing 3D animations on the web. One of its most eye-catching uses is a comet particle system: thousands of points streaming through space, leaving behind a glowing tail reminiscent of a real comet. With a single THREE.Points cloud backed by a BufferGeometry, you can draw the entire effect in one GPU call and animate it by editing typed arrays each frame.
How the Comet Is Built
Step 1: Set up the scene, camera and renderer
Create a THREE.Scene, a PerspectiveCamera and a transparent WebGLRenderer with the pixel ratio capped at 2 (1.5 on phones), then add OrbitControls with slow auto-rotate so the comet drifts behind the page.
Step 2: Add a nebula and starfield
Build one THREE.Points cloud of colour-tinted, twinkling stars on a distant sphere, then scatter soft nebula sprites whose cloud textures are generated once from fractal value noise on a canvas. Use additive glow blending and let each nebula slowly spin.
This is the same sky technique used in the Planet Quest solar system game: thousands of tinted, twinkling stars plus slowly spinning nebula clouds, all in a handful of draw calls so the comet keeps its frame rate.
// Planet Quest style sky: tinted twinkling stars + noise-cloud nebula sprites
const sky = new THREE.Group(); scene.add(sky);
const glow = { blending: THREE.AdditiveBlending, transparent: true, depthWrite: false };
function cloudTex(seed, S = 256) { // fractal value noise, soft round falloff
const cv = document.createElement('canvas'); cv.width = cv.height = S;
const g = cv.getContext('2d'), img = g.createImageData(S, S);
const hs = (x, y) => { const h = Math.sin(x * 127.1 + y * 311.7 + seed * 74.7) * 43758.5453; return h - Math.floor(h); };
const vn = (x, y) => { const xi = Math.floor(x), yi = Math.floor(y), xf = x - xi, yf = y - yi,
u = xf * xf * (3 - 2 * xf), v = yf * yf * (3 - 2 * yf);
return (hs(xi, yi) * (1 - u) + hs(xi + 1, yi) * u) * (1 - v) + (hs(xi, yi + 1) * (1 - u) + hs(xi + 1, yi + 1) * u) * v; };
for (let y = 0; y < S; y++) for (let x = 0; x < S; x++) {
let f = 0, a = 0.5, fr = 4 / S;
for (let o = 0; o < 5; o++) { f += a * vn(x * fr, y * fr); a *= 0.5; fr *= 2; }
const dx = x / S - 0.5, dy = y / S - 0.5, fall = Math.max(0, 1 - Math.hypot(dx, dy) * 2);
const k = (y * S + x) * 4; img.data[k] = img.data[k + 1] = img.data[k + 2] = 255;
img.data[k + 3] = 255 * Math.min(1, Math.max(0, (f - 0.35) * 1.8)) * fall * fall;
}
g.putImageData(img, 0, 0); return new THREE.CanvasTexture(cv);
}
const texs = [cloudTex(1), cloudTex(2), cloudTex(3)];
const cols = [0x7a4cff, 0x2f8fd8, 0xd04c9a, 0x2fc4b0, 0x5a6cff];
for (let i = 0; i < 26; i++) {
const m = new THREE.SpriteMaterial({ map: texs[i % 3], color: cols[i % cols.length], opacity: 1.6 + Math.random() * 0.8, ...glow });
m.rotation = Math.random() * Math.PI * 2;
const s = new THREE.Sprite(m); s.scale.setScalar(260 + Math.random() * 300);
const az = Math.random() * Math.PI * 2, el = (Math.random() - 0.5) * 1.3;
s.position.set(Math.cos(az) * Math.cos(el), Math.sin(el), Math.sin(az) * Math.cos(el)).multiplyScalar(450);
s.userData.spin = (Math.random() - 0.5) * 0.02; sky.add(s);
}
// in animate(): sky.children.forEach(s => { if (s.isSprite) s.material.rotation += s.userData.spin * dt; });
Step 3: Shape the rocky nucleus
Displace the vertices of a low-detail IcosahedronGeometry with 3D value noise and craters, then light it inside a ShaderMaterial with a fixed sun direction and a Fresnel rim, so no scene lights are needed.
Step 4: Emit the dust tail, ion tail and debris
Store position, velocity and life for every particle in typed arrays on one BufferGeometry. Dust drifts away from the sun and curves sideways, the blue ion tail streams straight away from the sun, and debris sparkles as it tumbles off.
Step 5: Add the glowing coma
Draw three large soft point sprites at the nucleus with additive blending to fake a bloom-like coma that pulses gently.
Step 6: Animate the particles every frame
In the render loop, step each particle, respawn it at the nucleus when its life runs out, upload the changed buffers, rotate the nucleus and update the shader time uniforms.
Frequently Asked Questions
How do you create a comet particle trail in Three.js?
Create a particle system using THREE.Points with a custom BufferGeometry. Assign each particle a velocity and lifetime. On each frame, move particles outward from the comet head while fading their opacity as they age. Reset expired particles at the comet origin to create a continuous trail effect.
What geometry and material are used for particle systems in Three.js?
Use THREE.BufferGeometry with position, color, and size attributes stored in Float32Array buffers. For the material, use THREE.PointsMaterial with size, vertexColors, transparent, and blending properties. AdditiveBlending creates the glowing effect typical of comet tails and fire particles.
How do you animate particles with velocity in Three.js?
Store velocity vectors for each particle in a separate array. In the animation loop, add each particle's velocity to its position every frame. Apply damping (multiply velocity by 0.98) to slow particles over time, and add gravity or random forces for more natural movement. Mark the position attribute as needsUpdate after modifying.
How do you create a glow effect on Three.js particles?
Use THREE.AdditiveBlending on the PointsMaterial, which makes overlapping particles brighter rather than opaque. Combine this with a circular gradient texture (bright center fading to transparent edges) applied as a point sprite map. The result is soft, luminous particles that naturally glow when clustered together.
How do I make a realistic comet tail in Three.js?
Use one THREE.Points system with a custom ShaderMaterial and two particle populations: a slow dust tail pushed away from the sun that curves sideways as it ages (yellow-white), and a fast, straight blue ion tail pointing directly away from the sun. Fade each particle by its remaining life and draw with additive blending.
Why does a comet have two tails?
The dust tail is made of grains pushed gently by sunlight, so it lags behind the comet and curves along its orbit. The ion tail is gas ionised by the Sun and carried by the solar wind, so it is blue and points straight away from the Sun.
How do I make a rocky comet nucleus in Three.js without lights?
Displace the vertices of a low-detail IcosahedronGeometry with 3D noise, then shade it in a ShaderMaterial with a dot product against a fixed sun direction plus a Fresnel rim for the bright sunlit edge. No PointLights are needed.