<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
   <title>3D Wave Frequency Morphing</title>
   <style>
       body {
           margin: 0;
           background-color: #000000;
           font-family: sans-serif;
           color: #c0c0c0;
       }
       #animationWindow {
           position: relative;
           width: 1000px;
           height: 1000px;
           background-color: rgba(0, 0, 0, 0.8);
           border: 1px solid #c0c0c0;
       }
       canvas {
           display: block;
           position: absolute;
           top: 0;
           left: 0;
       }
   </style>
</head>
<body>
   <div id="animationWindow">
       <canvas id="bgCanvas"></canvas>
   </div>

   <script>
       const animationWindow = document.getElementById('animationWindow');
       const canvas = document.getElementById('bgCanvas');
       const ctx = canvas.getContext('2d');
       let particles = [];
       let shapes = [];
       let currentShapeIndex, targetShapeIndex;
       let currentShapeData, targetShapeData;
       let progress = 0;
       let morphSpeed = 0.004;
       let angleX = 0;
       let angleY = 0;
       let particleCount = 25000;

       class Particle {
           constructor(x, y, z) {
               this.x = x;
               this.y = y;
               this.z = z;
               this.projectedX = 0;
               this.projectedY = 0;
               this.size = 1;
           }

           project(x, y, z) {
               const fov = 1000; 
               const dist = fov + z;
               this.projectedX = (x * fov) / dist + canvas.width / 2;
               this.projectedY = (y * fov) / dist + canvas.height / 2;
               this.size = Math.max(0, (1 - z / 1000) * 1.5);
           }

           draw() {
               if (this.projectedX < 0 || this.projectedX > canvas.width || this.projectedY < 0 || this.projectedY > canvas.height) {
                   return;
               }
               ctx.beginPath();
               ctx.arc(this.projectedX, this.projectedY, this.size, 0, Math.PI * 2);
               ctx.fillStyle = `rgba(192, 192, 192, ${this.size / 1.5})`;
               ctx.fill();
           }
       }
       
       // --- Shape Generation: Wave Frequencies ---

       function generateShapes() {
           shapes.push(createWaveFrequency1); // Low frequency
           shapes.push(createWaveFrequency2); // Medium frequency (radial)
           shapes.push(createWaveFrequency3); // High frequency (radial)
           shapes.push(createWaveFrequency4); // Interference pattern
           shapes.push(createWaveFrequency5); // High-frequency grid pattern
       }
       
       // Base function to create a grid of particles
       function createParticleGrid() {
           const tempParticles = [];
           const size = 800;
           const step = Math.sqrt((size * size) / particleCount);
            for (let x = -size / 2; x < size / 2; x += step) {
               for (let z = -size / 2; z < size / 2; z += step) {
                   tempParticles.push({x: x, y: 0, z: z});
               }
           }
           return tempParticles;
       }

       // Frequency 1: A very simple, low-frequency planar wave.
       function createWaveFrequency1() {
           const tempParticles = createParticleGrid();
           tempParticles.forEach(p => {
               p.y = Math.sin(p.x * 0.015) * 100;
           });
           return tempParticles;
       }

       // Frequency 2: A medium-frequency wave radiating from the center.
       function createWaveFrequency2() {
           const tempParticles = createParticleGrid();
           tempParticles.forEach(p => {
               const distance = Math.sqrt(p.x * p.x + p.z * p.z);
               p.y = Math.sin(distance * 0.03) * 100;
           });
           return tempParticles;
       }
       
       // Frequency 3: A higher-frequency wave radiating from the center.
       function createWaveFrequency3() {
           const tempParticles = createParticleGrid();
           tempParticles.forEach(p => {
               const distance = Math.sqrt(p.x * p.x + p.z * p.z);
               p.y = Math.sin(distance * 0.06) * 100;
           });
           return tempParticles;
       }
       
       // Frequency 4: An interference pattern created by two waves crossing.
       function createWaveFrequency4() {
            const tempParticles = createParticleGrid();
           tempParticles.forEach(p => {
               p.y = (Math.sin(p.x * 0.05) + Math.cos(p.z * 0.05)) * 80;
           });
           return tempParticles;
       }

       // Frequency 5: A very high-frequency, complex grid pattern.
       function createWaveFrequency5() {
            const tempParticles = createParticleGrid();
           tempParticles.forEach(p => {
               p.y = (Math.sin(p.x * 0.1) * Math.sin(p.z * 0.1)) * 100;
           });
           return tempParticles;
       }
       
       // --- Core Logic ---

       function morph() {
           progress += morphSpeed;
           if (progress >= 1) {
               progress = 0;
               currentShapeIndex = targetShapeIndex;
               do {
                   targetShapeIndex = Math.floor(Math.random() * shapes.length);
               } while (targetShapeIndex === currentShapeIndex);
               currentShapeData = shapes[currentShapeIndex]();
               targetShapeData = shapes[targetShapeIndex]();
           }

           for (let i = 0; i < particles.length; i++) {
               const p = particles[i];
                if (currentShapeData[i] && targetShapeData[i]) {
                   const start = currentShapeData[i];
                   const end = targetShapeData[i];
                   p.x = start.x + (end.x - start.x) * progress;
                   p.y = start.y + (end.y - start.y) * progress;
                   p.z = start.z + (end.z - start.z) * progress;
                }
           }
       }

       function animate() {
           requestAnimationFrame(animate);
           ctx.clearRect(0, 0, canvas.width, canvas.height);

           morph();
           angleX += 0.001;
           angleY += 0.002;

           const cosX = Math.cos(angleX);
           const sinX = Math.sin(angleX);
           const cosY = Math.cos(angleY);
           const sinY = Math.sin(angleY);

           particles.forEach(p => {
               const x1 = p.x * cosY - p.z * sinY;
               const z1 = p.x * sinY + p.z * cosY;
               const y2 = p.y * cosX - z1 * sinX;
               const z2 = p.y * sinX + z1 * cosX;
               
               p.project(x1, y2, z2);
               p.draw();
           });
       }

       function initialize() {
           canvas.width = animationWindow.clientWidth;
           canvas.height = animationWindow.clientHeight;
           
           particles = [];
           shapes = [];

           generateShapes();
           currentShapeIndex = Math.floor(Math.random() * shapes.length);
           do {
               targetShapeIndex = Math.floor(Math.random() * shapes.length);
           } while (targetShapeIndex === currentShapeIndex);

           currentShapeData = shapes[currentShapeIndex]();
           targetShapeData = shapes[targetShapeIndex]();
           
           // Ensure particle array matches data length
           const initialData = shapes[currentShapeIndex]();
           particleCount = initialData.length;
           for (let i = 0; i < particleCount; i++) {
               particles.push(new Particle(initialData[i].x, initialData[i].y, initialData[i].z));
           }
       }

       window.onload = () => {
           initialize();
           animate();
       };
   </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.