Topographic Flow Fields
Generated by GridFlow AI | Tags: isoline, contour map, perlin field, flow visualization, emergent pattern, topography, generative art
💡 AI 提示词
Create a generative art piece featuring topographic-style isoline contour maps driven by multiple layered Perlin noise fields, combined with particle physics that follow the flow of the noise gradient.🔧 核心算法要点
- Layer multiple Perlin noise octaves at different scales to create a complex scalar field representing topographic elevation
- Sample the noise field at a grid resolution to detect contour positions where field values cross threshold levels
- Render contour points with varying opacity based on proximity to exact threshold values, creating line-like visual effects
- Initialize particles that sample the noise field to determine movement direction and speed based on local gradient information
- Modulate particle behavior using secondary noise layers to create varied, non-uniform flow patterns
- Trail particles with position history to visualize flow paths through the contour field
- Overlay directional indicators at grid points showing local flow vectors for added visual information
- Fade the background slightly each frame to create organic trail persistence and temporal evolution
🎨 原始代码
var sketch = function(p) {
var particles = [];
var numParticles = 600;
var noiseScale1 = 0.004;
var noiseScale2 = 0.008;
var noiseScale3 = 0.02;
var time = 0;
var timeSpeed = 0.004;
var numContours = 14;
var contourSpacing = 0.12;
var mode = 'hybrid';
var Particle = function(x, y) {
this.pos = p.createVector(x, y);
this.vel = p.createVector(0, 0);
this.acc = p.createVector(0, 0);
this.maxSpeed = 2.2;
this.prevPos = this.pos.copy();
this.life = p.random(200, 400);
this.maxLife = this.life;
this.trail = [];
};
Particle.prototype.run = function() {
this.update();
this.show();
};
Particle.prototype.update = function() {
var n1 = p.noise(this.pos.x * noiseScale1, this.pos.y * noiseScale1, time);
var n2 = p.noise(this.pos.x * noiseScale2, this.pos.y * noiseScale2, time * 1.3 + 50);
var n3 = p.noise(this.pos.x * noiseScale3, this.pos.y * noiseScale3, time * 0.7 + 200);
var angle = n1 * p.TWO_PI * 3;
var strength = (n2 - 0.3) * 1.2;
var modulation = n3;
this.acc.x = p.cos(angle) * strength * (0.5 + modulation);
this.acc.y = p.sin(angle) * strength * (0.5 + modulation);
this.vel.add(this.acc);
this.vel.limit(this.maxSpeed * (0.5 + modulation * 0.5));
this.pos.add(this.vel);
this.trail.push(this.pos.copy());
if (this.trail.length > 25) this.trail.shift();
this.life -= 0.8;
};
Particle.prototype.show = function() {
var lifeRatio = this.life / this.maxLife;
var hue = p.map(p.noise(this.pos.x * 0.01, this.pos.y * 0.01, time * 0.5), 0, 1, 180, 320);
var alpha = lifeRatio * 200;
p.stroke(hue, 80, 95, alpha);
p.strokeWeight(1.5);
if (this.trail.length > 1) {
p.noFill();
p.beginShape();
for (var i = 0; i < this.trail.length; i++) {
p.vertex(this.trail[i].x, this.trail[i].y);
}
p.endShape();
}
};
Particle.prototype.isDead = function() {
return this.life <= 0 || this.pos.x < -10 || this.pos.x > p.width + 10 ||
this.pos.y < -10 || this.pos.y > p.height + 10;
};
var ContourField = function() {
this.resolution = 4;
this.hueBase = 220;
};
ContourField.prototype.getValue = function(x, y) {
var n1 = p.noise(x * noiseScale1, y * noiseScale1, time);
var n2 = p.noise(x * noiseScale2 * 0.5, y * noiseScale2 * 0.5, time * 0.4 + 300);
var n3 = p.noise(x * noiseScale3 * 0.3, y * noiseScale3 * 0.3, time * 0.2 + 600);
return n1 * 0.6 + n2 * 0.3 + n3 * 0.1;
};
ContourField.prototype.display = function() {
for (var y = 0; y < p.height; y += this.resolution * 2) {
for (var x = 0; x < p.width; x += this.resolution * 2) {
var val = this.getValue(x, y);
for (var c = 0; c < numContours; c++) {
var threshold = (c + 0.5) / numContours;
if (Math.abs(val - threshold) < contourSpacing * 0.5) {
var dist = Math.abs(val - threshold);
var alpha = p.map(dist, 0, contourSpacing * 0.5, 180, 20);
var hue = this.hueBase + c * 8 + p.noise(x * 0.005, y * 0.005, time) * 30;
var sat = 60 + p.noise(x * 0.01, y * 0.01) * 30;
p.stroke(hue % 360, sat, 90, alpha);
p.strokeWeight(1 + (1 - dist / (contourSpacing * 0.5)) * 1.5);
p.point(x, y);
}
}
}
}
};
var contourField = new ContourField();
p.setup = function() {
var container = document.getElementById('p5-wrapper');
p.createCanvas(container.offsetWidth, container.offsetHeight).parent(container);
p.colorMode(p.HSB, 360, 100, 100, 255);
p.background(0, 0, 8);
initParticles();
};
function initParticles() {
particles = [];
for (var i = 0; i < numParticles; i++) {
particles.push(new Particle(p.random(p.width), p.random(p.height)));
}
}
p.draw = function() {
p.background(0, 0, 8, 25);
time += timeSpeed;
contourField.display();
for (var i = particles.length - 1; i >= 0; i--) {
particles[i].run();
if (particles[i].isDead()) {
particles.splice(i, 1);
particles.push(new Particle(p.random(p.width), p.random(p.height)));
}
}
drawFlowIndicators();
};
function drawFlowIndicators() {
var spacing = 80;
p.strokeWeight(0.5);
for (var x = spacing / 2; x < p.width; x += spacing) {
for (var y = spacing / 2; y < p.height; y += spacing) {
var n1 = p.noise(x * noiseScale1, y * noiseScale1, time);
var angle = n1 * p.TWO_PI * 2;
var len = 15 + n1 * 15;
var hue = 180 + p.noise(x * 0.01, y * 0.01, time * 0.3) * 120;
var alpha = 40 + p.noise(x * 0.02, y * 0.02, time * 0.5) * 60;
p.stroke(hue % 360, 70, 100, alpha);
var x2 = x + p.cos(angle) * len;
var y2 = y + p.sin(angle) * len;
p.line(x, y, x2, y2);
}
}
}
p.windowResized = function() {
var container = document.getElementById('p5-wrapper');
p.resizeCanvas(container.offsetWidth, container.offsetHeight);
p.background(0, 0, 8);
initParticles();
};
}; // p5 init stripped
✨ AI 艺术解读
This piece visualizes the hidden structure within mathematical noise fields through the lens of topographic cartography. The contour lines reveal the underlying geometry of dimensional space where elevation represents complex interactions between multiple noise frequencies. Particles emerge as explorers navigating this landscape, their trails creating a river-like network that follows the natural gradients carved by the mathematical terrain. The result feels like viewing a living map of an imaginary terrain where time itself shapes the geography.
📝 补充说明
- Using HSB color mode with hue shifts based on position and time creates more organic, flowing color transitions compared to RGB
- The contour detection algorithm using threshold proximity rather than exact value matching produces smoother, more continuous-looking lines
- Particle trail length can be adjusted to balance between showing clear flow patterns and maintaining visual clarity
- Multiple noise octave layering with different time speeds creates quasi-fractal behavior that feels more natural
- The background fade rate controls how quickly old particles disappear, affecting the balance between persistence and freshness in the visualization