New to Claude Skills? Learn how to install them →

mengto on GitHub

Globe Particles

Free

Create stunning 3D particle globe visualizations.

by mengto4.6k stars on mengto/skills
6 views
Updated Aug 9, 2026
Get this skill

Free · Opens the source repo

What Globe Particles does

The Globe Particles skill enables developers to create intricate 3D particle visualizations that resemble a globe, featuring a dense luminous core and a surrounding orbital ring. This skill is particularly useful for projects that require a sophisticated planetary or orbital effect without altering the overall layout of the webpage. By leveraging WebGL and Three.js, it offers a premium visual experience that stands out in data visualization and design contexts.

The implementation is straightforward, requiring minimal HTML and CSS setup. The provided code snippets allow for easy integration into existing projects, ensuring that the globe effect can be added without disrupting other elements on the page. The skill focuses on maintaining a clean structure with a dark atmospheric background, allowing the luminous particles to shine and create depth in the visualization.

The particle shaders are designed to produce crisp and luminous points, enhancing the overall aesthetic of the globe. With customizable parameters such as core color and accent color, developers can tailor the visualization to align with the design requirements of their projects. The skill is ideal for applications in data representation, user interfaces, or any creative project that benefits from a visually striking globe effect.

Overall, Globe Particles is a valuable tool for designers and developers looking to enhance their projects with a high-quality 3D visualization that is both functional and visually appealing, while not imposing any layout changes or unrelated motion systems.

When to use it

Use this skill when you want to incorporate a sophisticated globe-like particle effect into your web application without changing the overall page design.

When not to use it

This skill is not suitable for projects that require extensive layout changes or for those needing a playful or overly complex visual style.

What you can build with it

Data Visualization Dashboard

Enhance your data visualization dashboard with a 3D globe effect to represent global data points attractively.

Interactive Web Applications

Integrate the globe particles in interactive web applications to provide users with a visually engaging experience.

Creative Portfolio Projects

Use this skill in your creative portfolio to showcase your ability to implement advanced visual effects in web design.

How to install Globe Particles

View source

1. Install with the skills CLI

npx skills add mengto/skills/globe-particles --agent claude-code

2. Or install it manually

Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.

Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs

Inside SKILL.md

Written by mengto

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 || 1300;
  const radius = options.radius || 1.35;
  const ringRadius = options.ringRadius || 2.05;
  const ringThickness = options.ringThickness || 0.12;
  const total = sphereCount + ringCount;

  const positions = new Float32Array(total * 3);
  const sizes = new Float32Array(total);
  const layers = new Float32Array(total);

  for (let i = 0; i < sphereCount; i++) {
    const z = Math.random() * 2 - 1;
    const theta = Math.random() * Math.PI * 2;
    const r = radius * (0.58 + Math.pow(Math.random(), 0.42) * 0.42);
    const root = Math.sqrt(1 - z * z);
    const index = i * 3;

    positions[index] = Math.cos(theta) * root * r;
    positions[index + 1] = Math.sin(theta) * root * r;
    positions[index + 2] = z * r;
    sizes[i] = 0.72 + Math.random() * 0.72;
    layers[i] = Math.random() * 0.28;
  }

  for (let i = 0; i < ringCount; i++) {
    const pointIndex = sphereCount + i;
    const angle = Math.random() * Math.PI * 2;
    const r = ringRadius + (Math.random() - 0.5) * ringThickness;
    const y = (Math.random() - 0.5) * ringThickness * 0.58;
    const index = pointIndex * 3;

    positions[index] = Math.cos(angle) * r;
    positions[index + 1] = y;
    positions[index + 2] = Math.sin(angle) * r;
    sizes[pointIndex] = 0.62 + Math.random() * 0.58;
    layers[pointIndex] = 0.72 + Math.random() * 0.28;
  }

  const geometry = new THREE.BufferGeometry();
  geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
  geometry.setAttribute("a_size", new THREE.BufferAttribute(sizes, 1));
  geometry.setAttribute("a_layer", new THREE.BufferAttribute(layers, 1));
  return geometry;
}

