back

Back

Spiral Galaxy

new

October 31, 2025

Spiral Galaxy - new Background

How to implement:

With tailwind installed in your project tutorial, implement these tailwind classes in your parent div where it will reflect the background.

Code:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Infinite Spiral Galaxy Smooth</title>
<style>
  body { margin: 0; overflow: hidden; background: #000; }
  canvas { display: block; }
</style>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js"></script>
<script>
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
camera.position.z = 0;

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// === Halo central ===
const halo = new THREE.Mesh(
  new THREE.RingGeometry(2.2,3,64),
  new THREE.MeshBasicMaterial({
    color:0xff4c4c,
    side: THREE.DoubleSide,
    transparent: true,
    opacity: 0.4,
    blending: THREE.AdditiveBlending
  })
);
halo.rotation.x = Math.PI/2;
scene.add(halo);

// Anéis lentos
const rings = [];
for(let i=0;i<3;i++){
  const ring = new THREE.Mesh(
    new THREE.RingGeometry(3.5 + i*0.7,3.7 + i*0.7,64),
    new THREE.MeshBasicMaterial({
      color: new THREE.Color(`hsl(${i*40},100%,60%)`),
      side: THREE.DoubleSide,
      transparent:true,
      opacity:0.25,
      blending: THREE.AdditiveBlending
    })
  );
  ring.rotation.x = Math.PI/2;
  scene.add(ring);
  rings.push({mesh:ring,speed:0.001 + i*0.001});
}

// === Criar textura circular para partículas ===
const circleCanvas = document.createElement('canvas');
circleCanvas.width = 32;
circleCanvas.height = 32;
const ctx = circleCanvas.getContext('2d');
ctx.beginPath();
ctx.arc(16,16,14,0,Math.PI*2);
ctx.closePath();
ctx.fillStyle = 'white';
ctx.fill();
const texture = new THREE.CanvasTexture(circleCanvas);

// Partículas galáxia espiral
const particleCount = 5000;
const tunnelDepth = 200;
const particlesGeo = new THREE.BufferGeometry();
const positions = [];
const colors = [];

const arms = 3;

for(let i=0;i<particleCount;i++){
  const arm = i % arms;
  const angle = (arm/arms)*Math.PI*2 + Math.random()*0.5;
  const radius = 2 + Math.random()*20;
  const height = (Math.random()-0.5)*2;
  const spread = Math.random()*0.5;
  const x = (radius+spread)*Math.cos(angle + radius*0.2);
  const y = height + spread*Math.sin(angle*3);
  const z = -Math.random()*tunnelDepth;
  positions.push(x,y,z);

  const color = new THREE.Color();
  if(Math.random() < 0.1){
    color.setHSL(0.05 + Math.random()*0.05,1,0.6); 
  } else {
    color.setHSL(0.55 + Math.random()*0.1,0.7,0.6);
  }
  colors.push(color.r,color.g,color.b);
}

particlesGeo.setAttribute('position', new THREE.Float32BufferAttribute(positions,3));
particlesGeo.setAttribute('color', new THREE.Float32BufferAttribute(colors,3));

const particleMat = new THREE.PointsMaterial({
  size:0.025, 
  vertexColors:true,
  transparent:true,
  opacity:0.7,
  blending: THREE.AdditiveBlending,
  map: texture,
  alphaTest: 0.01
});

const particleSystem = new THREE.Points(particlesGeo,particleMat);
scene.add(particleSystem);

// === Animação infinita e suave ===
const speed = 0.015;
const clock = new THREE.Clock();

function animate(){
  requestAnimationFrame(animate);
  const time = clock.getElapsedTime();

  halo.material.opacity = 0.3 + 0.07*Math.sin(time*1.5);
  rings.forEach(r=> r.mesh.rotation.z += r.speed);

  const pos = particleSystem.geometry.attributes.position.array;
  for(let i=0;i<particleCount;i++){
    const idx = i*3;
    pos[idx+2] += speed;
    if(pos[idx+2] > 0) pos[idx+2] -= tunnelDepth;
  }
  particleSystem.geometry.attributes.position.needsUpdate = true;

  renderer.render(scene,camera);
}

animate();

window.addEventListener('resize',()=>{
  camera.aspect = window.innerWidth/window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth,window.innerHeight);
});
</script>
</body>
</html>

View background:

Note

If this background has served you in any way, consider following me on github or supporting me to continue working at BacksEasy.