Ouroboros Spiral Tearing Into Deterministic Chaos Attractor
Generated by GridFlow AI | Tags: strange-attractor, chaos-theory, ouroboros, alchemical, flow-field, pixel-rendering, multi-layer-compositing, sacred-geometry
💡 AI 提示词
Ouroboros Spiral Tearing Itself into Deterministic Chaos Attractor - alchemical transmutation with mercury silver, sulfur yellow, and deep cosmic indigo - mathematical strange attractors mapped to flow fields - sacred geometry and recursive voids - cosmic void atmosphere with luminous tendrils🔧 核心算法要点
- Clifford strange attractor with dynamically morphing parameters a, b, c, d creating deterministic chaos
- Pixel-level rendering using loadPixels()/updatePixels() for per-pixel attractor computation with spread kernel
- Multi-layer compositing with 4 offscreen buffers: attractorBuffer, spiralBuffer, glowBuffer, compositeBuffer
- Organic spiral tendrils rendered with curveVertex() and noise-based displacement for silk-ribbon aesthetics
- Recursive void depth rendering with 6 depth levels and counter-rotating geometric patterns
- Advanced blend modes: ADD for additive glow, SCREEN for luminous bloom, BLUR filter for ethereal softness
- Mouse proximity influences tendril warping through angle-based attraction field
- Time-driven parameter oscillation creating the 'tearing into chaos' metamorphosis effect
🎨 原始代码
var sketch = function(p) {
var container;
var attractorBuffer, spiralBuffer, glowBuffer, compositeBuffer;
var params = { a: -1.4, b: 1.6, c: 1.0, d: 0.7 };
var targetParams = { a: -1.4, b: 1.6, c: 1.0, d: 0.7 };
var time = 0;
var attractorHistory = [];
var maxHistory = 50000;
var spiralAngle = 0;
var morphSpeed = 0.003;
var mode = 0;
var burstMode = false;
var burstParticles = [];
var voidDepth = 0;
var palette = {
mercury: null,
sulfur: null,
indigo: null,
void: null
};
p.setup = function() {
container = document.getElementById('p5-wrapper');
p.createCanvas(container.offsetWidth, container.offsetHeight).parent(container);
p.colorMode(p.RGB, 255, 255, 255, 1);
p.noStroke();
attractorBuffer = p.createGraphics(p.width, p.height);
spiralBuffer = p.createGraphics(p.width, p.height);
glowBuffer = p.createGraphics(p.width, p.height);
compositeBuffer = p.createGraphics(p.width, p.height);
initBuffers();
initPalette();
initAttractorHistory();
};
function initBuffers() {
[attractorBuffer, spiralBuffer, glowBuffer, compositeBuffer].forEach(function(buf) {
buf.colorMode(p.RGB, 255, 255, 255, 1);
buf.noStroke();
});
}
function initPalette() {
palette.mercury = p.color(192, 205, 214, 1);
palette.sulfur = p.color(212, 175, 55, 1);
palette.indigo = p.color(20, 10, 60, 1);
palette.void = p.color(5, 3, 15, 1);
}
function initAttractorHistory() {
attractorHistory = [];
var x = p.random(-2, 2);
var y = p.random(-2, 2);
for (var i = 0; i < maxHistory; i++) {
var newX = p.sin(params.a * y) + params.c * p.cos(params.a * x);
var newY = p.sin(params.b * x) + params.d * p.cos(params.b * y);
x = newX;
y = newY;
attractorHistory.push({ x: x, y: y, age: i });
}
}
function updateTargetParams() {
var t = time * 0.0003;
targetParams.a = p.map(p.sin(t * 1.1), -1, 1, -2.5, -0.5);
targetParams.b = p.map(p.sin(t * 0.9), -1, 1, -2.5, 2.5);
targetParams.c = p.map(p.sin(t * 0.7), -1, 1, -1.5, 1.5);
targetParams.d = p.map(p.sin(t * 1.3), -1, 1, -1.5, 2.0);
params.a += (targetParams.a - params.a) * morphSpeed * 60;
params.b += (targetParams.b - params.b) * morphSpeed * 60;
params.c += (targetParams.c - params.c) * morphSpeed * 60;
params.d += (targetParams.d - params.d) * morphSpeed * 60;
}
function computeAttractorStep(x, y) {
var newX = p.sin(params.a * y) + params.c * p.cos(params.a * x);
var newY = p.sin(params.b * x) + params.d * p.cos(params.b * y);
return { x: newX, y: newY };
}
function renderAttractorPixelLevel() {
attractorBuffer.loadPixels();
var d = attractorBuffer.pixels;
var w = attractorBuffer.width;
var h = attractorBuffer.height;
for (var i = 0; i < d.length; i += 4) {
d[i] = 0;
d[i + 1] = 0;
d[i + 2] = 5;
d[i + 3] = 255;
}
var scale = p.min(w, h) * 0.15;
var centerX = w / 2;
var centerY = h / 2;
var step = 3;
for (var iter = 0; iter < maxHistory; iter += step) {
var pt = attractorHistory[iter];
var sx = centerX + pt.x * scale;
var sy = centerY + pt.y * scale;
if (sx >= 0 && sx < w && sy >= 0 && sy < h) {
var idx = (Math.floor(sy) * w + Math.floor(sx)) * 4;
var age = pt.age / maxHistory;
var intensity = p.map(age, 0, 1, 0.8, 0.2);
var colorMix = (p.sin(time * 0.002 + iter * 0.001) + 1) * 0.5;
var r = p.lerp(192, 212, colorMix) * intensity;
var g = p.lerp(205, 175, colorMix) * intensity;
var b = p.lerp(214, 55, colorMix) * intensity;
d[idx] = p.min(255, d[idx] + r);
d[idx + 1] = p.min(255, d[idx + 1] + g);
d[idx + 2] = p.min(255, d[idx + 2] + b);
var spread = 2;
for (var dx = -spread; dx <= spread; dx++) {
for (var dy = -spread; dy <= spread; dy++) {
var nidx = ((Math.floor(sy) + dy) * w + (Math.floor(sx) + dx)) * 4;
if (nidx > 0 && nidx < d.length - 4) {
var dist = p.sqrt(dx * dx + dy * dy);
var falloff = p.max(0, 1 - dist / spread) * 0.3 * intensity;
d[nidx] = p.min(255, d[nidx] + r * falloff);
d[nidx + 1] = p.min(255, d[nidx + 1] + g * falloff);
d[nidx + 2] = p.min(255, d[nidx + 2] + b * falloff);
}
}
}
}
}
attractorBuffer.updatePixels();
}
function renderSpiralOrganicCurves() {
spiralBuffer.background(0, 0, 0, 0);
spiralBuffer.push();
spiralBuffer.translate(spiralBuffer.width / 2, spiralBuffer.height / 2);
var numTendrils = 8;
for (var t = 0; t < numTendrils; t++) {
var baseAngle = (t / numTendrils) * p.TWO_PI + spiralAngle;
spiralBuffer.beginShape();
var steps = 200;
for (var i = 0; i <= steps; i++) {
var progress = i / steps;
var angle = baseAngle + progress * p.TWO_PI * 3;
var radius = progress * p.min(spiralBuffer.width, spiralBuffer.height) * 0.4;
var noiseAngle = angle + p.noise(p.cos(angle) * 0.5 + t, p.sin(angle) * 0.5 + t, time * 0.001) * p.TWO_PI * 0.5;
var noiseRadius = radius * (1 + (p.noise(i * 0.02, t, time * 0.002) - 0.5) * 0.4);
var mx = (p.mouseX - spiralBuffer.width / 2) / (spiralBuffer.width * 0.5);
var my = (p.mouseY - spiralBuffer.height / 2) / (spiralBuffer.height * 0.5);
var mouseAngle = p.atan2(my, mx);
var mouseDist = p.sqrt(mx * mx + my * my);
var angleDiff = p.abs(p.cos((angle - mouseAngle) * 0.5));
noiseRadius += mouseDist * angleDiff * 50 * (1 - progress * 0.5);
var x = p.cos(noiseAngle) * noiseRadius;
var y = p.sin(noiseAngle) * noiseRadius;
var alpha = p.sin(progress * p.PI) * 0.6;
var col = p.lerpColor(palette.mercury, palette.sulfur, 0.3 + 0.3 * p.sin(time * 0.003 + t));
spiralBuffer.stroke(col.red(), col.green(), col.blue(), alpha);
spiralBuffer.strokeWeight(p.map(progress, 0, 1, 4, 1));
spiralBuffer.curveVertex(x, y);
if (i === 0 || i === 1) {
spiralBuffer.curveVertex(x, y);
}
}
spiralBuffer.endShape();
}
spiralBuffer.pop();
}
function renderOuroborosVoid() {
glowBuffer.background(0, 0, 0, 0);
glowBuffer.push();
glowBuffer.translate(glowBuffer.width / 2, glowBuffer.height / 2);
var spiralRadius = p.min(glowBuffer.width, glowBuffer.height) * 0.42;
var segments = 100;
glowBuffer.beginShape();
for (var i = 0; i <= segments; i++) {
var angle = (i / segments) * p.TWO_PI * 4 + time * 0.001;
var r = spiralRadius * (1 - i / segments);
var wobble = p.noise(i * 0.1, time * 0.002) * 20;
var x = p.cos(angle) * (r + wobble);
var y = p.sin(angle) * (r + wobble);
glowBuffer.fill(20, 10, 60, 0.1);
glowBuffer.curveVertex(x, y);
}
glowBuffer.endShape(p.CLOSE);
for (var ring = 0; ring < 5; ring++) {
var ringR = spiralRadius * (1 - ring * 0.15);
glowBuffer.noFill();
glowBuffer.stroke(40 + ring * 20, 30 + ring * 15, 80 + ring * 30, 0.3 - ring * 0.05);
glowBuffer.strokeWeight(2);
glowBuffer.beginShape();
for (var j = 0; j <= 64; j++) {
var a = (j / 64) * p.TWO_PI;
var n = p.noise(p.cos(a) * 2 + ring, p.sin(a) * 2 + ring, time * 0.001) * 30;
glowBuffer.curveVertex(p.cos(a) * (ringR + n), p.sin(a) * (ringR + n));
}
glowBuffer.endShape(p.CLOSE);
}
glowBuffer.pop();
}
function renderBurstEffect() {
if (!burstMode) return;
for (var i = burstParticles.length - 1; i >= 0; i--) {
var part = burstParticles[i];
part.x += part.vx;
part.y += part.vy;
part.life -= 0.02;
part.vx *= 0.98;
part.vy *= 0.98;
if (part.life <= 0) {
burstParticles.splice(i, 1);
continue;
}
var size = part.life * 8;
glowBuffer.push();
glowBuffer.translate(glowBuffer.width / 2, glowBuffer.height / 2);
glowBuffer.fill(212, 175, 55, part.life * 0.8);
glowBuffer.noStroke();
glowBuffer.ellipse(part.x, part.y, size, size);
glowBuffer.pop();
}
if (burstParticles.length === 0) {
burstMode = false;
}
}
function compositeFinalImage() {
compositeBuffer.background(p.red(palette.void), p.green(palette.void), p.blue(palette.void));
compositeBuffer.image(glowBuffer, 0, 0);
compositeBuffer.blendMode(p.ADD);
compositeBuffer.image(attractorBuffer, 0, 0);
compositeBuffer.image(spiralBuffer, 0, 0);
compositeBuffer.blendMode(p.BLEND);
compositeBuffer.filter(p.BLUR, 3);
compositeBuffer.blendMode(p.SCREEN);
compositeBuffer.image(attractorBuffer, 0, 0);
compositeBuffer.image(spiralBuffer, 0, 0);
compositeBuffer.blendMode(p.BLEND);
renderVoidDepthRecursive();
compositeBuffer.push();
compositeBuffer.translate(compositeBuffer.width / 2, compositeBuffer.height / 2);
compositeBuffer.noFill();
compositeBuffer.stroke(192, 205, 214, 0.4);
compositeBuffer.strokeWeight(1);
compositeBuffer.ellipse(0, 0, p.min(compositeBuffer.width, compositeBuffer.height) * 0.85);
compositeBuffer.stroke(212, 175, 55, 0.2);
compositeBuffer.ellipse(0, 0, p.min(compositeBuffer.width, compositeBuffer.height) * 0.87);
compositeBuffer.pop();
}
function renderVoidDepthRecursive() {
compositeBuffer.push();
compositeBuffer.translate(compositeBuffer.width / 2, compositeBuffer.height / 2);
var depthLevels = 6;
for (var d = 0; d < depthLevels; d++) {
var size = p.min(compositeBuffer.width, compositeBuffer.height) * (0.1 + d * 0.12);
var rot = time * 0.0005 * (d % 2 === 0 ? 1 : -1);
var alpha = p.map(d, 0, depthLevels, 0.15, 0.03);
compositeBuffer.push();
compositeBuffer.rotate(rot);
compositeBuffer.noFill();
for (var i = 0; i < 8; i++) {
var angle = (i / 8) * p.TWO_PI + d * 0.2;
var col = i % 2 === 0 ? palette.indigo : palette.mercury;
compositeBuffer.stroke(p.red(col), p.green(col), p.blue(col), alpha);
compositeBuffer.strokeWeight(p.map(d, 0, depthLevels, 2, 0.5));
compositeBuffer.beginShape();
for (var j = 0; j <= 20; j++) {
var t = j / 20;
var r = size * 0.5 * t;
var wave = p.sin(t * p.PI * 3 + time * 0.002) * 20;
var x = p.cos(angle) * (r + wave);
var y = p.sin(angle) * (r + wave);
compositeBuffer.curveVertex(x, y);
}
compositeBuffer.endShape();
}
compositeBuffer.pop();
}
compositeBuffer.pop();
}
function triggerBurst() {
burstMode = true;
burstParticles = [];
var numParticles = 100;
for (var i = 0; i < numParticles; i++) {
var angle = p.random(p.TWO_PI);
var speed = p.random(2, 8);
burstParticles.push({
x: p.random(-100, 100),
y: p.random(-100, 100),
vx: p.cos(angle) * speed,
vy: p.sin(angle) * speed,
life: 1
});
}
mode = (mode + 1) % 3;
voidDepth = p.random(3, 8);
}
function evolveAttractor() {
var lastPt = attractorHistory[attractorHistory.length - 1];
var x = lastPt.x;
var y = lastPt.y;
attractorHistory.shift();
for (var i = 0; i < 50; i++) {
var newPt = computeAttractorStep(x, y);
x = newPt.x;
y = newPt.y;
}
attractorHistory.push({ x: x, y: y, age: 0 });
for (var j = 0; j < attractorHistory.length; j++) {
attractorHistory[j].age++;
}
}
p.draw = function() {
time++;
updateTargetParams();
if (time % 3 === 0) {
evolveAttractor();
}
renderAttractorPixelLevel();
renderSpiralOrganicCurves();
renderOuroborosVoid();
renderBurstEffect();
compositeFinalImage();
p.image(compositeBuffer, 0, 0);
spiralAngle += 0.002;
p.push();
p.noFill();
p.stroke(192, 205, 214, 0.3);
p.strokeWeight(0.5);
var mx = p.mouseX - p.width / 2;
var my = p.mouseY - p.height / 2;
p.line(mx * 0.5, -p.height, mx * 0.5, p.height);
p.line(-p.width, my * 0.5, p.width, my * 0.5);
p.pop();
};
p.mouseMoved = function() {};
p.mousePressed = function() {
triggerBurst();
};
p.keyPressed = function() {
if (p.key === 'r' || p.key === 'R') {
initAttractorHistory();
time = 0;
}
if (p.key === 'm' || p.key === 'M') {
morphSpeed = morphSpeed === 0.003 ? 0.02 : 0.003;
}
if (p.key === ' ') {
mode = (mode + 1) % 3;
}
};
p.windowResized = function() {
p.resizeCanvas(container.offsetWidth, container.offsetHeight);
var oldW = attractorBuffer.width;
var oldH = attractorBuffer.height;
attractorBuffer = p.createGraphics(p.width, p.height);
spiralBuffer = p.createGraphics(p.width, p.height);
glowBuffer = p.createGraphics(p.width, p.height);
compositeBuffer = p.createGraphics(p.width, p.height);
initBuffers();
};
}; // p5 init stripped
✨ AI 艺术解读
This artwork visualizes the philosophical paradox of the Ouroboros - the serpent consuming itself - rendered through the mathematical lens of deterministic chaos theory. The Clifford attractor, with its deceptively simple equations, produces infinitely complex patterns that appear to tear and reform, much like consciousness confronting its own infinite recursion. The alchemical palette of mercury, sulfur, and indigo evokes the ancient art of transmutation, suggesting that chaos itself is the prima materia from which all ordered beauty emerges. The recursive void depths create a sense of cosmic insignificance while the luminous tendrils offer glimpses of transcendent order within the void.
📝 补充说明
- For performance, attractor computation uses 3-pixel step size and only evolves 50 iterations per frame, with history buffer management
- The blend mode sequence (ADD -> BLUR -> SCREEN) creates the signature alchemical glow impossible with single-layer rendering
- Mouse influence uses angle-based falloff rather than simple distance, creating directional flow distortion
- Parameter morphing uses exponential easing for smooth transitions between chaotic regimes
- Object pooling for burst particles prevents garbage collection stutters during click interactions