<div class="blackhole-container" style="width:1000px;height:1000px;position:relative;background-color:#000">
   <canvas style="position:absolute;top:0;left:0;width:100%;height:100%"></canvas>
   <script>
       // Self-executing function to encapsulate the code and keep it from interfering with other scripts.
       (function() {
           // This makes the element self-contained. It finds the canvas inside its own container.
           const container = document.currentScript.parentElement;
           const canvas = container.querySelector('canvas');
           if (!container || !canvas) return;

           const ctx = canvas.getContext('2d');
           
           let particles = [];
           // Using the particle count from your file.
           const particleCount = 50000;

           // --- Optimized Camera Settings ---
           // The camera angle is fixed, so we pre-calculate the sine and cosine once,
           // instead of in every frame. This is much more efficient.
           const angleX = Math.PI / 2; 
           const cosX = Math.cos(angleX);
           const sinX = Math.sin(angleX);

           // These were the values from your file.
           const singularityRadius = 100;
           const photonSphereRadius = 100;

           class Particle {
               constructor(diskType) {
                   this.diskType = diskType; // 'main' or 'perpendicular'
                   this.reset();
               }

               reset() {
                   let radius;
                   // Using the exact size parameters from your file.
                   if (this.diskType === 'main') {
                       radius = photonSphereRadius + Math.random() * (350 / 25);
                   } else {
                       radius = photonSphereRadius + Math.random() * 350;
                   }
                   
                   const angle = Math.random() * Math.PI * 2;
                   const tubeRadius = 15 * (1 - Math.pow(radius / 450, 2));
                   const tubeAngle = Math.random() * Math.PI * 2;
                   
                   if (this.diskType === 'main') {
                       this.x = (radius + Math.cos(tubeAngle) * tubeRadius) * Math.cos(angle);
                       this.z = (radius + Math.cos(tubeAngle) * tubeRadius) * Math.sin(angle);
                       this.y = Math.sin(tubeAngle) * tubeRadius;
                   } else {
                       this.x = (radius + Math.cos(tubeAngle) * tubeRadius) * Math.cos(angle);
                       this.y = (radius + Math.cos(tubeAngle) * tubeRadius) * Math.sin(angle);
                       this.z = Math.sin(tubeAngle) * tubeRadius;
                   }

                   this.speed = (0.001 + (1 / radius) * 0.2) * 10;
                   this.angle = angle;
                   this.radius = radius;
                   this.life = Math.random() * 200 + 100;
                   this.age = 0;
               }
               
               update() {
                   this.age++;
                   this.angle += this.speed;

                   if (this.diskType === 'main') {
                        this.x = this.radius * Math.cos(this.angle);
                        this.z = this.radius * Math.sin(this.angle);
                   } else {
                        this.x = this.radius * Math.cos(this.angle);
                        this.y = this.radius * Math.sin(this.angle);
                   }

                   if (this.age >= this.life || this.radius < photonSphereRadius) {
                       this.reset();
                   }
               }

               project(rotX, rotY, rotZ) {
                   const fov = 1000;
                   let dist = fov + rotZ;
                   this.isBehind = rotZ > 0;
                   
                   if (dist > 0) {
                       this.projectedX = (rotX * fov) / dist + centerX;
                       this.projectedY = (rotY * fov) / dist + centerY;
                       
                       // Gravitational Lensing effect from your code.
                       if (this.isBehind && this.diskType === 'perpendicular') {
                           const dist2D = Math.sqrt(Math.pow(this.projectedX - centerX, 2) + Math.pow(this.projectedY - centerY, 2));
                           if (dist2D < photonSphereRadius * 3) {
                               const pullFactor = Math.pow(photonSphereRadius / dist2D, 2);
                               this.projectedY += (centerY - this.projectedY) * -pullFactor * 1.5;
                           }
                       }

                       this.size = Math.max(0, (2 - rotZ / 500) * 0.6);
                   } else {
                       this.size = -1;
                   }
               }

               draw() {
                   if (this.size <= 0) return;
                   const opacity = (1 - (this.age / this.life)) * 0.9;
                   ctx.fillStyle = `rgba(255, 255, 255, ${opacity})`;
                   ctx.fillRect(this.projectedX, this.projectedY, this.size, this.size);
               }
           }

           let centerX, centerY;
           function init() {
               canvas.width = container.clientWidth;
               canvas.height = container.clientHeight;
               centerX = canvas.width / 2;
               centerY = canvas.height / 2;
               
               particles = [];
               for (let i = 0; i < particleCount; i++) {
                   particles.push(new Particle(i < particleCount / 2 ? 'main' : 'perpendicular'));
               }
               
               animate();
           }
           
           function drawSingularity() {
               ctx.beginPath();
               ctx.arc(centerX, centerY, singularityRadius, 0, Math.PI * 2);
               ctx.fillStyle = '#000';
               ctx.fill();
           }

           function animate() {
               requestAnimationFrame(animate);
               ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
               ctx.fillRect(0, 0, canvas.width, canvas.height);
               
               const foregroundParticles = [];
               const backgroundParticles = [];

               particles.forEach(p => {
                   p.update();

                   // --- Simplified Rotation Logic ---
                   // This replaces the complex camera calculations with a much faster version
                   // that produces the exact same result for the fixed camera angle.
                   const y_rotated = p.y * cosX - p.z * sinX;
                   const z_rotated = p.y * sinX + p.z * cosX;
                   
                   // No Y-axis rotation is needed since the camera is static.
                   const x_final = p.x;
                   const y_final = y_rotated;
                   const z_final = z_rotated;

                   p.project(x_final, y_final, z_final);

                   if (p.isBehind) backgroundParticles.push(p);
                   else foregroundParticles.push(p);
               });
               
               ctx.shadowBlur = 8;
               ctx.shadowColor = "rgba(255, 255, 255, 0.2)";
               backgroundParticles.forEach(p => p.draw());
               
               ctx.shadowBlur = 0;
               drawSingularity();
               
               ctx.shadowBlur = 8;
               ctx.shadowColor = "rgba(255, 255, 255, 0.2)";
               foregroundParticles.forEach(p => p.draw());

               ctx.shadowBlur = 0;
           }
           
           init();

       })();
   </script>
</div>
 

We need your consent to load the translations

We use a third-party service to translate the website content that may collect data about your activity. Please review the details in the privacy policy and accept the service to view the translations.