Close Menu

    Subscribe to Updates

    Get the latest creative news from FooBar about art, design and business.

    What's Hot

    How to create Interactive 3D Galaxy Animation using HTML CSS and JS

    24 October 2025

    How to create Animated Social Media Card Hover using HTML CSS and JS

    20 October 2025

    How to create Animated LogOut Button using HTML CSS and JS

    18 October 2025
    Facebook X (Twitter) Instagram YouTube Telegram Threads
    Coding StellaCoding Stella
    • Home
    • Blog
    • HTML & CSS
      • Login Form
    • JavaScript
    • Hire us!
    Coding StellaCoding Stella
    Home - JavaScript - How to create Interactive 3D Galaxy Animation using HTML CSS and JS
    JavaScript

    How to create Interactive 3D Galaxy Animation using HTML CSS and JS

    Coding StellaBy Coding Stella24 October 2025Updated:25 October 2025No Comments16 Mins Read
    Share Facebook Twitter Pinterest LinkedIn Tumblr Reddit Email WhatsApp Copy Link

    Let’s create an Interactive 3D Galaxy Animation using HTML, CSS, and JavaScript. This project will feature a stunning galaxy filled with glowing stars that move and respond to user interaction, creating a mesmerizing 3D space effect.

    We’ll use:

    • HTML to structure the canvas and elements.
    • CSS to style the background and lighting effects.
    • JavaScript to create and animate the 3D galaxy, using mouse movement or scroll for interactivity.

    This project is perfect for developers who love visual creativity and want to explore 3D effects in web design. Let’s dive into this cosmic coding adventure and bring the galaxy to life right in your browser! 🌌✨

    HTML :

    This code builds an interactive 3D galaxy animation using HTML, CSS, and JavaScript with Three.js. It creates a main glowing star in the center, surrounded by rotating planets and shining particle trails. The camera moves smoothly around the scene, and everything is lit with colorful lights and bloom effects to make it look realistic and futuristic.

    There are also buttons that let users control the animation – you can reset the camera view, change how fast time moves, switch between beautiful color themes like Inferno, Veridian, and Celestial, or activate a “resonance” mode that makes the planets glow and connect with energy arcs. Overall, the project turns code into a stunning 3D galaxy that feels alive and interactive.

    <!DOCTYPE html>
    <html lang="en">
    
      <head>
        <meta charset="UTF-8">
        <title>Interactive 3D Galaxy Animation | @coding.stella</title>
        <link rel="stylesheet" href="./style.css">
      </head>
        
    <body>
      <div id="container"></div>
      <div class="stats" id="stats">
        <div>Systems: Active</div>
        <div>Resonance: Stable</div>
        <div>Phase: Nominal</div>
      </div>
      <div class="controls">
        <button class="control-button" id="resetView">
          <span></span><span></span><span></span><span></span><span>Reset View</span>
        </button>
        <button class="control-button activate-button" id="activateTrigger">
          <span></span><span></span><span></span><span></span><span>Activate Resonance</span>
        </button>
        <button class="control-button" id="timeAccel">
          <span></span><span></span><span></span><span></span><span>Time: 1x</span>
        </button>
        <button class="control-button" id="toggleTheme">
          <span></span><span></span><span></span><span></span><span>Theme: Inferno</span>
        </button>
      </div>
    </body>
    
      <script id="starVertexShader" type="x-shader/x-vertex">
        varying vec3 vPosition;
        varying vec3 vNormal;
        varying vec2 vUv;
        void main(){
            vPosition=position;
            vNormal=normal;
            vUv=uv;
            gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);
        }
      </script>
      <script id="starFragmentShader" type="x-shader/x-fragment">
        uniform float time;
        uniform float intensity;
        uniform vec3 color1;
        uniform vec3 color2;
        varying vec3 vPosition;
        varying vec3 vNormal;
        varying vec2 vUv;
        float noise(vec3 p){return fract(sin(dot(p,vec3(12.9898,78.233,54.53)))*43758.5453);}
        void main(){
            vec3 pos=vPosition+time*0.1;
            float n1=noise(pos*3.0);
            float n2=noise(pos*6.0+vec3(100.0));
            float n3=noise(pos*12.0+vec3(200.0));
            float pattern=n1*0.5+n2*0.3+n3*0.2;
            pattern=pow(pattern,2.0);
            vec3 finalColor=mix(color1,color2,pattern);
            finalColor*=(1.0+intensity*pattern*2.0);
            float fresnel=pow(1.0-dot(vNormal,vec3(0.0,0.0,1.0)),2.0);
            finalColor+=fresnel*intensity*0.5;
            gl_FragColor=vec4(finalColor,1.0);
        }
      </script>
      <script id="planetVertexShader" type="x-shader/x-vertex">
        varying vec3 vPosition;
        varying vec3 vNormal;
        varying vec2 vUv;
        void main(){
            vPosition=position;
            vNormal=normalize(normalMatrix*normal);
            vUv=uv;
            gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);
        }
      </script>
      <script id="planetFragmentShader" type="x-shader/x-fragment">
        uniform float time;
        uniform vec3 baseColor;
        uniform vec3 accentColor;
        uniform float energy;
        varying vec3 vPosition;
        varying vec3 vNormal;
        varying vec2 vUv;
        float noise(vec2 p){return fract(sin(dot(p,vec2(12.9898,78.233)))*43758.5453);}
        void main(){
            vec2 uv=vUv+time*0.02;
            float n1=noise(uv*8.0);
            float n2=noise(uv*16.0);
            float pattern=n1*0.7+n2*0.3;
            vec3 color=mix(baseColor,accentColor,pattern);
            float fresnel=pow(1.0-abs(dot(vNormal,vec3(0.0,0.0,1.0))),1.5);
            color+=fresnel*accentColor*0.5;
            color*=(1.0+energy*0.8);
            gl_FragColor=vec4(color,1.0);
        }
      </script>
      <script id="arcVertexShader" type="x-shader/x-vertex">
        varying vec2 vUv;
        varying vec3 vPosition;
        void main(){
            vUv=uv;
            vPosition=position;
            gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);
        }
      </script>
      <script id="arcFragmentShader" type="x-shader/x-fragment">
        uniform float time;
        uniform vec3 color;
        uniform float opacity;
        uniform float energy;
        varying vec2 vUv;
        varying vec3 vPosition;
        void main(){
            float flow=abs(sin(vUv.x*15.0-time*12.0));
            float pulse=sin(time*8.0)*0.5+0.5;
            float pattern=pow(flow,1.5)*(1.0+pulse*energy);
            float fade=sin(vUv.x*3.14159);
            vec3 finalColor=color*(pattern*2.0+0.3);
            float alpha=fade*opacity*(pattern+0.2)*(1.0+energy);
            gl_FragColor=vec4(finalColor,alpha);
        }
      </script>
      <script type="importmap">
        {
          "imports": {
            "three": "https://cdnjs.cloudflare.com/ajax/libs/three.js/0.161.0/three.module.js",
            "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.161.0/examples/jsm/"
          }
        }
      </script>
      <script type="module">
        import * as THREE from "three";
        import { OrbitControls } from "three/addons/controls/OrbitControls.js";
        import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
        import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
        import { UnrealBloomPass } from "three/addons/postprocessing/UnrealBloomPass.js";
        import { OutputPass } from "three/addons/postprocessing/OutputPass.js";
    
        let scene, camera, renderer, controls, clock, composer;
        let star,
          orrery,
          starLight,
          ambientLight,
          blueLight,
          purpleLight,
          metalMaterial,
          ringMaterial;
        let planets = [];
        let activeEffects = [];
        let particleSystems = [];
        let timeAcceleration = 1.0;
        let isResonanceActive = false;
        let currentThemeIndex = 0;
    
        const container = document.getElementById("container");
        const activateButton = document.getElementById("activateTrigger");
        const resetButton = document.getElementById("resetView");
        const timeButton = document.getElementById("timeAccel");
        const themeButton = document.getElementById("toggleTheme");
    
        const themes = [
          {
            name: "Inferno",
            starColors: { color1: 0xffffff, color2: 0xffcc00 },
            planetData: [
              {
                baseColor: [0.8, 0.2, 0.1],
                accentColor: [1, 0.6, 0.2],
                trailColor: 0xff4400,
              },
              {
                baseColor: [0.6, 0.1, 0.1],
                accentColor: [1, 0.4, 0.1],
                trailColor: 0xff8800,
              },
              {
                baseColor: [0.9, 0.3, 0],
                accentColor: [1, 0.8, 0.3],
                trailColor: 0xffaa33,
              },
            ],
            ambientLightColor: 0x401008,
            starLightColor: 0xffcc88,
            directionalLights: { color1: 0xff6600, color2: 0xdd3300 },
            metalMaterialColor: 0x332222,
            ringColor: 0xff8866,
            arcColor: 0xffccaa,
          },
          {
            name: "Veridian",
            starColors: { color1: 0xccffee, color2: 0x66ffcc },
            planetData: [
              {
                baseColor: [0.2, 0.8, 0.5],
                accentColor: [0.8, 1, 0.9],
                trailColor: 0x00ffaa,
              },
              {
                baseColor: [0.1, 0.6, 0.7],
                accentColor: [0.5, 0.9, 1],
                trailColor: 0x00ccff,
              },
              {
                baseColor: [0.5, 0.8, 0.2],
                accentColor: [0.9, 1, 0.6],
                trailColor: 0xaaff00,
              },
            ],
            ambientLightColor: 0x0a3024,
            starLightColor: 0xccffdd,
            directionalLights: { color1: 0x33cc88, color2: 0x4488cc },
            metalMaterialColor: 0x779988,
            ringColor: 0x88ffcc,
            arcColor: 0xeeffee,
          },
          {
            name: "Celestial",
            starColors: { color1: 0xffe4b5, color2: 0xff8844 },
            planetData: [
              {
                baseColor: [1, 0.4, 0.4],
                accentColor: [1, 0.8, 0.2],
                trailColor: 0xff6644,
              },
              {
                baseColor: [0.3, 0.8, 0.3],
                accentColor: [0.6, 1, 0.8],
                trailColor: 0x44ff88,
              },
              {
                baseColor: [0.3, 0.4, 1],
                accentColor: [0.8, 0.6, 1],
                trailColor: 0x4488ff,
              },
            ],
            ambientLightColor: 0x1a2440,
            starLightColor: 0xffe4b5,
            directionalLights: { color1: 0x4488ff, color2: 0x8844ff },
            metalMaterialColor: 0x4a6080,
            ringColor: 0x88ccff,
            arcColor: 0xffeebb,
          },
        ];
    
        function init() {
          scene = new THREE.Scene();
          clock = new THREE.Clock();
          camera = new THREE.PerspectiveCamera(
            55,
            window.innerWidth / window.innerHeight,
            0.1,
            3000,
          );
          camera.position.set(25, 20, 25);
          renderer = new THREE.WebGLRenderer({
            antialias: true,
            powerPreference: "high-performance",
          });
          renderer.setSize(window.innerWidth, window.innerHeight);
          renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
          renderer.toneMapping = THREE.ACESFilmicToneMapping;
          renderer.toneMappingExposure = 1.2;
          renderer.shadowMap.enabled = true;
          renderer.shadowMap.type = THREE.PCFSoftShadowMap;
          container.appendChild(renderer.domElement);
          controls = new OrbitControls(camera, renderer.domElement);
          controls.enableDamping = true;
          controls.dampingFactor = 0.03;
          controls.minDistance = 8;
          controls.maxDistance = 150;
          controls.autoRotate = true;
          controls.autoRotateSpeed = 0.15;
          controls.enablePan = false;
          setupLighting();
          createOrrery();
          createEnvironment();
          setupPostProcessing();
          applyTheme(currentThemeIndex);
          window.addEventListener("resize", onWindowResize);
          activateButton.addEventListener("click", activateResonance);
          resetButton.addEventListener("click", resetView);
          timeButton.addEventListener("click", toggleTimeAcceleration);
          themeButton.addEventListener("click", toggleTheme);
        }
    
        function setupLighting() {
          ambientLight = new THREE.AmbientLight(0x1a2440, 0.8);
          scene.add(ambientLight);
          starLight = new THREE.PointLight(0xffe4b5, 3, 120, 1.8);
          starLight.castShadow = true;
          starLight.shadow.mapSize.width = 2048;
          starLight.shadow.mapSize.height = 2048;
          scene.add(starLight);
          blueLight = new THREE.DirectionalLight(0x4488ff, 0.5);
          blueLight.position.set(-50, 30, -30);
          scene.add(blueLight);
          purpleLight = new THREE.DirectionalLight(0x8844ff, 0.3);
          purpleLight.position.set(30, -20, 50);
          scene.add(purpleLight);
        }
    
        function setupPostProcessing() {
          composer = new EffectComposer(renderer);
          composer.addPass(new RenderPass(scene, camera));
          const bloomPass = new UnrealBloomPass(
            new THREE.Vector2(window.innerWidth, window.innerHeight),
            0.6,
            0.5,
            0.15,
          );
          composer.addPass(bloomPass);
          composer.addPass(new OutputPass());
        }
    
        function createOrrery() {
          orrery = new THREE.Group();
          scene.add(orrery);
          metalMaterial = new THREE.MeshStandardMaterial({
            color: 0x4a6080,
            metalness: 0.95,
            roughness: 0.2,
            emissive: 0x1a2540,
            emissiveIntensity: 0.4,
            envMapIntensity: 1.5,
          });
          ringMaterial = new THREE.MeshBasicMaterial({
            color: 0x88ccff,
            wireframe: true,
            transparent: true,
            opacity: 0.4,
          });
          const starGeo = new THREE.IcosahedronGeometry(2.2, 2);
          const starMaterial = new THREE.ShaderMaterial({
            uniforms: {
              time: { value: 0 },
              intensity: { value: 1 },
              color1: {
                value: new THREE.Color(themes[currentThemeIndex].starColors.color1),
              },
              color2: {
                value: new THREE.Color(themes[currentThemeIndex].starColors.color2),
              },
            },
            vertexShader: document.getElementById("starVertexShader").textContent,
            fragmentShader:
              document.getElementById("starFragmentShader").textContent,
          });
          star = new THREE.Mesh(starGeo, starMaterial);
          star.castShadow = false;
          star.receiveShadow = false;
          orrery.add(star);
          for (let i = 0; i < 7; i++) {
            const gearGeo = new THREE.TorusGeometry(2.8 + i * 0.5, 0.18, 12, 64);
            const gear = new THREE.Mesh(gearGeo, metalMaterial);
            gear.rotation.x = Math.PI / 2;
            gear.position.y = -2 - i * 0.25;
            gear.userData.rotationSpeed =
              (i % 2 === 0 ? 1 : -1) * (0.08 + i * 0.04);
            gear.castShadow = true;
            gear.receiveShadow = true;
            orrery.add(gear);
          }
          const planetGeometries = [
            new THREE.OctahedronGeometry(0.6, 1),
            new THREE.DodecahedronGeometry(0.9, 1),
            new THREE.IcosahedronGeometry(0.7, 1),
          ];
          const planetBaseData = [
            { size: 0.6, distance: 8, speed: 0.6 },
            { size: 0.9, distance: 14, speed: 0.35 },
            { size: 0.7, distance: 22, speed: 0.25 },
          ];
          planetBaseData.forEach((data, i) => {
            const planetGroup = new THREE.Group();
            planetGroup.userData.orbitSpeed = data.speed;
            planetGroup.rotation.y = Math.random() * Math.PI * 2;
            orrery.add(planetGroup);
            const ringGeo = new THREE.TorusGeometry(data.distance, 0.08, 20, 128);
            const ring = new THREE.Mesh(ringGeo, ringMaterial);
            ring.rotation.x = Math.PI / 2;
            planetGroup.add(ring);
            const planetMaterial = new THREE.ShaderMaterial({
              uniforms: {
                time: { value: 0 },
                baseColor: {
                  value: new THREE.Vector3(
                    ...themes[currentThemeIndex].planetData[i].baseColor,
                  ),
                },
                accentColor: {
                  value: new THREE.Vector3(
                    ...themes[currentThemeIndex].planetData[i].accentColor,
                  ),
                },
                energy: { value: 0 },
              },
              vertexShader:
                document.getElementById("planetVertexShader").textContent,
              fragmentShader: document.getElementById("planetFragmentShader")
                .textContent,
            });
            const planet = new THREE.Mesh(planetGeometries[i], planetMaterial);
            planet.position.x = data.distance;
            planet.userData.selfRotation = 0.6;
            planet.castShadow = true;
            planet.receiveShadow = true;
            planetGroup.add(planet);
            createParticleTrail(
              planet,
              themes[currentThemeIndex].planetData[i].trailColor,
              data.distance,
            );
            planets.push({
              group: planetGroup,
              body: planet,
              material: planetMaterial,
            });
          });
        }
    
        function createParticleTrail(planet, color, radius) {
          const count = 50;
          const positions = new Float32Array(count * 3);
          const colors = new Float32Array(count * 3);
          const sizes = new Float32Array(count);
          const c = new THREE.Color(color);
          for (let i = 0; i < count; i++) {
            const angle = (i / count) * Math.PI * 2;
            positions[i * 3] = Math.cos(angle) * radius;
            positions[i * 3 + 1] = 0;
            positions[i * 3 + 2] = Math.sin(angle) * radius;
            colors[i * 3] = c.r;
            colors[i * 3 + 1] = c.g;
            colors[i * 3 + 2] = c.b;
            sizes[i] = Math.random() * 0.5 + 0.1;
          }
          const geom = new THREE.BufferGeometry();
          geom.setAttribute("position", new THREE.BufferAttribute(positions, 3));
          geom.setAttribute("color", new THREE.BufferAttribute(colors, 3));
          geom.setAttribute("size", new THREE.BufferAttribute(sizes, 1));
          const mat = new THREE.PointsMaterial({
            size: 0.3,
            vertexColors: true,
            transparent: true,
            opacity: 0.6,
            blending: THREE.AdditiveBlending,
            sizeAttenuation: true,
          });
          const points = new THREE.Points(geom, mat);
          planet.parent.add(points);
          particleSystems.push({
            system: points,
            planet: planet,
            positions: positions,
            radius: radius,
            currentIndex: 0,
          });
        }
    
        function createEnvironment() {
          const layers = [
            {
              count: 3000,
              distance: [600, 1000],
              size: [0.8, 1.5],
              color: 0x6688bb,
            },
            { count: 2000, distance: [1000, 1500], size: [1, 2], color: 0x88aadd },
            {
              count: 1000,
              distance: [1500, 2000],
              size: [1.5, 3],
              color: 0xaaccff,
            },
          ];
          layers.forEach((layer) => {
            const positions = new Float32Array(layer.count * 3);
            const colors = new Float32Array(layer.count * 3);
            const sizes = new Float32Array(layer.count);
            const c = new THREE.Color(layer.color);
            for (let i = 0; i < layer.count; i++) {
              const u = Math.random(),
                v = Math.random();
              const theta = 2 * Math.PI * u;
              const phi = Math.acos(2 * v - 1);
              const r =
                layer.distance[0] +
                Math.random() * (layer.distance[1] - layer.distance[0]);
              positions[i * 3] = r * Math.sin(phi) * Math.cos(theta);
              positions[i * 3 + 1] = r * Math.sin(phi) * Math.sin(theta);
              positions[i * 3 + 2] = r * Math.cos(phi);
              colors[i * 3] = c.r;
              colors[i * 3 + 1] = c.g;
              colors[i * 3 + 2] = c.b;
              sizes[i] =
                layer.size[0] + Math.random() * (layer.size[1] - layer.size[0]);
            }
            const geom = new THREE.BufferGeometry();
            geom.setAttribute("position", new THREE.BufferAttribute(positions, 3));
            geom.setAttribute("color", new THREE.BufferAttribute(colors, 3));
            geom.setAttribute("size", new THREE.BufferAttribute(sizes, 1));
            const mat = new THREE.PointsMaterial({
              size: 2,
              vertexColors: true,
              sizeAttenuation: true,
              transparent: true,
              opacity: 0.8,
            });
            const stars = new THREE.Points(geom, mat);
            scene.add(stars);
          });
        }
    
        function activateResonance() {
          if (isResonanceActive) return;
          isResonanceActive = true;
          planets.forEach(
            (p) => (p.body.userData.baseOrbitSpeed = p.group.userData.orbitSpeed),
          );
          themes; // preserve reference
          const activationEffect = {
            type: "activation",
            startTime: clock.getElapsedTime(),
            duration: 8,
            update: (elapsed) => {
              const prog = elapsed / activationEffect.duration;
              const intens = Math.sin(prog * Math.PI) * 3;
              starLight.intensity = 3 + intens * 3;
              star.material.uniforms.intensity.value = 1 + intens * 2;
              metalMaterial.emissiveIntensity = 0.4 + intens * 2;
              planets.forEach((p) => {
                p.group.userData.orbitSpeed =
                  p.body.userData.baseOrbitSpeed * (1 + intens * 1.5);
                p.material.uniforms.energy.value = intens;
              });
            },
            end: () => {
              starLight.intensity = 3;
              star.material.uniforms.intensity.value = 1;
              metalMaterial.emissiveIntensity = 0.4;
              planets.forEach((p) => {
                p.group.userData.orbitSpeed = p.body.userData.baseOrbitSpeed;
                p.material.uniforms.energy.value = 0;
              });
              isResonanceActive = false;
            },
          };
          activeEffects.push(activationEffect);
          for (let i = 0; i < planets.length; i++) {
            createEnhancedArc(
              planets[i].body,
              i === 0 ? star : planets[i - 1].body,
              6,
            );
          }
        }
    
        function createEnhancedArc(obj1, obj2, duration) {
          const material = new THREE.ShaderMaterial({
            uniforms: {
              time: { value: 0 },
              color: { value: new THREE.Color(themes[currentThemeIndex].arcColor) },
              opacity: { value: 0 },
              energy: { value: 0 },
            },
            vertexShader: document.getElementById("arcVertexShader").textContent,
            fragmentShader:
              document.getElementById("arcFragmentShader").textContent,
            transparent: true,
            blending: THREE.AdditiveBlending,
            depthWrite: false,
          });
          const arcEffect = {
            type: "arc",
            mesh: null,
            material: material,
            startTime: clock.getElapsedTime(),
            duration: duration,
            obj1: obj1,
            obj2: obj2,
            update: (elapsed) => {
              const prog = elapsed / arcEffect.duration;
              if (prog > 1) return;
              const p1 = new THREE.Vector3(),
                p2 = new THREE.Vector3();
              obj1.getWorldPosition(p1);
              obj2.getWorldPosition(p2);
              const mid = p1.clone().lerp(p2, 0.5);
              const ctrl = mid
                .clone()
                .add(new THREE.Vector3(0, p1.distanceTo(p2) * 0.5, 0));
              const curve = new THREE.QuadraticBezierCurve3(p1, ctrl, p2);
              const tube = new THREE.TubeGeometry(curve, 48, 0.15, 12, false);
              if (!arcEffect.mesh) {
                arcEffect.mesh = new THREE.Mesh(tube, material);
                scene.add(arcEffect.mesh);
              } else {
                arcEffect.mesh.geometry.dispose();
                arcEffect.mesh.geometry = tube;
              }
              const intens = Math.sin(prog * Math.PI);
              material.uniforms.opacity.value = intens * 0.9;
              material.uniforms.energy.value = intens * 2;
              material.uniforms.time.value = clock.getElapsedTime();
              arcEffect.mesh.visible = true;
            },
            end: () => {
              if (arcEffect.mesh) {
                scene.remove(arcEffect.mesh);
                arcEffect.mesh.geometry.dispose();
                arcEffect.material.dispose();
              }
            },
          };
          activeEffects.push(arcEffect);
        }
    
        function resetView() {
          controls.reset();
          camera.position.set(25, 20, 25);
          controls.update();
        }
    
        function toggleTimeAcceleration() {
          timeAcceleration =
            timeAcceleration === 1 ? 3 : timeAcceleration === 3 ? 0.5 : 1;
          document
            .getElementById("timeAccel")
            .querySelector("span:last-of-type").textContent =
            `Time: ${timeAcceleration}x`;
        }
    
        function toggleTheme() {
          currentThemeIndex = (currentThemeIndex + 1) % themes.length;
          applyTheme(currentThemeIndex);
        }
    
        function applyTheme(i) {
          const theme = themes[i];
          themeButton.querySelector("span:last-of-type").textContent =
            `Theme: ${theme.name}`;
          star.material.uniforms.color1.value.set(theme.starColors.color1);
          star.material.uniforms.color2.value.set(theme.starColors.color2);
          planets.forEach((p, idx) => {
            const pd = theme.planetData[idx];
            p.material.uniforms.baseColor.value.set(...pd.baseColor);
            p.material.uniforms.accentColor.value.set(...pd.accentColor);
          });
          particleSystems.forEach((ps, idx) => {
            const c = new THREE.Color(theme.planetData[idx].trailColor);
            const col = ps.system.geometry.attributes.color;
            for (let j = 0; j < col.count; j++) col.setXYZ(j, c.r, c.g, c.b);
            col.needsUpdate = true;
          });
          ambientLight.color.set(theme.ambientLightColor);
          starLight.color.set(theme.starLightColor);
          blueLight.color.set(theme.directionalLights.color1);
          purpleLight.color.set(theme.directionalLights.color2);
          metalMaterial.color.set(theme.metalMaterialColor);
          ringMaterial.color.set(theme.ringColor);
        }
    
        function onWindowResize() {
          camera.aspect = window.innerWidth / window.innerHeight;
          camera.updateProjectionMatrix();
          renderer.setSize(window.innerWidth, window.innerHeight);
          composer.setSize(window.innerWidth, window.innerHeight);
        }
    
        function animate() {
          requestAnimationFrame(animate);
          const delta = clock.getDelta() * timeAcceleration;
          const t = clock.getElapsedTime();
          star.rotation.y += delta * 0.3;
          star.rotation.x += delta * 0.15;
          star.material.uniforms.time.value = t;
          orrery.children.forEach((c) => {
            if (c.userData.rotationSpeed)
              c.rotation.z += delta * c.userData.rotationSpeed;
          });
          planets.forEach((p) => {
            p.group.rotation.y += delta * p.group.userData.orbitSpeed;
            p.body.rotation.y += delta * p.body.userData.selfRotation;
            p.body.rotation.z += delta * p.body.userData.selfRotation * 0.3;
            p.material.uniforms.time.value = t;
          });
          particleSystems.forEach(
            (ps) => (ps.system.geometry.attributes.position.needsUpdate = true),
          );
          for (let i = activeEffects.length - 1; i >= 0; i--) {
            const e = activeEffects[i];
            const elapsed = t - e.startTime;
            if (elapsed > e.duration) {
              e.end();
              activeEffects.splice(i, 1);
            } else {
              e.update(elapsed, delta);
            }
          }
          controls.update();
          composer.render(delta);
        }
    
        init();
        animate();
      </script>
    
    </html>

    CSS :

    This CSS gives the 3D galaxy page a futuristic, glowing look with dark space-style backgrounds, neon buttons, and glass-like panels. The control buttons have animated borders, hover effects, and a glowing “Activate Resonance” button for emphasis. The stats box and controls use blur and transparency for a clean sci-fi feel, and everything adjusts nicely on mobile screens.

    @import url("https://fonts.googleapis.com/css2?family=Orbitron:wght@300;500;700;900&display=swap");
    @import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap");
    
    body {
      margin: 0;
      overflow: hidden;
      background: radial-gradient(circle at center, #0a0f1a 0%, #020408 100%);
      height: 100vh;
      width: 100vw;
      font-family: "Inter", "Orbitron", sans-serif;
    }
    
    #container {
      width: 100%;
      height: 100%;
      display: block;
      position: absolute;
      top: 0;
      left: 0;
    }
    
    .controls {
      position: absolute;
      bottom: 20px;
      left: 50%;
      transform: translateX(-50%);
      z-index: 100;
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      gap: 12px;
      align-items: center;
      padding: 8px;
      background: rgba(255, 255, 255, 0.01);
      backdrop-filter: blur(40px) saturate(180%);
      border-radius: 20px;
      border: 1px solid rgba(255, 255, 255, 0.08);
      box-shadow:
        0 25px 50px rgba(0, 0, 0, 0.25),
        0 0 0 1px rgba(255, 255, 255, 0.02),
        inset 0 1px 0 rgba(255, 255, 255, 0.1),
        inset 0 -1px 0 rgba(0, 0, 0, 0.1);
    }
    
    .control-button {
      position: relative;
      background: rgba(255, 255, 255, 0.08);
      backdrop-filter: blur(20px) saturate(180%);
      border: 1px solid rgba(255, 255, 255, 0.12);
      border-radius: 12px;
      padding: 10px 18px;
      color: rgba(255, 255, 255, 0.9);
      font-size: 12px;
      font-weight: 500;
      cursor: pointer;
      transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
      overflow: hidden;
      user-select: none;
      letter-spacing: 0.5px;
      min-width: 100px;
      text-align: center;
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 8px;
      flex-shrink: 0;
      box-shadow:
        0 8px 32px rgba(0, 0, 0, 0.12),
        0 1px 2px rgba(0, 0, 0, 0.24),
        inset 0 1px 0 rgba(255, 255, 255, 0.15),
        inset 0 -1px 0 rgba(0, 0, 0, 0.1);
    }
    
    .control-button:hover {
      background: rgba(255, 255, 255, 0.12);
      border-color: rgba(255, 255, 255, 0.2);
      color: rgba(255, 255, 255, 1);
      transform: translateY(-2px);
      box-shadow:
        0 12px 36px rgba(0, 0, 0, 0.2),
        0 1px 3px rgba(0, 0, 0, 0.24),
        inset 0 1px 0 rgba(255, 255, 255, 0.15),
        inset 0 -1px 0 rgba(0, 0, 0, 0.1);
    }
    
    .control-button:active {
      background: rgba(255, 255, 255, 0.15);
      transform: translateY(0);
      box-shadow:
        0 8px 32px rgba(0, 0, 0, 0.12),
        0 1px 2px rgba(0, 0, 0, 0.24),
        inset 0 2px 4px rgba(0, 0, 0, 0.1),
        inset 0 1px 0 rgba(255, 255, 255, 0.15);
    }
    
    .control-button > span:last-of-type {
      position: relative;
      z-index: 2;
    }
    
    .control-button > span:not(:last-of-type) {
      position: absolute;
      display: block;
      box-shadow:
        0 0 15px rgba(128, 200, 255, 0.9),
        0 0 30px rgba(128, 200, 255, 0.7);
    }
    
    .control-button > span:nth-child(1) {
      top: 0;
      left: -100%;
      width: 100%;
      height: 2px;
      background: linear-gradient(90deg, transparent, rgba(128, 200, 255, 1));
    }
    .control-button:hover > span:nth-child(1) {
      animation: animateBorder1 2s linear infinite;
    }
    
    .control-button > span:nth-child(2) {
      top: -100%;
      right: 0;
      width: 2px;
      height: 100%;
      background: linear-gradient(180deg, transparent, rgba(128, 200, 255, 1));
    }
    .control-button:hover > span:nth-child(2) {
      animation: animateBorder2 2s linear infinite;
      animation-delay: 0.5s;
    }
    
    .control-button > span:nth-child(3) {
      bottom: 0;
      right: -100%;
      width: 100%;
      height: 2px;
      background: linear-gradient(270deg, transparent, rgba(128, 200, 255, 1));
    }
    .control-button:hover > span:nth-child(3) {
      animation: animateBorder3 2s linear infinite;
      animation-delay: 1s;
    }
    
    .control-button > span:nth-child(4) {
      bottom: -100%;
      left: 0;
      width: 2px;
      height: 100%;
      background: linear-gradient(0deg, transparent, rgba(128, 200, 255, 1));
    }
    .control-button:hover > span:nth-child(4) {
      animation: animateBorder4 2s linear infinite;
      animation-delay: 1.5s;
    }
    
    @keyframes animateBorder1 {
      0% {
        left: -100%;
      }
      50%,100% {
        left: 100%;
      }
    }
    @keyframes animateBorder2 {
      0% {
        top: -100%;
      }
      50%,100% {
        top: 100%;
      }
    }
    @keyframes animateBorder3 {
      0% {
        right: -100%;
      }
      50%,100% {
        right: 100%;
      }
    }
    @keyframes animateBorder4 {
      0% {
        bottom: -100%;
      }
      50%,
      100% {
        bottom: 100%;
      }
    }
    
    .activate-button {
      background: linear-gradient(
        135deg,
        rgba(255, 100, 100, 0.15) 0%,
        rgba(255, 50, 50, 0.1) 100%
      );
      border: 1px solid rgba(255, 100, 100, 0.3);
      color: rgba(255, 150, 150, 0.95);
    }
    
    .activate-button:hover {
      background: linear-gradient(
        135deg,
        rgba(255, 120, 120, 0.2) 0%,
        rgba(255, 80, 80, 0.15) 100%
      );
      border-color: rgba(255, 150, 150, 0.4);
      color: rgba(255, 200, 200, 1);
    }
    
    .activate-button > span:not(:last-of-type) {
      box-shadow:
        0 0 15px rgba(255, 120, 120, 0.9),
        0 0 30px rgba(255, 120, 120, 0.7);
    }
    .activate-button > span:nth-child(1) {
      background: linear-gradient(90deg, transparent, rgba(255, 120, 120, 1));
    }
    .activate-button > span:nth-child(2) {
      background: linear-gradient(180deg, transparent, rgba(255, 120, 120, 1));
    }
    .activate-button > span:nth-child(3) {
      background: linear-gradient(270deg, transparent, rgba(255, 120, 120, 1));
    }
    .activate-button > span:nth-child(4) {
      background: linear-gradient(0deg, transparent, rgba(255, 120, 120, 1));
    }
    
    .stats {
      position: absolute;
      top: 20px;
      right: 20px;
      color: rgba(255, 255, 255, 0.7);
      font-size: 10px;
      font-weight: 400;
      pointer-events: none;
      z-index: 5;
      text-shadow: 0 0 10px rgba(0, 0, 0, 0.8);
      line-height: 1.6;
      background: rgba(255, 255, 255, 0.03);
      backdrop-filter: blur(20px) saturate(150%);
      padding: 10px 14px;
      border-radius: 10px;
      border: 1px solid rgba(255, 255, 255, 0.06);
      box-shadow:
        0 8px 32px rgba(0, 0, 0, 0.12),
        inset 0 1px 0 rgba(255, 255, 255, 0.08);
    }
    
    .stats div {
      margin-bottom: 4px;
      padding: 2px 0;
    }
    
    .stats div:last-child {
      margin-bottom: 0;
    }
    
    .controls::before {
      content: "";
      position: absolute;
      top: -1px;
      left: -1px;
      right: -1px;
      bottom: -1px;
      background: linear-gradient(
        45deg,
        rgba(255, 255, 255, 0.05) 0%,
        transparent 25%,
        transparent 75%,
        rgba(255, 255, 255, 0.05) 100%
      );
      border-radius: 21px;
      z-index: -1;
      opacity: 0.5;
    }
    
    @media (max-width: 640px) {
      .controls {
        gap: 8px;
        bottom: 15px;
        left: 15px;
        right: 15px;
        transform: translateX(0);
        width: auto;
      }
      .control-button {
        padding: 8px 12px;
        font-size: 11px;
        flex-grow: 1;
      }
      .stats {
        top: 15px;
        right: 15px;
        padding: 8px 12px;
      }
    }

    In conclusion, building an Interactive 3D Galaxy Animation using HTML, CSS, and JavaScript has been an exciting and visually rewarding project. By combining structure, styling, and animation logic, we’ve created an immersive experience that feels alive and dynamic🚀💫

    If your project has problems, don’t worry. Just click to download the source code and face your coding challenges with excitement. Have fun coding!

    Animation space
    Share. Copy Link Twitter Facebook LinkedIn Email WhatsApp
    Previous ArticleHow to create Animated Social Media Card Hover using HTML CSS and JS
    Coding Stella
    • Website

    Related Posts

    JavaScript

    How to create Animated Social Media Card Hover using HTML CSS and JS

    20 October 2025
    JavaScript

    How to create Animated LogOut Button using HTML CSS and JS

    18 October 2025
    JavaScript

    How to create Animated Firework Diwali using HTML CSS and JS

    16 October 2025
    Add A Comment
    Leave A Reply Cancel Reply

    Trending Post

    Master Frontend in 100 Days Ebook

    2 March 202429K Views

    How to make Modern Login Form using HTML & CSS | Glassmorphism

    11 January 202427K Views

    How to make I love you Animation in HTML CSS & JavaScript

    14 February 202421K Views

    How to make Valentine’s Day Card using HTML & CSS

    13 February 202414K Views
    Follow Us
    • Instagram
    • Facebook
    • YouTube
    • Twitter
    ads
    Featured Post

    How to create Password Validator using HTML CSS & JavaScript

    30 October 2024

    2024’s Game-Changing CSS Frameworks You Can’t Ignore

    18 January 2024

    How to make QR Code Generator using HTML CSS & JavaScript

    14 January 2024

    How to make Monster Alien Star Rating using HTML CSS & JavaScript

    20 April 2024
    Latest Post

    How to create Interactive 3D Galaxy Animation using HTML CSS and JS

    24 October 2025

    How to create Animated Social Media Card Hover using HTML CSS and JS

    20 October 2025

    How to create Animated LogOut Button using HTML CSS and JS

    18 October 2025

    How to create Animated Firework Diwali using HTML CSS and JS

    16 October 2025
    Facebook X (Twitter) Instagram YouTube
    • About Us
    • Privacy Policy
    • Return and Refund Policy
    • Terms and Conditions
    • Contact Us
    • Buy me a coffee
    © 2025 Coding Stella. Made with 💙 by @coding.stella

    Type above and press Enter to search. Press Esc to cancel.

    Ad Blocker Enabled!
    Ad Blocker Enabled!
    Looks like you're using an ad blocker. We rely on advertising to help fund our site.
    Okay! I understood