Mycorrhizal Dreams - Perlin Network Growth
Generated by GridFlow AI | Tags: mycorrhizal, perlin-noise, network-growth, emergent, organic-simulation, fungal-networks, bioluminescent
💡 AI 提示词
Create a generative art piece simulating mycorrhizal network growth using Perlin noise fields to drive organic branching patterns. Include node-based growth with energy dynamics, branching rules based on noise thresholds, particle atmosphere, and ethereal glow effects.🔧 核心算法要点
- Use Perlin noise field to determine growth direction angles for each node with time-based offset for dynamic evolution
- Implement node class with energy system where nodes lose energy over time and transfer it to child branches during growth
- Apply branching rules: nodes spawn children when energy > 0.6, age > 30, and noise value exceeds 0.65 threshold
- Create multi-layered glow effect around nodes with varying alpha transparency and pulsing based on bloom phase
- Implement particle system that flows through noise field to create atmospheric depth around the network
- Add seed regeneration when node count drops below 50 to maintain continuous growth cycles
- Calculate flow forces using separate noise layer to create subtle directional drifts in node movement
🎨 原始代码
var sketch = function(p) {
var nodes = [];
var connections = [];
var particles = [];
var noiseScale = 0.002;
var timeOffset = 0;
var maxNodes = 400;
var container;
class Node {
constructor(x, y, parent) {
this.pos = p.createVector(x, y);
this.parent = parent;
this.children = [];
this.energy = p.random(0.5, 1.0);
this.maxEnergy = this.energy;
this.age = 0;
this.radius = p.random(2, 5);
this.noiseOffset = p.random(1000);
this.bloomPhase = p.random(p.TWO_PI);
this.isTerminus = false;
this.terminusTimer = 0;
}
update() {
if (this.isTerminus) {
this.terminusTimer++;
if (this.terminusTimer > p.random(60, 180)) {
this.isTerminus = false;
this.terminusTimer = 0;
}
return;
}
this.age++;
var angle = p.noise(
this.pos.x * noiseScale + this.noiseOffset,
this.pos.y * noiseScale + this.noiseOffset,
timeOffset
) * p.TWO_PI * 3;
angle += p.noise(this.pos.x * 0.01, this.pos.y * 0.01) * 0.5 - 0.25;
var speed = 0.8 + this.energy * 0.5;
var vel = p.createVector(p.cos(angle), p.sin(angle));
vel.mult(speed);
var flowForce = p.createVector(
p.noise(this.pos.x * 0.003, this.pos.y * 0.003, timeOffset * 0.5) - 0.5,
p.noise(this.pos.x * 0.003 + 100, this.pos.y * 0.003 + 100, timeOffset * 0.5) - 0.5
);
flowForce.mult(0.3);
vel.add(flowForce);
this.pos.add(vel);
var edgeMargin = 50;
if (this.pos.x < edgeMargin) vel.x += 0.2;
if (this.pos.x > p.width - edgeMargin) vel.x -= 0.2;
if (this.pos.y < edgeMargin) vel.y += 0.2;
if (this.pos.y > p.height - edgeMargin) vel.y -= 0.2;
this.pos.x = p.constrain(this.pos.x, 10, p.width - 10);
this.pos.y = p.constrain(this.pos.y, 10, p.height - 10);
this.energy -= 0.0008 + (this.children.length * 0.001);
this.energy = p.max(this.energy, 0.1);
this.radius = p.map(this.energy, 0, this.maxEnergy, 2, 5);
var growthNoise = p.noise(this.pos.x * 0.01, this.pos.y * 0.01, timeOffset * 2);
if (this.age > 30 && this.energy > 0.6 && growthNoise > 0.65 && this.children.length < 3 && nodes.length < maxNodes) {
this.growBranch();
}
if (this.energy < 0.3 && p.random() > 0.98) {
this.isTerminus = true;
}
}
growBranch() {
var branchAngle = p.noise(this.pos.x * 0.02, this.pos.y * 0.02, timeOffset * 3) * p.PI - p.PI / 2;
branchAngle += this.parent ? p.atan2(this.pos.y - this.parent.pos.y, this.pos.x - this.parent.pos.x) : 0;
branchAngle += p.random(-0.8, 0.8);
var newX = this.pos.x + p.cos(branchAngle) * 15;
var newY = this.pos.y + p.sin(branchAngle) * 15;
var tooClose = false;
for (var n of nodes) {
if (p.dist(newX, newY, n.pos.x, n.pos.y) < 20) {
tooClose = true;
break;
}
}
if (!tooClose) {
var child = new Node(newX, newY, this);
child.energy = this.energy * p.random(0.6, 0.9);
child.maxEnergy = child.energy;
child.radius = child.energy * 4;
this.children.push(child);
nodes.push(child);
connections.push({from: this, to: child});
}
}
display() {
var pulse = p.sin(p.frameCount * 0.05 + this.bloomPhase) * 0.3 + 0.7;
var glowSize = this.radius * 3 * pulse * this.energy;
for (var i = 3; i > 0; i--) {
var alpha = p.map(i, 0, 3, this.energy * 100, this.energy * 20);
p.fill(120, 180, 140, alpha);
p.noStroke();
p.ellipse(this.pos.x, this.pos.y, glowSize * (i * 0.5), glowSize * (i * 0.5));
}
var hue = p.map(this.energy, 0, 1, 80, 140);
p.fill(hue, 150, 180, 255);
p.noStroke();
p.ellipse(this.pos.x, this.pos.y, this.radius * 2, this.radius * 2);
if (this.isTerminus) {
p.fill(200, 220, 255, 200);
p.ellipse(this.pos.x, this.pos.y, this.radius * 3, this.radius * 3);
}
}
}
class Particle {
constructor() {
this.pos = p.createVector(p.random(p.width), p.random(p.height));
this.vel = p.createVector(0, 0);
this.life = p.random(100, 255);
this.maxLife = this.life;
this.size = p.random(1, 3);
}
update() {
var angle = p.noise(this.pos.x * 0.005, this.pos.y * 0.005, timeOffset) * p.TWO_PI * 2;
this.vel.x = p.cos(angle) * 0.5;
this.vel.y = p.sin(angle) * 0.5;
this.pos.add(this.vel);
this.life -= 0.5;
if (this.life < 0 || this.pos.x < 0 || this.pos.x > p.width || this.pos.y < 0 || this.pos.y > p.height) {
this.pos = p.createVector(p.random(p.width), p.random(p.height));
this.life = this.maxLife;
}
}
display() {
var alpha = p.map(this.life, 0, this.maxLife, 0, 100);
p.fill(180, 200, 180, alpha);
p.noStroke();
p.ellipse(this.pos.x, this.pos.y, this.size, this.size);
}
}
p.setup = function() {
container = document.getElementById('p5-wrapper');
p.createCanvas(container.offsetWidth, container.offsetHeight).parent(container);
p.colorMode(p.HSB, 360, 100, 100, 255);
p.background(20, 30, 15);
var seeds = 5;
for (var i = 0; i < seeds; i++) {
var x = p.random(p.width * 0.2, p.width * 0.8);
var y = p.random(p.height * 0.2, p.height * 0.8);
var seed = new Node(x, y, null);
nodes.push(seed);
}
for (var i = 0; i < 150; i++) {
particles.push(new Particle());
}
};
p.draw = function() {
p.fill(20, 30, 15, 20);
p.noStroke();
p.rect(0, 0, p.width, p.height);
timeOffset += 0.003;
for (var i = 0; i < particles.length; i++) {
particles[i].update();
particles[i].display();
}
for (var i = 0; i < connections.length; i++) {
var conn = connections[i];
var gradient = p.map(conn.to.energy, 0, 1, 0.3, 1);
var alpha = 50 + gradient * 80;
for (var j = 3; j > 0; j--) {
p.strokeWeight(j * 1.5);
p.stroke(100, 120, 140, alpha * 0.2);
p.line(conn.from.pos.x, conn.from.pos.y, conn.to.pos.x, conn.to.pos.y);
}
p.strokeWeight(1);
p.stroke(110, 140, 160, alpha);
p.line(conn.from.pos.x, conn.from.pos.y, conn.to.pos.x, conn.to.pos.y);
}
for (var i = 0; i < nodes.length; i++) {
nodes[i].update();
nodes[i].display();
}
var deadNodes = [];
for (var i = nodes.length - 1; i >= 0; i--) {
if (nodes[i].energy <= 0) {
deadNodes.push(nodes.splice(i, 1)[0]);
}
}
for (var i = connections.length - 1; i >= 0; i--) {
var conn = connections[i];
if (!nodes.includes(conn.from) || !nodes.includes(conn.to)) {
connections.splice(i, 1);
}
}
if (nodes.length < 50 && p.frameCount % 120 === 0) {
var x = p.random(p.width);
var y = p.random(p.height);
var newSeed = new Node(x, y, null);
newSeed.energy = 1.0;
newSeed.maxEnergy = 1.0;
nodes.push(newSeed);
}
p.noFill();
p.strokeWeight(2);
p.stroke(80, 100, 120, 30);
p.rect(5, 5, p.width - 10, p.height - 10);
};
p.windowResized = function() {
container = document.getElementById('p5-wrapper');
p.resizeCanvas(container.offsetWidth, container.offsetHeight);
p.background(20, 30, 15);
};
}; // p5 init stripped
✨ AI 艺术解读
This piece visualizes the underground communication networks formed by mycorrhizal fungi connecting plant ecosystems. The glowing nodes represent nutrient exchange points where fungal threads meet and share resources. As nodes grow and branch following organic noise-driven paths, they form emergent networks that mirror the symbiotic relationships found in forest ecosystems. The ethereal particles flowing through the scene suggest the constant exchange of chemical signals and nutrients that travel through these living networks.
📝 补充说明
- Noise scale of 0.002 creates smooth directional changes while avoiding jittery movement patterns
- Energy dynamics create natural lifecycle where older branches fade as new growth emerges at network edges
- Glow effects use multiple concentric layers with decreasing alpha for soft bioluminescent appearance
- Terminal bloom state creates moments of heightened brightness mimicking spore release events
- Particle count of 150 provides atmospheric depth without overwhelming the primary network visualization