| name | globe-particles |
| description | Create a globe-like 3D particle visualization with a dense luminous spherical core and thinner orbital ring or flattened disc. Use when a design needs a premium planetary, orbital, synthesized data-globe effect rendered with real WebGL/Three.js particles, not generic starfields or full page layout changes. |
Globe Particles
Scope
- Apply only to a globe-like 3D particle visualization.
- Do not change full page layout, copy, or unrelated motion systems.
- Use for planetary, orbital, infrastructure, or synthesized data-globe effects.
- Keep the core neutral or white-hot and derive ring/glow accents from the design's primary color.
Visual Target
- Dense spherical core of luminous points.
- Thinner outer orbital ring or flattened disc around the sphere.
- Clear globe silhouette with tilt, depth, and layered particle density.
- Dark atmospheric background, restrained glow, clean structure, and subtle sci-fi depth.
- Premium and cinematic, not playful or noisy.
HTML And CSS
<div class="globe-particles-shell">
<canvas class="globe-particles-canvas" data-globe-particles></canvas>
</div>
.globe-particles-shell {
position: relative;
width: min(100%, 760px);
aspect-ratio: 1 / 1;
}
.globe-particles-canvas {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
display: block;
pointer-events: none;
}
Particle Shader
Use circular shader points so particles stay crisp and luminous.
const globeParticleVertex = `
attribute float a_size;
attribute float a_layer;
uniform float u_time;
uniform float u_pointSize;
varying float v_layer;
varying float v_depth;
varying float v_falloff;
void main() {
vec3 pos = position;
float breathe = 1.0 + sin(u_time * 0.65 + a_layer * 4.0) * 0.012;
pos *= breathe;
vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0);
gl_PointSize = u_pointSize * a_size * (1.0 / max(0.18, -mvPosition.z));
gl_Position = projectionMatrix * mvPosition;
v_layer = a_layer;
v_depth = smoothstep(-1.8, 1.8, pos.z);
v_falloff = smoothstep(2.45, 0.25, length(pos));
}
`;
const globeParticleFragment = `
precision highp float;
uniform vec3 u_coreColor;
uniform vec3 u_accentColor;
varying float v_layer;
varying float v_depth;
varying float v_falloff;
void main() {
vec2 uv = gl_PointCoord - 0.5;
float d = length(uv);
float alpha = smoothstep(0.5, 0.0, d);
alpha *= alpha;
vec3 color = mix(u_coreColor, u_accentColor, smoothstep(0.35, 1.0, v_layer));
color += vec3(1.0) * v_depth * 0.08;
color = mix(color * 0.42, color, clamp(v_falloff + v_layer * 0.28, 0.0, 1.0));
alpha *= mix(0.52, 1.0, clamp(v_falloff + v_layer * 0.24, 0.0, 1.0));
gl_FragColor = vec4(color, alpha);
}
`;
Three.js Recipe
import * as THREE from "three";
function hexToRgb01(hex) {
const clean = hex.replace("#", "").trim();
const value = clean.length === 3
? clean.split("").map((char) => char + char).join("")
: clean;
return new THREE.Color(
parseInt(value.slice(0, 2), 16) / 255,
parseInt(value.slice(2, 4), 16) / 255,
parseInt(value.slice(4, 6), 16) / 255
);
}
function buildGlobeParticleGeometry(options = {}) {
const sphereCount = options.sphereCount || 2600;
const ringCount = options.ringCount || ;
radius = options. || ;
ringRadius = options. || ;
ringThickness = options. || ;
total = sphereCount + ringCount;
positions = (total * );
sizes = (total);
layers = (total);
( i = ; i < sphereCount; i++) {
z = .() * - ;
theta = .() * . * ;
r = radius * ( + .(.(), ) * );
root = .( - z * z);
index = i * ;
positions[index] = .(theta) * root * r;
positions[index + ] = .(theta) * root * r;
positions[index + ] = z * r;
sizes[i] = + .() * ;
layers[i] = .() * ;
}
( i = ; i < ringCount; i++) {
pointIndex = sphereCount + i;
angle = .() * . * ;
r = ringRadius + (.() - ) * ringThickness;
y = (.() - ) * ringThickness * ;
index = pointIndex * ;
positions[index] = .(angle) * r;
positions[index + ] = y;
positions[index + ] = .(angle) * r;
sizes[pointIndex] = + .() * ;
layers[pointIndex] = + .() * ;
}
geometry = .();
geometry.(, .(positions, ));
geometry.(, .(sizes, ));
geometry.(, .(layers, ));
geometry;
}
() {
(!canvas) {};
renderer = .({
canvas,
: ,
: ,
});
renderer.(, );
renderer.(.(. || , options. || ));
renderer. = .;
scene = .();
camera = .(, , , );
camera..(, , options. || );
accent = options.
? .(options.)
: ((.).().() || );
geometry = (options);
material = .({
: globeParticleVertex,
: globeParticleFragment,
: ,
: ,
: .,
: {
: { : },
: { : options. || },
: { : .(options. || ) },
: { : accent },
},
});
particles = .(geometry, material);
particles.. = options. ?? -;
particles.. = options. ?? ;
scene.(particles);
reduceMotion = .().;
pointer = .(, );
rafId = ;
() {
width = .(, canvas.);
height = .(, canvas.);
renderer.(.(. || , options. || ));
renderer.(width, height, );
camera. = width / height;
camera.();
}
() {
rect = canvas.();
pointer. = ((event. - rect.) / rect. - ) * ;
pointer. = ((event. - rect.) / rect. - ) * ;
}
() {
t = time * ;
material... = t;
mouseStrength = options. ?? ;
breath = reduceMotion ? : .(t * ) * ;
particles.. = t * (options. || );
particles.. = (options. ?? -) + pointer. * mouseStrength;
particles.. = (options. ?? ) + pointer. * mouseStrength;
particles..( + breath);
renderer.(scene, camera);
(!reduceMotion) rafId = (render);
}
() {
(rafId);
();
();
}
();
();
.(, handleResize);
.(, handlePointerMove);
{
(rafId);
.(, handleResize);
.(, handlePointerMove);
geometry.();
material.();
renderer.();
};
}
cleanupGlobe = (.(), {
: ,
: ,
: ,
: ,
: ,
: ,
: ,
});
Tuning Knobs
- Density: tune
sphereCount and ringCount separately.
- Scale: tune
radius, ringRadius, ringThickness, and cameraDistance.
- Color: keep
coreColor neutral; derive accentColor from the brand primary.
- Motion: tune
rotationSpeed, tiltX, tiltZ, mouseStrength, and breathing amplitude.
- Glow: tune
pointSize, additive blending, and particle count so the shape stays crisp.
- Performance: lower particle counts or cap
maxDpr before changing the visual structure.
Taste Rules
- The silhouette must read as a globe, not a loose starfield.
- The ring should feel orbital and tilted, not like a flat decorative underline.
- Use restrained glow; let density and depth create the premium feel.
- Keep mouse response gentle so the object drifts rather than swings.
- Put the globe over a dark background or inside a dark atmospheric shell.
Avoid
- Generic starfield noise with no spherical structure.
- Oversized particles or bloom that destroys the globe silhouette.
- Hardcoded accent colors when the design has a clear primary color.
- Wild cursor interaction or fast spinning.
- Dense fog that turns the object into a blurry blob.
Quick Checks
- Sphere and ring are distinct particle populations.
- Core reads mostly neutral or white-hot.
- Accent color appears on ring, highlights, or glow.
- Tilt reveals the ring and globe depth.
- Reduced motion renders a still or near-still object.
- Geometry, material, renderer, listeners, and RAF are cleaned up.