Subharmonic Void Resonance
Generated by GridFlow AI | Tags: julia set, complex mapping, synesthetic, subharmonics, recursive, iridescent, multi-layer, psychedelic, void resonance
💡 AI 提示词
Create a visually stunning p5.js artwork exploring Synesthetic Frequency Mapping of Subharmonics using complex number mapping with iterative Julia sets, multi-layer compositing, and a psychedelic iridescent palette.🔧 核心算法要点
- Complex number arithmetic (multiplication, addition, magnitude) for Julia set iteration where z = z² + c
- Per-pixel Julia set computation using loadPixels/updatePixels with escape-time algorithm up to 150 iterations
- Multi-layer rendering with three createGraphics buffers: glowBuffer for atmospheric background, juliaBuffer for fractal computation, overlayBuffer for organic tendrils and sacred geometry
- Iridescent color mapping that shifts based on pixel position, iteration count, and time for synesthetic shimmer effects
- Gaussian-inspired glow convolution using nested loops with distance-based falloff for luminous bloom
- Subharmonic visualization using parametric polar curves with spiral perturbations and multiple overlapping harmonic layers
- Screen and Add blend modes for composite layering with vignette post-processing for depth
🎨 原始代码
var sketch = function(p) {
var juliaBuffer, glowBuffer, overlayBuffer;
var time = 0;
var mouseActive = false;
var mode = 0;
var detailLevel = 2;
var cBase = {x: -0.7, y: 0.27015};
var palette = [];
var iterationData = [];
p.setup = function() {
var container = document.getElementById('p5-wrapper');
p.createCanvas(container.offsetWidth, container.offsetHeight).parent(container);
p.pixelDensity(1);
p.colorMode(p.RGB, 255);
juliaBuffer = p.createGraphics(p.width, p.height);
glowBuffer = p.createGraphics(p.width, p.height);
overlayBuffer = p.createGraphics(p.width, p.height);
juliaBuffer.pixelDensity(1);
glowBuffer.pixelDensity(1);
overlayBuffer.pixelDensity(1);
palette = [
{r: 10, g: 5, b: 30},
{r: 60, g: 20, b: 100},
{r: 120, g: 10, b: 180},
{r: 200, g: 50, b: 255},
{r: 255, g: 100, b: 200},
{r: 255, g: 200, b: 50}
];
p.noStroke();
};
p.draw = function() {
time += 0.005;
var mx = p.mouseX / p.width;
var my = p.mouseY / p.height;
var c = {
x: cBase.x + (mx - 0.5) * 0.5 * (mouseActive ? 1 : 0.3),
y: cBase.y + (my - 0.5) * 0.5 * (mouseActive ? 1 : 0.3)
};
drawBackground(glowBuffer);
computeJuliaSet(juliaBuffer, c, time);
applyGlowEffects(glowBuffer, juliaBuffer);
drawOverlay(overlayBuffer, c, time);
compositeAll();
time += 0.003;
};
function drawBackground(buf) {
buf.loadPixels();
var d = buf.pixels;
var scale = 0.3 + 0.1 * Math.sin(time * 0.5);
for (var y = 0; y < buf.height; y += 4) {
for (var x = 0; x < buf.width; x += 4) {
var idx = (y * buf.width + x) * 4;
var ny = y / buf.height - 0.5;
var nx = x / buf.width - 0.5;
var r = 5 + Math.sin(ny * 4 + time * 0.3) * 15;
var g = 3 + Math.cos(nx * 3 + time * 0.4) * 8;
var b = 15 + Math.sin((nx + ny) * 5 + time * 0.2) * 20;
var wave = Math.sin(nx * 8 + ny * 6 + time * 2) * 0.5 + 0.5;
r += wave * 10;
g += wave * 5;
d[idx] = r;
d[idx + 1] = g;
d[idx + 2] = b;
d[idx + 3] = 255;
}
}
buf.updatePixels();
};
function complexMul(a, b) {
return {
x: a.x * b.x - a.y * b.y,
y: a.x * b.y + a.y * b.x
};
}
function complexAdd(a, b) {
return {x: a.x + b.x, y: a.y + b.y};
}
function complexMag(z) {
return Math.sqrt(z.x * z.x + z.y * z.y);
}
function computeJuliaSet(buf, c, t) {
buf.loadPixels();
var d = buf.pixels;
var maxIter = 150;
var escape = 4;
var oscC = {
x: c.x + Math.sin(t * 0.7) * 0.08,
y: c.y + Math.cos(t * 0.5) * 0.08
};
for (var py = 0; py < buf.height; py += detailLevel) {
for (var px = 0; px < buf.width; px += detailLevel) {
var zx = (px / buf.width - 0.5) * 3.5;
var zy = (py / buf.height - 0.5) * 3.5;
var z = {x: zx, y: zy};
var iter = 0;
while (iter < maxIter) {
var z2 = complexMul(z, z);
z = complexAdd(z2, oscC);
if (complexMag(z) > escape) break;
iter++;
}
var hue = iter / maxIter;
var col = mapIterationToColor(iter, maxIter, t + zx * 0.5, zy * 0.5);
for (var dy = 0; dy < detailLevel && py + dy < buf.height; dy++) {
for (var dx = 0; dx < detailLevel && px + dx < buf.width; dx++) {
var pidx = ((py + dy) * buf.width + (px + dx)) * 4;
d[pidx] = col.r;
d[pidx + 1] = col.g;
d[pidx + 2] = col.b;
d[pidx + 3] = iter < maxIter ? 255 : 0;
}
}
}
}
buf.updatePixels();
};
function mapIterationToColor(iter, maxIter, phaseX, phaseY) {
if (iter >= maxIter) return {r: 0, g: 0, b: 0};
var t = iter / maxIter;
var iriShift = Math.sin(phaseX * 3 + time) * 0.5 + 0.5;
var hueIndex = (t * 5 + iriShift * 2) % 6;
var idx1 = Math.floor(hueIndex);
var idx2 = (idx1 + 1) % 6;
var f = hueIndex - idx1;
var c1 = palette[idx1];
var c2 = palette[idx2];
var intensity = Math.pow(t, 0.6) * 2.5;
var shimmer = Math.sin(phaseY * 10 + time * 4) * 0.3 + 0.7;
return {
r: Math.min(255, (c1.r * (1 - f) + c2.r * f) * intensity * shimmer),
g: Math.min(255, (c1.g * (1 - f) + c2.g * f) * intensity * shimmer),
b: Math.min(255, (c1.b * (1 - f) + c2.b * f) * intensity * shimmer)
};
};
function applyGlowEffects(buf, src) {
buf.loadPixels();
var d = buf.pixels;
var s = src.pixels;
var blur = 8;
for (var y = 0; y < buf.height; y += 6) {
for (var x = 0; x < buf.width; x += 6) {
var idx = (y * buf.width + x) * 4;
var si = (y * src.width + x) * 4;
var alpha = s[si + 3] / 255;
if (alpha > 0.1) {
var glowIntensity = alpha * 0.4;
var pulse = Math.sin(time * 3 + x * 0.02 + y * 0.02) * 0.1 + 0.9;
for (var gy = -blur; gy <= blur; gy += 3) {
for (var gx = -blur; gx <= blur; gx += 3) {
var nx = x + gx;
var ny = y + gy;
if (nx >= 0 && nx < buf.width && ny >= 0 && ny < buf.height) {
var dist = Math.sqrt(gx * gx + gy * gy);
var falloff = 1 - (dist / (blur * 1.5));
if (falloff > 0) {
var nidx = (ny * buf.width + nx) * 4;
var weight = falloff * glowIntensity * pulse;
d[nidx] = Math.min(255, d[nidx] + s[si] * weight * 0.5);
d[nidx + 1] = Math.min(255, d[nidx + 1] + s[si + 1] * weight * 0.3);
d[nidx + 2] = Math.min(255, d[nidx + 2] + s[si + 2] * weight * 0.8);
}
}
}
}
}
}
}
buf.updatePixels();
};
function drawOverlay(buf, c, t) {
buf.background(0, 0);
var numSubharmonics = 7;
for (var h = 0; h < numSubharmonics; h++) {
var freq = (h + 1) * 0.5;
var phase = t * freq + h * 0.7;
var amp = 0.15 + 0.1 * Math.sin(t * 0.5 + h);
buf.push();
buf.translate(p.width / 2, p.height / 2);
buf.rotate(phase * 0.2);
buf.beginShape();
var steps = 120;
for (var i = 0; i <= steps; i++) {
var angle = (i / steps) * p.TWO_PI;
var r = 50 + Math.sin(angle * 8 + phase) * 30 * amp + Math.cos(angle * 4 - phase * 2) * 20 * amp;
var x = Math.cos(angle) * r + Math.sin(angle * 3 + phase) * 15;
var y = Math.sin(angle) * r + Math.cos(angle * 2 - phase) * 15;
var iri = Math.sin(angle * 5 + t * 2 + h) * 0.5 + 0.5;
var hue = (h / numSubharmonics + iri * 0.3) % 1;
var col = palette[Math.floor(hue * 6) % 6];
buf.fill(col.r, col.g, col.b, 100 + iri * 80);
buf.vertex(x * 2, y * 2);
}
buf.endShape(p.CLOSE);
buf.noFill();
buf.stroke(255, 255, 255, 30 + iri * 50);
buf.strokeWeight(1);
for (var k = 0; k < 8; k++) {
buf.beginShape();
var tk = t + k * 0.3;
for (var i = 0; i <= steps; i++) {
var angle = (i / steps) * p.TWO_PI;
var r = 80 + Math.sin(angle * 5 + tk * freq) * 40 + Math.cos(angle * 3 - tk * 0.7) * 25;
var spiralX = Math.cos(angle) * r + Math.sin(angle * 6 + tk) * 20;
var spiralY = Math.sin(angle) * r + Math.cos(angle * 4 - tk * 1.3) * 20;
if (i === 0) buf.vertex(spiralX, spiralY);
else buf.vertex(spiralX, spiralY);
}
buf.endShape();
}
buf.pop();
}
var glyphSize = 80;
var glyphSpacing = 150;
for (var gy = 0; gy < p.height / glyphSpacing + 1; gy++) {
for (var gx = 0; gx < p.width / glyphSpacing + 1; gx++) {
var gpx = gx * glyphSpacing + Math.sin(gy * 0.5 + t) * 20;
var gpy = gy * glyphSpacing + Math.cos(gx * 0.5 + t * 0.7) * 20;
var rot = t * 0.2 + gx * 0.3 + gy * 0.4;
buf.push();
buf.translate(gpx, gpy);
buf.rotate(rot);
buf.noFill();
buf.stroke(150, 100, 200, 40);
buf.strokeWeight(1);
var s = glyphSize * 0.4;
buf.line(-s, -s, s, s);
buf.line(s, -s, -s, s);
buf.ellipse(0, 0, s * 0.8, s * 0.8);
buf.pop();
}
}
};
function compositeAll() {
p.background(5, 3, 15);
p.image(glowBuffer, 0, 0);
p.blendMode(p.ADD);
p.image(juliaBuffer, 0, 0);
p.blendMode(p.SCREEN);
p.tint(255, 255, 255, 180);
p.image(overlayBuffer, 0, 0);
p.blendMode(p.BLEND);
drawVignette();
};
function drawVignette() {
p.loadPixels();
var d = p.pixels;
for (var y = 0; y < p.height; y += 6) {
for (var x = 0; x < p.width; x += 6) {
var nx = x / p.width - 0.5;
var ny = y / p.height - 0.5;
var dist = Math.sqrt(nx * nx + ny * ny);
var vignette = 1 - Math.pow(dist * 1.3, 2);
vignette = Math.max(0, Math.min(1, vignette));
var idx = (y * p.width + x) * 4;
d[idx] *= vignette;
d[idx + 1] *= vignette;
d[idx + 2] *= vignette;
}
}
p.updatePixels();
};
p.mouseMoved = function() {
mouseActive = true;
};
p.mouseReleased = function() {
mouseActive = false;
};
p.mousePressed = function() {
cBase.x = (p.mouseX / p.width - 0.5) * 2;
cBase.y = (p.mouseY / p.height - 0.5) * 2;
};
p.keyPressed = function() {
if (p.key === ' ') {
mode = (mode + 1) % 3;
detailLevel = mode === 0 ? 2 : mode === 1 ? 3 : 4;
} else if (p.key === 'r' || p.key === 'R') {
cBase = {x: -0.7, y: 0.27015};
time = 0;
} else if (p.key === 'g' || p.key === 'G') {
palette = palette.reverse();
} else if (p.key === '1') {
palette = [
{r: 10, g: 5, b: 30},
{r: 60, g: 20, b: 100},
{r: 120, g: 10, b: 180},
{r: 200, g: 50, b: 255},
{r: 255, g: 100, b: 200},
{r: 255, g: 200, b: 50}
];
} else if (p.key === '2') {
palette = [
{r: 30, g: 10, b: 50},
{r: 100, g: 30, b: 80},
{r: 180, g: 50, b: 120},
{r: 255, g: 100, b: 160},
{r: 200, g: 180, b: 255},
{r: 100, g: 200, b: 200}
];
} else if (p.key === '3') {
palette = [
{r: 5, g: 10, b: 20},
{r: 20, g: 40, b: 80},
{r: 50, g: 100, b: 180},
{r: 100, g: 200, b: 255},
{r: 200, g: 255, b: 200},
{r: 255, g: 255, b: 100}
];
}
};
p.windowResized = function() {
var container = document.getElementById('p5-wrapper');
p.resizeCanvas(container.offsetWidth, container.offsetHeight);
var oldJulia = juliaBuffer;
var oldGlow = glowBuffer;
var oldOverlay = overlayBuffer;
juliaBuffer = p.createGraphics(p.width, p.height);
glowBuffer = p.createGraphics(p.width, p.height);
overlayBuffer = p.createGraphics(p.width, p.height);
juliaBuffer.pixelDensity(1);
glowBuffer.pixelDensity(1);
overlayBuffer.pixelDensity(1);
};
}; // p5 init stripped
✨ AI 艺术解读
This piece maps the mathematical structure of subharmonic frequencies onto the complex plane, creating a visualization where each iteration represents a vibration in the cosmic void. The Julia set fractal emerges as a living artifact of harmonic decay, its iridescent colors suggesting synesthetic perception of sound frequencies. Mouse movement continuously distorts the fractal parameter c, morphing the void between different resonant states, while the overlay tendrils represent the interference patterns between competing frequencies. The piece evokes the sensation of perceiving multiple dimensions simultaneously—a mathematical hallucination where consciousness interfaces with pure number.
📝 补充说明
- Julia set parameter shifts via mouse create organic morphing between different fractal attractors—press mouse to lock new base parameter
- Detail level cycles with SPACE key (2/3/4 pixel step) trading quality for performance on complex iterations
- Number keys 1-3 swap between three iridescent palettes; G key reverses current palette
- Press R to reset fractal to classic Julia set parameters and zero time counter
- Performance optimization: 6px step for glow computation balances visual richness with frame rate; adjust detailLevel for quality/speed tradeoff