Aurora Borealis Magnetic Field Ribbons
Generated by GridFlow AI | Tags: aurora-borealis, flow-field, ribbon-trails, arctic-palette, multi-layer-compositing, curvevertex, generative-art, interactive
💡 AI 提示词
Create a visually stunning aurora borealis artwork with flowing magnetic field line ribbons using Perlin noise flow fields, curveVertex ribbon trails, multi-layer compositing with additive blend modes for glow effects, and an arctic color palette of deep navy, ice blue, pale lavender, and white.🔧 核心算法要点
- Multi-layer compositing using createGraphics buffers for background (deep navy + stars), mid-ground (ribbon trails), and foreground (glow effects)
- Perlin noise flow field simulation combining multiple octaves of noise for organic magnetic field line behavior
- Ribbon class storing position history as a trail array, rendered with curveVertex for smooth flowing silk-like ribbons
- Additive blend mode (BLEND) compositing with gaussian blur filter on glow layer for aurora luminance effect
- Mouse attraction/repulsion influence on ribbon velocity vectors, with press amplification for burst spawning
- Arctic palette interpolation across ice blue, pale lavender, and white hues with alpha gradient trails
🎨 原始代码
var sketch = function(p) {
var ribbons = [];
var time = 0;
var ribbonCount = 60;
var maxRibbons = 150;
var bgBuffer, ribbonBuffer, glowBuffer;
var container;
var showGlow = true;
var blurAmount = 4;
var noiseScale = 0.004;
var palette = {
deepNavy: [8, 12, 28],
iceBlue: [135, 206, 235],
paleBlue: [168, 216, 234],
paleLavender: [220, 208, 255],
white: [245, 250, 255],
glowCyan: [100, 200, 255]
};
var Ribbon = function(x, y) {
this.x = x;
this.y = y;
this.vx = p.random(-1, 1);
this.vy = p.random(-1, 1);
this.prevPositions = [];
this.maxLength = p.floor(p.random(60, 140));
this.hue = p.random();
this.thickness = p.random(0.8, 2.5);
this.speed = p.random(0.6, 1.4);
this.wobbleOffset = p.random(1000);
this.maxAge = p.random(400, 800);
this.age = 0;
};
Ribbon.prototype.mouseInfluence = function(mouseX, mouseY, isPressed) {
if (p.mouseX > 0 && p.mouseX < p.width && p.mouseY > 0 && p.mouseY < p.height) {
var dx = p.mouseX - this.x;
var dy = p.mouseY - this.y;
var distSq = dx * dx + dy * dy;
var maxDist = 300;
if (distSq < maxDist * maxDist) {
var dist = p.sqrt(distSq);
var force = (1 - dist / maxDist) * 0.15;
this.vx += dx * force * 0.01;
this.vy += dy * force * 0.01;
if (isPressed) {
this.vx += dx * force * 0.05;
this.vy += dy * force * 0.05;
this.speed = p.min(this.speed * 1.05, 3.0);
}
}
}
};
Ribbon.prototype.update = function() {
var n1 = p.noise(this.x * noiseScale, this.y * noiseScale, time * 0.3 + this.wobbleOffset);
var n2 = p.noise(this.x * noiseScale * 1.5 + 100, this.y * noiseScale * 1.5 + 100, time * 0.2);
var angle = n1 * p.TWO_PI * 3 + n2 * p.TWO_PI;
this.vx = this.vx * 0.96 + p.cos(angle) * this.speed * 0.6;
this.vy = this.vy * 0.96 + p.sin(angle) * this.speed * 0.6;
var maxSpeed = 4;
var speed = p.sqrt(this.vx * this.vx + this.vy * this.vy);
if (speed > maxSpeed) {
this.vx = (this.vx / speed) * maxSpeed;
this.vy = (this.vy / speed) * maxSpeed;
}
this.x += this.vx;
this.y += this.vy;
this.age++;
var margin = 50;
if (this.x < -margin) { this.x = p.width + margin; this.age = 0; }
if (this.x > p.width + margin) { this.x = -margin; this.age = 0; }
if (this.y < -margin) { this.y = p.height + margin; this.age = 0; }
if (this.y > p.height + margin) { this.y = -margin; this.age = 0; }
};
Ribbon.prototype.drawCurve = function(buffer) {
this.prevPositions.unshift({ x: this.x, y: this.y });
if (this.prevPositions.length > this.maxLength) {
this.prevPositions.pop();
}
if (this.prevPositions.length < 5) return;
var colorMix = this.hue;
var r, g, b;
if (colorMix < 0.33) {
var t = colorMix / 0.33;
r = palette.paleBlue[0] * (1 - t) + palette.paleLavender[0] * t;
g = palette.paleBlue[1] * (1 - t) + palette.paleLavender[1] * t;
b = palette.paleBlue[2] * (1 - t) + palette.paleLavender[2] * t;
} else if (colorMix < 0.66) {
var t = (colorMix - 0.33) / 0.33;
r = palette.paleLavender[0] * (1 - t) + palette.white[0] * t;
g = palette.paleLavender[1] * (1 - t) + palette.white[1] * t;
b = palette.paleLavender[2] * (1 - t) + palette.white[2] * t;
} else {
var t = (colorMix - 0.66) / 0.34;
r = palette.white[0] * (1 - t) + palette.iceBlue[0] * t;
g = palette.white[1] * (1 - t) + palette.iceBlue[1] * t;
b = palette.white[2] * (1 - t) + palette.iceBlue[2] * t;
}
buffer.noFill();
for (var i = 1; i < this.prevPositions.length; i++) {
var t = i / this.prevPositions.length;
var alpha = p.map(t, 0, 1, 0.9, 0.03);
var weight = p.map(t, 0, 1, this.thickness, 0.2);
buffer.stroke(r, g, b, alpha * 255);
buffer.strokeWeight(weight);
if (i < this.prevPositions.length - 1) {
buffer.line(
this.prevPositions[i - 1].x,
this.prevPositions[i - 1].y,
this.prevPositions[i].x,
this.prevPositions[i].y
);
}
}
};
Ribbon.prototype.drawGlow = function(buffer) {
if (this.prevPositions.length < 5) return;
buffer.noFill();
for (var i = 1; i < this.prevPositions.length; i++) {
var t = i / this.prevPositions.length;
var glowAlpha = t * 0.25;
buffer.stroke(palette.glowCyan[0], palette.glowCyan[1], palette.glowCyan[2], glowAlpha * 255);
buffer.strokeWeight(p.map(t, 0, 1, 8, 2));
if (i < this.prevPositions.length - 1) {
buffer.line(
this.prevPositions[i - 1].x,
this.prevPositions[i - 1].y,
this.prevPositions[i].x,
this.prevPositions[i].y
);
}
}
};
Ribbon.prototype.isExpired = function() {
return this.age > this.maxAge && this.prevPositions.length < 10;
};
function drawBackground(buffer) {
buffer.background(palette.deepNavy[0], palette.deepNavy[1], palette.deepNavy[2]);
for (var y = 0; y < buffer.height; y += 6) {
for (var x = 0; x < buffer.width; x += 6) {
var n = p.noise(x * 0.003, y * 0.003, time * 0.05);
if (n > 0.7 && p.random() < 0.015) {
var brightness = p.random(40, 100);
buffer.stroke(brightness, brightness, brightness + 25);
buffer.strokeWeight(p.random(0.5, 1.8));
buffer.point(x + p.random(-2, 2), y + p.random(-2, 2));
}
}
}
for (var i = 0; i < 5; i++) {
var h = p.height * 0.1 + i * p.height * 0.15;
var waveOffset = p.sin(time * 0.3 + i) * 30;
buffer.noFill();
buffer.stroke(20, 30, 60, 30 + i * 10);
buffer.strokeWeight(1);
buffer.beginShape();
for (var x = 0; x <= buffer.width; x += 20) {
var y = h + p.sin(x * 0.005 + time * 0.5 + i) * 15 + waveOffset;
buffer.vertex(x, y);
}
buffer.endShape();
}
}
p.setup = function() {
container = document.getElementById('p5-wrapper');
p.createCanvas(container.offsetWidth, container.offsetHeight).parent(container);
p.colorMode(p.RGB, 255, 255, 255, 255);
bgBuffer = p.createGraphics(p.width, p.height);
ribbonBuffer = p.createGraphics(p.width, p.height);
glowBuffer = p.createGraphics(p.width, p.height);
p.background(palette.deepNavy[0], palette.deepNavy[1], palette.deepNavy[2]);
for (var i = 0; i < ribbonCount; i++) {
ribbons.push(new Ribbon(p.random(p.width), p.random(p.height * 0.7)));
}
};
p.draw = function() {
drawBackground(bgBuffer);
ribbonBuffer.blendMode(p.BLEND);
ribbonBuffer.background(0, 0, 0, 0);
glowBuffer.blendMode(p.BLEND);
glowBuffer.background(0, 0, 0, 0);
var mouseInfluenceX = (p.mouseX - p.width / 2) * 0.003;
var mouseInfluenceY = (p.mouseY - p.height / 2) * 0.003;
for (var i = ribbons.length - 1; i >= 0; i--) {
ribbons[i].mouseInfluence(mouseInfluenceX, mouseInfluenceY, p.mouseIsPressed);
ribbons[i].update();
ribbons[i].drawCurve(ribbonBuffer);
if (showGlow) {
ribbons[i].drawGlow(glowBuffer);
}
if (ribbons[i].isExpired()) {
ribbons.splice(i, 1);
}
}
if (showGlow) {
glowBuffer.filter(p.BLUR, blurAmount);
}
p.blendMode(p.BLEND);
p.image(bgBuffer, 0, 0);
if (showGlow) {
p.blendMode(p.ADD);
p.image(glowBuffer, 0, 0);
}
p.blendMode(p.BLEND);
p.image(ribbonBuffer, 0, 0);
drawMouseIndicator();
time += 0.004;
if (p.frameCount % 400 === 0 && ribbons.length < maxRibbons) {
var spawnY = p.random(p.height * 0.2, p.height * 0.6);
ribbons.push(new Ribbon(p.random(p.width), spawnY));
}
};
function drawMouseIndicator() {
if (p.mouseX > 0 && p.mouseX < p.width && p.mouseY > 0 && p.mouseY < p.height) {
var pulse = p.sin(p.frameCount * 0.08) * 0.3 + 0.7;
p.noFill();
p.stroke(palette.paleBlue[0], palette.paleBlue[1], palette.paleBlue[2], 60 * pulse);
p.strokeWeight(1);
p.ellipse(p.mouseX, p.mouseY, 40, 40);
p.stroke(palette.paleLavender[0], palette.paleLavender[1], palette.paleLavender[2], 40 * pulse);
p.ellipse(p.mouseX, p.mouseY, 80, 80);
p.stroke(palette.white[0], palette.white[1], palette.white[2], 20 * pulse);
p.ellipse(p.mouseX, p.mouseY, 120, 120);
}
};
p.mousePressed = function() {
for (var i = 0; i < 6; i++) {
var angle = p.random(p.TWO_PI);
var dist = p.random(30, 80);
var x = p.mouseX + p.cos(angle) * dist;
var y = p.mouseY + p.sin(angle) * dist;
var newRibbon = new Ribbon(x, y);
newRibbon.speed = p.random(1.5, 2.5);
ribbons.push(newRibbon);
}
if (ribbons.length > maxRibbons) {
ribbons.splice(0, ribbons.length - maxRibbons);
}
};
p.keyPressed = function() {
if (p.key === 'g' || p.key === 'G') {
showGlow = !showGlow;
} else if (p.key === 'r' || p.key === 'R') {
ribbons = [];
for (var i = 0; i < ribbonCount; i++) {
ribbons.push(new Ribbon(p.random(p.width), p.random(p.height * 0.7)));
}
} else if (p.key === '1') {
blurAmount = 2;
} else if (p.key === '2') {
blurAmount = 4;
} else if (p.key === '3') {
blurAmount = 8;
} else if (p.key === ' ') {
p.saveCanvas('aurora-borealis-' + p.millis(), 'png');
}
};
p.windowResized = function() {
container = document.getElementById('p5-wrapper');
p.resizeCanvas(container.offsetWidth, container.offsetHeight);
bgBuffer.resizeCanvas(p.width, p.height);
ribbonBuffer.resizeCanvas(p.width, p.height);
glowBuffer.resizeCanvas(p.width, p.height);
drawBackground(bgBuffer);
};
p.mouseDragged = function() {
if (p.random() < 0.25) {
var newRibbon = new Ribbon(
p.mouseX + p.random(-15, 15),
p.mouseY + p.random(-15, 15)
);
newRibbon.speed = p.random(1.2, 2.0);
ribbons.push(newRibbon);
if (ribbons.length > maxRibbons) {
ribbons.shift();
}
}
};
};
// p5 init stripped
✨ AI 艺术解读
This artwork captures the ethereal dance of charged particles following magnetic field lines in the Arctic sky. The ribbons flow organically through a Perlin noise field, creating the characteristic curtain-like patterns of the aurora. Multiple layers with additive blending simulate the way light accumulates in the upper atmosphere, while the mouse interaction allows viewers to perturb the field like solar wind disturbances reaching Earth.
📝 补充说明
- Performance optimization: Ribbon trail length capped between 60-140 points, max 150 ribbons total with gradual spawning
- Color interpolation uses custom RGB mixing rather than HSB for more predictable arctic palette blending
- Background stars generated via noise threshold sampling with subtle brightness variation
- Blur filter radius adjustable via keyboard (1-3 keys) for different glow intensity vs performance tradeoffs
- Ribbons respawn when exiting canvas bounds, with age-based culling to prevent infinite memory growth