function initGlobeParticles(canvas, options = {}) {
  if (!canvas) return () => {};

  const renderer = new THREE.WebGLRenderer({
    canvas,
    antialias: true,
    alpha: true,
  });
  renderer.setClearColor(0x000000, 0);
  renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, options.maxDpr || 1.6));
  renderer.outputColorSpace = THREE.SRGBColorSpace;

  const scene = new THREE.Scene();
  const camera = new THREE.PerspectiveCamera(38, 1, 0.1, 100);
  camera.position.set(0, 0, options.cameraDistance || 5.6);

  const accent = options.accentColor
    ? new THREE.Color(options.accentColor)
    : hexToRgb01(getComputedStyle(document.documentElement).getPropertyValue("--brand-accent").trim() || "#8b5cf6");

  const geometry = buildGlobeParticleGeometry(options);
  const material = new THREE.ShaderMaterial({
    vertexShader: globeParticleVertex,
    fragmentShader: globeParticleFragment,
    transparent: true,
    depthWrite: false,
    blending: THREE.AdditiveBlending,
    uniforms: {
      u_time: { value: 0 },
      u_pointSize: { value: options.pointSize || 18 },
      u_coreColor: { value: new THREE.Color(options.coreColor || 0xf8fafc) },
      u_accentColor: { value: accent },
    },
  });

  const particles = new THREE.Points(geometry, material);
  particles.rotation.x = options.tiltX ?? -0.42;
  particles.rotation.z = options.tiltZ ?? 0.22;
  scene.add(particles);

  const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
  const pointer = new THREE.Vector2(0, 0);
  let rafId = 0;

  function resize() {
    const width = Math.max(1, canvas.clientWidth);
    const height = Math.max(1, canvas.clientHeight);
    renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, options.maxDpr || 1.6));
    renderer.setSize(width, height, false);
    camera.aspect = width / height;
    camera.updateProjectionMatrix();
  }

  function handlePointerMove(event) {
    const rect = canvas.getBoundingClientRect();
    pointer.x = ((event.clientX - rect.left) / rect.width - 0.5) * 2;
    pointer.y = ((event.clientY - rect.top) / rect.height - 0.5) * 2;
  }

  function render(time = 0) {
    const t = time * 0.001;
    material.uniforms.u_time.value = t;

    const mouseStrength = options.mouseStrength ?? 0.08;
    const breath = reduceMotion ? 0 : Math.sin(t * 0.55) * 0.045;
    particles.rotation.y = t * (options.rotationSpeed || 0.12);
    particles.rotation.x = (options.tiltX ?? -0.42) + pointer.y * mouseStrength;
    particles.rotation.z = (options.tiltZ ?? 0.22) + pointer.x * mouseStrength;
    particles.scale.setScalar(1 + breath);

    renderer.render(scene, camera);
    if (!reduceMotion) rafId = requestAnimationFrame(render);
  }

  function handleResize() {
    cancelAnimationFrame(rafId);
    resize();
    render();
  }

  resize();
  render();
  window.addEventListener("resize", handleResize);
  window.addEventListener("pointermove", handlePointerMove);

  return () => {
    cancelAnimationFrame(rafId);
    window.removeEventListener("resize", handleResize);
    window.removeEventListener("pointermove", handlePointerMove);
    geometry.dispose();
    material.dispose();
    renderer.dispose();
  };
}

const cleanupGlobe = initGlobeParticles(document.querySelector("[data-globe-particles]"), {
  sphereCount: 2600,
  ringCount: 1300,
  accentColor: "#8b5cf6",
  radius: 1.35,
  ringRadius: 2.05,
  rotationSpeed: 0.12,
  mouseStrength: 0.08,
});

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.

Frequently asked questions about Globe Particles

Similar skills