Seismic Wave Propagation

📅 April 23, 2026 🏷️ art
top-ones seismic wave-propagation perlin-noise particle-system fluid-dynamics emergent field-visualization
Generated by GridFlow AI | Tags: seismic, wave-propagation, perlin-noise, particle-system, fluid-dynamics, emergent, field-visualization

💡 AI 提示词

Create a seismic wave propagation visualization using Perlin noise where particles represent earth sensors responding to earthquake epicenters, with multiple moving epicenters creating interference patterns and wave gradients displayed as vector fields.

🔧 核心算法要点

  1. Initialize a grid of particles representing seismic sensors across the canvas with stored base positions
  2. Create multiple moving epicenters that generate circular wave propagation with distance-based attenuation
  3. Calculate wave displacement using layered Perlin noise at different frequencies combined with radial waves from epicenters
  4. Apply lateral shear forces using additional noise layers to create realistic wave distortion
  5. Update particle velocities with acceleration, damping, and spring-like return forces to base positions
  6. Visualize displacement magnitude with HSB color gradients and render gradient field vectors showing wave direction and magnitude
  7. Periodically relocate epicenters to introduce emergent interference patterns

🎨 原始代码

var sketch = function(p) {
    var particles = [];
    var cols, rows;
    var resolution = 10;
    var t = 0;
    var epicenters = [];
    var numEpicenters = 4;
    var waveField = [];
    
    p.setup = function() {
        var container = document.getElementById('p5-wrapper');
        p.createCanvas(container.offsetWidth, container.offsetHeight).parent(container);
        p.colorMode(p.HSB, 360, 100, 100, 100);
        p.background(0);
        
        cols = Math.floor(p.width / resolution);
        rows = Math.floor(p.height / resolution);
        
        for (var i = 0; i < cols; i++) {
            for (var j = 0; j < rows; j++) {
                var particle = {
                    baseX: i * resolution,
                    baseY: j * resolution,
                    x: i * resolution,
                    y: j * resolution,
                    vx: 0,
                    vy: 0,
                    ax: 0,
                    ay: 0,
                    displacement: 0
                };
                particles.push(particle);
            }
        }
        
        for (var e = 0; e < numEpicenters; e++) {
            epicenters.push({
                x: p.random(p.width),
                y: p.random(p.height),
                phase: p.random(1000),
                freq: p.random(0.003, 0.008),
                amp: p.random(25, 45),
                speed: p.random(0.8, 1.5),
                life: 1
            });
        }
        
        for (var i = 0; i < cols; i++) {
            waveField[i] = [];
            for (var j = 0; j < rows; j++) {
                waveField[i][j] = 0;
            }
        }
    };
    
    p.draw = function() {
        p.background(0, 0, 8, 0.15);
        
        t += 0.008;
        
        for (var i = 0; i < cols; i++) {
            for (var j = 0; j < rows; j++) {
                var idx = i + j * cols;
                var part = particles[idx];
                
                var noiseVal1 = p.noise(i * 0.015 + t, j * 0.015, t * 0.5);
                var noiseVal2 = p.noise(i * 0.03 + t * 1.2, j * 0.03, t * 0.8 + 100);
                var noiseVal3 = p.noise(i * 0.008 - t * 0.3, j * 0.008, t * 0.3 + 200);
                
                var wave1 = p.sin(p.dist(part.baseX, part.baseY, p.width * 0.5, p.height * 0.5) * 0.03 - t * 3);
                var wave2 = p.sin(p.dist(part.baseX, part.baseY, p.width * 0.3, p.height * 0.7) * 0.025 - t * 2.5);
                var wave3 = p.sin(p.dist(part.baseX, part.baseY, p.width * 0.7, p.height * 0.3) * 0.035 - t * 4);
                
                var epicenterWave = 0;
                for (var e = 0; e < epicenters.length; e++) {
                    var epic = epicenters[e];
                    var d = p.dist(part.baseX, part.baseY, epic.x, epic.y);
                    var waveFromEpic = p.sin(d * 0.04 - t * epic.speed * 3 + epic.phase);
                    var attenuation = 1 / (1 + d * 0.003);
                    epicenterWave += waveFromEpic * attenuation * epic.amp * epic.life;
                }
                
                var totalDisplacement = 0;
                totalDisplacement += (noiseVal1 - 0.5) * 35;
                totalDisplacement += (noiseVal2 - 0.5) * 20;
                totalDisplacement += (noiseVal3 - 0.5) * 40;
                totalDisplacement += wave1 * 15 + wave2 * 12 + wave3 * 10;
                totalDisplacement += epicenterWave;
                
                var radialAngle = p.atan2(part.baseY - p.height * 0.5, part.baseX - p.width * 0.5);
                
                part.ax = p.cos(radialAngle) * totalDisplacement * 0.08;
                part.ay = p.sin(radialAngle) * totalDisplacement * 0.08;
                
                var shearX = p.noise(i * 0.02, j * 0.02 + t, t * 0.4) - 0.5;
                var shearY = p.noise(i * 0.02 + 500, j * 0.02, t * 0.4 + 500) - 0.5;
                part.ax += shearX * 12;
                part.ay += shearY * 12;
                
                part.vx += part.ax * 0.3;
                part.vy += part.ay * 0.3;
                part.vx *= 0.88;
                part.vy *= 0.88;
                
                part.x += part.vx;
                part.y += part.vy;
                
                var returnForce = 0.025;
                part.x += (part.baseX - part.x) * returnForce;
                part.y += (part.baseY - part.y) * returnForce;
                
                part.displacement = p.dist(part.x, part.y, part.baseX, part.baseY);
                
                waveField[i][j] = totalDisplacement;
            }
        }
        
        var hueBase = (t * 30) % 360;
        
        for (var i = 0; i < cols - 1; i++) {
            for (var j = 0; j < rows - 1; j++) {
                var idx = i + j * cols;
                var part = particles[idx];
                
                var d = part.displacement;
                var speed = p.sqrt(part.vx * part.vx + part.vy * part.vy);
                
                var hue = (hueBase + d * 2 + speed * 8) % 360;
                var sat = 70 + speed * 5;
                var bri = 30 + d * 1.5;
                
                p.stroke(hue, sat, bri, 85);
                
                var nextI = particles[idx + 1];
                var nextJ = particles[idx + cols];
                
                p.line(part.x, part.y, nextI.x, nextI.y);
                p.line(part.x, part.y, nextJ.x, nextJ.y);
                
                if (d > 20) {
                    p.fill(hue, sat, bri + 20, 60);
                    p.noStroke();
                    p.ellipse(part.x, part.y, 3, 3);
                    p.noFill();
                    p.stroke(hue, sat, bri, 85);
                }
            }
        }
        
        var maxField = 0;
        for (var i = 1; i < cols - 1; i++) {
            for (var j = 1; j < rows - 1; j++) {
                var idx = i + j * cols;
                var part = particles[idx];
                var dx = waveField[i + 1][j] - waveField[i - 1][j];
                var dy = waveField[i][j + 1] - waveField[i][j - 1];
                
                var gradMag = p.sqrt(dx * dx + dy * dy);
                if (gradMag > maxField) maxField = gradMag;
            }
        }
        
        for (var i = 1; i < cols - 1; i += 3) {
            for (var j = 1; j < rows - 1; j += 3) {
                var idx = i + j * cols;
                var part = particles[idx];
                var dx = waveField[i + 1][j] - waveField[i - 1][j];
                var dy = waveField[i][j + 1] - waveField[i][j - 1];
                
                var gradMag = p.sqrt(dx * dx + dy * dy);
                var norm = gradMag / (maxField + 0.001);
                
                if (norm > 0.3) {
                    var angle = p.atan2(dy, dx);
                    var len = norm * 25;
                    
                    p.stroke((hueBase + norm * 60) % 360, 90, 80, norm * 100);
                    p.strokeWeight(1.5);
                    p.line(
                        part.x - p.cos(angle) * len * 0.5,
                        part.y - p.sin(angle) * len * 0.5,
                        part.x + p.cos(angle) * len * 0.5,
                        part.y + p.sin(angle) * len * 0.5
                    );
                    
                    p.noStroke();
                    p.fill((hueBase + norm * 60) % 360, 80, 100, norm * 150);
                    p.ellipse(
                        part.x + p.cos(angle) * len * 0.5,
                        part.y + p.sin(angle) * len * 0.5,
                        3, 3
                    );
                    p.noFill();
                    p.strokeWeight(1);
                }
            }
        }
        
        if (p.frameCount % 300 === 0) {
            epicenters[p.floor(p.random(epicenters.length))] = {
                x: p.random(p.width),
                y: p.random(p.height),
                phase: p.random(1000),
                freq: p.random(0.003, 0.008),
                amp: p.random(25, 45),
                speed: p.random(0.8, 1.5),
                life: 1
            };
        }
        
        for (var e = 0; e < epicenters.length; e++) {
            epicenters[e].life = 0.98 + p.noise(e * 10, t) * 0.02;
        }
        
        p.noStroke();
        p.fill(0, 0, 100, 3);
        p.textSize(10);
        p.text("SEISMIC WAVE PROPAGATION - " + p.floor(p.frameRate()) + " FPS", 10, p.height - 10);
    };
    
    p.windowResized = function() {
        var container = document.getElementById('p5-wrapper');
        p.resizeCanvas(container.offsetWidth, container.offsetHeight);
        
        cols = Math.floor(p.width / resolution);
        rows = Math.floor(p.height / resolution);
        
        particles = [];
        for (var i = 0; i < cols; i++) {
            for (var j = 0; j < rows; j++) {
                var particle = {
                    baseX: i * resolution,
                    baseY: j * resolution,
                    x: i * resolution,
                    y: j * resolution,
                    vx: 0,
                    vy: 0,
                    ax: 0,
                    ay: 0,
                    displacement: 0
                };
                particles.push(particle);
            }
        }
        
        waveField = [];
        for (var i = 0; i < cols; i++) {
            waveField[i] = [];
            for (var j = 0; j < rows; j++) {
                waveField[i][j] = 0;
            }
        }
    };
}; // p5 init stripped

✨ AI 艺术解读

This artwork transforms invisible seismic energy into visible wave patterns, visualizing how earthquakes propagate through the earth. The interference of multiple epicenters creates emergent complexity from simple wave equations. Each particle acts as a seismograph, recording the passage of waves while the gradient field reveals the invisible forces of tectonic energy transfer. The piece captures the beautiful violence of geological processes.

📝 补充说明

  • Layering Perlin noise at different octaves creates natural-looking wave interference patterns
  • Distance-based wave attenuation from epicenters simulates real seismic energy decay
  • Drawing gradient vectors only when magnitude exceeds threshold reveals wave fronts elegantly
  • Using HSB color mode with hue shifting over time creates smooth color transitions based on wave intensity
  • Particle damping of 0.88 combined with return force creates organic settling behavior after wave passage