// Version 5: Pure Gaussian Noise - Infinite organic motion without looping const staggerVisualizerEl = document.querySelector('.stagger-visualizer'); const fragment = document.createDocumentFragment(); const grid = [12, 6]; const col = grid[0]; const row = grid[1]; const numberOfElements = col * row; // Store element data for continuous noise const elementData = []; for (let i = 0; i < numberOfElements; i++) { let div = document.createElement('div'); div.className = 'part'; div.dataset.index = i; fragment.appendChild(div); // Each element gets unique noise parameters elementData.push({ element: div, index: i, // Multiple frequency layers for natural-looking noise freq1X: anime.utils.random(0.25, 0.75), freq2X: anime.utils.random(0.1, 0.25), freq1Y: anime.utils.random(0.2, 0.6), freq2Y: anime.utils.random(0.05, 0.2), freq1Rot: anime.utils.random(0.1, 0.4), freq2Rot: anime.utils.random(0.025, 0.1), freqScale: anime.utils.random(0.15, 0.4), freqOpacity: anime.utils.random(0.05, 0.25), // Amplitudes ampX: anime.utils.random(4, 12), ampY: anime.utils.random(4, 12), ampRot: anime.utils.random(20, 45), ampScale: anime.utils.random(0.05, 0.15), // Phase offsets for variation phase1X: Math.random() * Math.PI * 2, phase2X: Math.random() * Math.PI * 2, phase1Y: Math.random() * Math.PI * 2, phase2Y: Math.random() * Math.PI * 2, phase1Rot: Math.random() * Math.PI * 2, phase2Rot: Math.random() * Math.PI * 2, phaseScale: Math.random() * Math.PI * 2, phaseOpacity: Math.random() * Math.PI * 2, // Random drift direction preference driftX: anime.utils.random(-0.5, 0.5), driftY: anime.utils.random(-0.5, 0.5), }); } staggerVisualizerEl.appendChild(fragment); // Continuous Gaussian noise animation using requestAnimationFrame let animationStart = Date.now(); let lastFrameTime = animationStart; const updateNoise = () => { const now = Date.now(); const elapsed = (now - animationStart) / 1000; // Convert to seconds lastFrameTime = now; elementData.forEach((data) => { // Multi-layered sine waves create smooth Gaussian-like noise // Layer 1: Low frequency, high amplitude (main motion) const noiseX1 = Math.sin(elapsed * data.freq1X + data.phase1X) * data.ampX; const noiseY1 = Math.sin(elapsed * data.freq1Y + data.phase1Y) * data.ampY; const noiseRot1 = Math.sin(elapsed * data.freq1Rot + data.phase1Rot) * data.ampRot; // Layer 2: Higher frequency, lower amplitude (detail/jitter) const noiseX2 = Math.sin(elapsed * data.freq2X + data.phase2X) * data.ampX * 0.4; const noiseY2 = Math.sin(elapsed * data.freq2Y + data.phase2Y) * data.ampY * 0.3; const noiseRot2 = Math.sin(elapsed * data.freq2Rot + data.phase2Rot) * data.ampRot * 0.3; // Combine layers const totalX = noiseX1 + noiseX2 + (data.driftX * elapsed * 0.01); const totalY = noiseY1 + noiseY2 + (data.driftY * elapsed * 0.01); const totalRot = noiseRot1 + noiseRot2; // Scale breathing const scale = 1 + (Math.sin(elapsed * data.freqScale + data.phaseScale) * data.ampScale); // Opacity breathing - very subtle const opacity = 0.7 + Math.sin(elapsed * data.freqOpacity + data.phaseOpacity) * 0.25; // Apply transforms directly to style data.element.style.transform = `translateX(${totalX}px) translateY(${totalY}px) rotate(${totalRot}deg) scale(${Math.max(0.3, scale)})`; data.element.style.opacity = opacity; }); requestAnimationFrame(updateNoise); }; // Start the noise animation updateNoise();