<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>Mirrored Rings Animation</title>
</head>
<body style="margin: 0; padding: 0; background-color: #000;">
   <mirrored-rings-animation style="width: 1000px; height: 1000px;"></mirrored-rings-animation>

   <script type="module">
       class MirroredRingsAnimation extends HTMLElement {
           constructor() {
               super();
               this.attachShadow({ mode: 'open' });

               // Define the styles for the component's container and canvas
               const style = document.createElement('style');
               style.textContent = `
                   :host {
                       display: block;
                       width: 100%;
                       height: 100%;
                       overflow: hidden;
                   }
                   canvas {
                       display: block;
                       width: 100%;
                       height: 100%;
                       background-color: #000;
                   }
               `;
               this.shadowRoot.appendChild(style);

               // Load the three.js library dynamically. The animation will start once it's loaded.
               const threeScript = document.createElement('script');
               threeScript.src = 'https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js';
               threeScript.onload = () => this.initAnimation();
               this.shadowRoot.appendChild(threeScript);
           }

           initAnimation() {
               // --- SCENE SETUP ---
               const scene = new THREE.Scene();
               const camera = new THREE.PerspectiveCamera(75, this.clientWidth / this.clientHeight, 0.1, 1000);
               const renderer = new THREE.WebGLRenderer({ antialias: true });
               renderer.setSize(this.clientWidth, this.clientHeight);
               this.shadowRoot.appendChild(renderer.domElement); // Append canvas to the component, not the page body

               // --- LIGHTING ---
               const ambientLight = new THREE.AmbientLight(0x404040, 5);
               scene.add(ambientLight);
               const directionalLight = new THREE.DirectionalLight(0xffffff, 2);
               directionalLight.position.set(0, 1, 1).normalize();
               scene.add(directionalLight);
               camera.position.z = 15;

               // --- PARAMETERS ---
               const colors = [0xff5733, 0xff3333, 0x5733ff, 0x33e6];
               const majorRadius = 5.35;
               const rungCount = 107;
               const totalRotation = Math.PI * 2;
               const dumbbells1 = [];
               const dumbbells2 = [];
               let time = 0;
               const swirlSpeed = 0.01;
               const individualRotationSpeed = 6;

               // --- DUMBBELL CREATION FUNCTION ---
               function createDumbbell(color) {
                   const dumbbellGroup = new THREE.Group();
                   const ballGeometry = new THREE.SphereGeometry(0.2, 16, 16);
                   const ballMaterial = new THREE.MeshPhongMaterial({ color: color });
                   const dot1 = new THREE.Mesh(ballGeometry, ballMaterial);
                   const dot2 = new THREE.Mesh(ballGeometry, ballMaterial);
                   const barLength = 1;
                   const rungGeometry = new THREE.CylinderGeometry(0.1, 0.1, barLength, 8);
                   const rungMaterial = new THREE.MeshPhongMaterial({ color: color });
                   const rung = new THREE.Mesh(rungGeometry, rungMaterial);
                   dot1.position.y = barLength / 2;
                   dot2.position.y = -barLength / 2;
                   dumbbellGroup.add(dot1, dot2, rung);
                   return dumbbellGroup;
               }

               // --- OBJECT INITIALIZATION ---
               for (let i = 0; i < rungCount; i++) {
                   const randomColor = colors[Math.floor(Math.random() * colors.length)];
                   const dumbbell1 = createDumbbell(randomColor);
                   const dumbbell2 = createDumbbell(randomColor);
                   dumbbells1.push(dumbbell1);
                   dumbbells2.push(dumbbell2);
                   scene.add(dumbbell1, dumbbell2);
               }

               // --- ANIMATION LOOP ---
               const animate = () => {
                   requestAnimationFrame(animate);
                   time += swirlSpeed;

                   for (let i = 0; i < rungCount; i++) {
                       const t = (i / rungCount) * totalRotation;
                       const animatedTime = t + time;

                       // Ring 1 (XY Plane)
                       const x1 = majorRadius * Math.cos(animatedTime);
                       const y1 = majorRadius * Math.sin(animatedTime);
                       const pos1 = new THREE.Vector3(x1, y1, 0);
                       const pos2 = new THREE.Vector3(-x1, -y1, 0);
                       const dumbbell1 = dumbbells1[i];
                       dumbbell1.position.copy(pos1);
                       const alignmentQuaternion1 = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), pos2.clone().sub(pos1).normalize());
                       const initialXAngle1 = i * (15 * (Math.PI / 180));
                       const continuousRotation1 = time * individualRotationSpeed;
                       const totalXRotation1 = initialXAngle1 + continuousRotation1;
                       const localRotationQuaternion1 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), totalXRotation1);
                       dumbbell1.quaternion.copy(alignmentQuaternion1).multiply(localRotationQuaternion1);

                       // Ring 2 (XZ Plane)
                       const x2_ring = majorRadius * Math.cos(animatedTime);
                       const z2_ring = majorRadius * Math.sin(animatedTime);
                       const pos1_ring2 = new THREE.Vector3(x2_ring, 0, z2_ring);
                       const pos2_ring2 = new THREE.Vector3(-x2_ring, 0, -z2_ring);
                       const dumbbell2 = dumbbells2[i];
                       dumbbell2.position.copy(pos1_ring2);
                       const alignmentQuaternion2 = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), pos2_ring2.clone().sub(pos1_ring2).normalize());
                       const initialXAngle2 = i * (15 * (Math.PI / 180));
                       const continuousRotation2 = time * individualRotationSpeed;
                       const totalXRotation2 = initialXAngle2 + continuousRotation2;
                       const localRotationQuaternion2 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), totalXRotation2);
                       dumbbell2.quaternion.copy(alignmentQuaternion2).multiply(localRotationQuaternion2);
                   }
                   renderer.render(scene, camera);
               };
               animate();

               // --- RESIZE HANDLING ---
               // This makes the animation responsive to the element's container size.
               const resizeObserver = new ResizeObserver(entries => {
                   const entry = entries[0];
                   const { width, height } = entry.contentRect;
                   camera.aspect = width / height;
                   camera.updateProjectionMatrix();
                   renderer.setSize(width, height);
               });
               resizeObserver.observe(this);
           }
       }

       // Register the custom element with the browser
       customElements.define('mirrored-rings-animation', MirroredRingsAnimation);
   </script>
</body>
</html>
 

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.