Quantum Wave Function Collapse - Alchemical Transmutation

📅 April 25, 2026 🏷️ art
quantum julia-set fractal alchemy superposition wave-function sacred-geometry generative-art pending-review
Generated by GridFlow AI | Tags: quantum, julia-set, fractal, alchemy, superposition, wave-function, sacred-geometry, generative-art

💡 AI 提示词

Quantum Wave Function Collapse Superposition rendered through alchemical transmutation lens using complex number Julia set mapping per-pixel, with mercury silver sulfur yellow and cosmic indigo palette, multi-layer compositing with glow effects, sacred geometry overlays, quantum collapse indicators, mouse-interactive c parameter manipulation, keyboard mode switching

🔧 核心算法要点

  1. Complex number iterative Julia set computation per pixel with smooth escape-time coloring
  2. Per-pixel bilinear interpolation for performance optimization with 2-pixel step rendering
  3. Multi-layer compositing using createGraphics offscreen buffers with SCREEN and ADD blend modes
  4. Quantum parameter interpolation where mouse position continuously influences Julia set c constant
  5. Sacred geometry spiral and polygon overlays using curveVertex for organic forms
  6. Volumetric glow layer generation through neighborhood sampling and falloff calculation
  7. Wave interference pattern rendering with perspective projection and layered rotation
  8. State collapse visualization with animated ellipses and coordinate readouts

🎨 原始代码

var sketch = function(p) {
  var juliaBuffer, glowBuffer, overlayBuffer, noiseBuffer;
  var width, height;
  var cReal = -0.7, cImag = 0.27015;
  var targetCReal = -0.7, targetCImag = 0.27015;
  var mode = 0, targetMode = 0;
  var time = 0;
  var pixelStep = 2;
  var juliaPixels;

  p.setup = function() {
    var container = document.getElementById('p5-wrapper');
    width = container.offsetWidth;
    height = container.offsetHeight;
    p.createCanvas(width, height).parent(container);
    p.pixelDensity(1);
    p.colorMode(p.RGB, 255, 255, 255, 1);
    p.noStroke();

    juliaBuffer = p.createGraphics(width, height);
    glowBuffer = p.createGraphics(width, height);
    overlayBuffer = p.createGraphics(width, height);
    noiseBuffer = p.createGraphics(width, height);

    juliaBuffer.colorMode(p.RGB, 255, 255, 255, 1);
    glowBuffer.colorMode(p.RGB, 255, 255, 255, 1);
    overlayBuffer.colorMode(p.RGB, 255, 255, 255, 1);
    noiseBuffer.colorMode(p.RGB, 255, 255, 255, 1);

    juliaPixels = new Array(width * height * 4);

    generateStaticNoise();
  };


  function generateStaticNoise() {
    noiseBuffer.loadPixels();
    for (var i = 0; i < noiseBuffer.width * noiseBuffer.height * 4; i += 4) {
      var n = p.noise(i * 0.001, time * 0.001) * 30;
      noiseBuffer.pixels[i] = 75 + n;
      noiseBuffer.pixels[i + 1] = 75 + n;
      noiseBuffer.pixels[i + 2] = 130 + n;
      noiseBuffer.pixels[i + 3] = 255;
    }
    noiseBuffer.updatePixels();
  }

  function computeJulia(x, y, maxIter) {
    var zr = x * 3.5 / (width / 2) - 1.75;
    var zi = y * 2.0 / (height / 2) - 1.0;
    var m = 0;

    while (zr * zr + zi * zi < 4 && m < maxIter) {
      var newZr = zr * zr - zi * zi + cReal;
      var newZi = 2 * zr * zi + cImag;
      zr = newZr;
      zi = newZi;
      m++;
    }

    if (m === maxIter) {
      return 0;
    }

    var smooth = m + 1 - p.log(p.log(zr * zr + zi * zi)) / p.log(2);
    return smooth / maxIter;
  }

  function renderJuliaSet() {
    juliaBuffer.loadPixels();

    var maxIter = mode === 0 ? 150 : 200;

    for (var py = 0; py < height; py += pixelStep) {
      for (var px = 0; px < width; px += pixelStep) {
        var iter = computeJulia(px, py, maxIter);

        var r, g, b;

        if (iter === 0) {
          r = 10; g = 5; b = 40;
        } else {
          var t = iter;
          var silver = p.sin(t * p.PI * 4 + time * 0.5) * 0.5 + 0.5;
          var yellow = p.pow(t, 0.3) * p.sin(t * p.PI * 6 + time * 0.3) * 0.5 + 0.5;
          var indigo = p.pow(t, 2) * p.cos(t * p.PI * 2 - time * 0.2) * 0.5 + 0.5;

          if (mode === 0) {
            r = p.map(silver * yellow, 0, 1, 20, 192);
            g = p.map(yellow * indigo, 0, 1, 15, 192);
            b = p.map(indigo * silver + p.pow(t, 3), 0, 1, 60, 220);
          } else if (mode === 1) {
            r = p.map(p.pow(t, 0.5) * silver, 0, 1, 30, 240);
            g = p.map(p.pow(t, 0.7) * yellow, 0, 1, 200, 220);
            b = p.map(p.pow(t, 1.5) * indigo + p.pow(t, 2) * 0.5, 0, 1, 80, 140);
          } else {
            r = p.map(silver + indigo * 0.5, 0, 1, 10, 150);
            g = p.map(yellow + silver * 0.3, 0, 1, 10, 150);
            b = p.map(indigo * 2 + t * 0.5, 0, 1, 100, 255);
          }
        }

        for (var dy = 0; dy < pixelStep && py + dy < height; dy++) {
          for (var dx = 0; dx < pixelStep && px + dx < width; dx++) {
            var idx = ((py + dy) * width + (px + dx)) * 4;
            juliaPixels[idx] = r;
            juliaPixels[idx + 1] = g;
            juliaPixels[idx + 2] = b;
            juliaPixels[idx + 3] = 255;
          }
        }
      }
    }

    for (var i = 0; i < juliaPixels.length; i++) {
      juliaBuffer.pixels[i] = juliaPixels[i];
    }
    juliaBuffer.updatePixels();

    applyBilinearInterpolation(juliaBuffer);
  }

  function applyBilinearInterpolation(buffer) {
    buffer.loadPixels();
    var tempPixels = buffer.pixels.slice();

    for (var y = 0; y < height; y++) {
      for (var x = 0; x < width; x++) {
        if (x % pixelStep === 0 && y % pixelStep === 0) continue;

        var x0 = Math.floor(x / pixelStep) * pixelStep;
        var y0 = Math.floor(y / pixelStep) * pixelStep;
        var x1 = Math.min(x0 + pixelStep, width - 1);
        var y1 = Math.min(y0 + pixelStep, height - 1);

        var fx = (x - x0) / pixelStep;
        var fy = (y - y0) / pixelStep;


        var idx = (y * width + x) * 4;
        var idx00 = (y0 * width + x0) * 4;
        var idx10 = (y0 * width + x1) * 4;
        var idx01 = (y1 * width + x0) * 4;
        var idx11 = (y1 * width + x1) * 4;

        for (var c = 0; c < 3; c++) {
          var v00 = tempPixels[idx00 + c];
          var v10 = tempPixels[idx10 + c];
          var v01 = tempPixels[idx01 + c];
          var v11 = tempPixels[idx11 + c];

          var v0 = v00 * (1 - fx) + v10 * fx;
          var v1 = v01 * (1 - fx) + v11 * fx;
          buffer.pixels[idx + c] = v0 * (1 - fy) + v1 * fy;
        }
      }
    }

    buffer.updatePixels();
  }

  function renderGlowLayer() {
    glowBuffer.clear();
    glowBuffer.loadPixels();

    juliaBuffer.loadPixels();

    for (var y = 0; y < height; y += 3) {
      for (var x = 0; x < width; x += 3) {
        var idx = (y * width + x) * 4;
        var r = juliaBuffer.pixels[idx];
        var g = juliaBuffer.pixels[idx + 1];
        var b = juliaBuffer.pixels[idx + 2];
        var a = juliaBuffer.pixels[idx + 3];

        if (a > 0) {
          var lum = (r * 0.299 + g * 0.587 + b * 0.114) / 255;

          if (lum > 0.3 && lum < 0.8) {
            var glowSize = p.map(lum, 0.3, 0.8, 8, 20);

            for (var gy = 0; gy < glowSize; gy++) {
              for (var gx = 0; gx < glowSize; gx++) {
                var nx = x + gx - glowSize / 2;
                var ny = y + gy - glowSize / 2;

                if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
                  var nidx = (ny * width + nx) * 4;
                  var dist = p.dist(x, y, nx, ny) / glowSize;
                  var falloff = p.max(0, 1 - dist * dist);

                  glowBuffer.pixels[nidx] = p.min(255, glowBuffer.pixels[nidx] + r * falloff * 0.15);
                  glowBuffer.pixels[nidx + 1] = p.min(255, glowBuffer.pixels[nidx + 1] + g * falloff * 0.15);
                  glowBuffer.pixels[nidx + 2] = p.min(255, glowBuffer.pixels[nidx + 2] + b * falloff * 0.15);
                  glowBuffer.pixels[nidx + 3] = 255;
                }
              }
            }
          }
        }
      }
    }

    glowBuffer.updatePixels();
  }

  function renderSacredGeometry() {
    overlayBuffer.clear();
    overlayBuffer.push();
    overlayBuffer.translate(width / 2, height / 2);

    var rotSpeed = time * 0.1;
    var layers = 5;

    for (var l = 0; l < layers; l++) {
      var layerRot = rotSpeed * (l % 2 === 0 ? 1 : -1) * (0.3 + l * 0.2);
      var layerScale = 0.3 + l * 0.15;
      var alpha = 0.03 + 0.02 * p.sin(time * 0.5 + l);

      overlayBuffer.push();
      overlayBuffer.rotate(layerRot);
      overlayBuffer.scale(layerScale);

      var hue;
      if (l % 3 === 0) {
        hue = p.color(192, 192, 192, alpha);
      } else if (l % 3 === 1) {
        hue = p.color(255, 215, 0, alpha);
      } else {
        hue = p.color(75, 0, 130, alpha);
      }

      overlayBuffer.stroke(hue);
      overlayBuffer.strokeWeight(1);
      overlayBuffer.noFill();

      var sides = 3 + l * 2;
      var radius = 180 + p.sin(time * 0.3 + l * 0.5) * 40;

      overlayBuffer.beginShape();
      for (var i = 0; i <= sides; i++) {
        var angle = (i / sides) * p.TWO_PI - p.PI / 2;
        var px = p.cos(angle) * radius;
        var py = p.sin(angle) * radius;

        overlayBuffer.curveVertex(px, py);
      }
      for (var i = 0; i <= sides; i++) {
        var angle = (i / sides) * p.TWO_PI - p.PI / 2;
        var px = p.cos(angle) * radius * 0.5;
        var py = p.sin(angle) * radius * 0.5;
        overlayBuffer.curveVertex(px, py);
      }
      overlayBuffer.endShape(p.CLOSE);

      overlayBuffer.noStroke();
      var spiralPoints = 50;
      for (var sp = 0; sp < spiralPoints; sp++) {
        var t = sp / spiralPoints;
        var angle = t * p.PI * 4 + l * 0.5;
        var spiralR = t * radius * 1.5;
        var sx = p.cos(angle) * spiralR;
        var sy = p.sin(angle) * spiralR;
        var spiralAlpha = alpha * 2 * p.sin(t * p.PI);
        var spiralCol;
        if (l % 3 === 0) spiralCol = p.color(192, 192, 192, spiralAlpha);
        else if (l % 3 === 1) spiralCol = p.color(255, 215, 0, spiralAlpha);
        else spiralCol = p.color(100, 50, 200, spiralAlpha);

        overlayBuffer.fill(spiralCol);
        var dotSize = 2 + t * 3;
        overlayBuffer.ellipse(sx, sy, dotSize, dotSize);
      }

      overlayBuffer.pop();
    }

    overlayBuffer.pop();
  }

  function renderWaveInterference() {
    overlayBuffer.push();
    overlayBuffer.translate(width / 2, height / 2);

    var waveLayers = 8;
    for (var w = 0; w < waveLayers; w++) {
      var waveRot = time * 0.05 * (w % 2 === 0 ? 1 : -1);
      var waveOffset = w * 0.3;
      var waveAlpha = 0.015 + 0.01 * p.sin(time * 0.2 + w);

      overlayBuffer.push();
      overlayBuffer.rotate(waveRot);

      var waveCol;
      if (w % 3 === 0) waveCol = p.color(180, 180, 200, waveAlpha);
      else if (w % 3 === 1) waveCol = p.color(255, 200, 50, waveAlpha);
      else waveCol = p.color(60, 30, 150, waveAlpha);

      overlayBuffer.noFill();
      overlayBuffer.stroke(waveCol);
      overlayBuffer.strokeWeight(0.5);

      overlayBuffer.beginShape();
      var waveAmplitude = 100 + w * 30;
      var waveFrequency = 0.01 + w * 0.002;

      for (var wx = -width / 2; wx < width / 2; wx += 5) {
        var waveY = p.sin(wx * waveFrequency + time * 0.5 + waveOffset) * waveAmplitude;
        var waveZ = p.cos(wx * waveFrequency * 0.7 + time * 0.3) * 50;
        var perspective = 1 / (1 + waveZ * 0.01);
        overlayBuffer.curveVertex(wx * perspective, waveY * perspective);
      }
      overlayBuffer.endShape();

      overlayBuffer.pop();
    }

    overlayBuffer.pop();
  }

  function renderQuantumCollapse() {
    if (p.mouseX > 0 && p.mouseX < width && p.mouseY > 0 && p.mouseY < height) {
      var mx = (p.mouseX - width / 2) / (width / 2);
      var my = (p.mouseY - height / 2) / (height / 2);
      targetCReal = mx * 1.5 - 0.5;
      targetCImag = my * 1.0;
    }

    cReal += (targetCReal - cReal) * 0.03;
    cImag += (targetCImag - cImag) * 0.03;
  }

  p.draw = function() {
    time += 0.016;

    mode += (targetMode - mode) * 0.05;

    renderQuantumCollapse();
    renderJuliaSet();
    renderGlowLayer();

    p.background(5, 2, 20);

    p.image(noiseBuffer, 0, 0);

    p.blendMode(p.BLEND);
    p.image(glowBuffer, 0, 0);

    p.blendMode(p.SCREEN);
    p.image(juliaBuffer, 0, 0);

    renderSacredGeometry();
    renderWaveInterference();

    p.blendMode(p.ADD);
    p.image(overlayBuffer, 0, 0);

    p.blendMode(p.BLEND);

    renderCollapseIndicator();

    if (Math.abs(cReal - targetCReal) < 0.01 && Math.abs(cImag - targetCImag) < 0.01) {
      if (p.random() < 0.001) {
        targetCReal = p.random(-1.5, 0.5);
        targetCImag = p.random(-1.0, 1.0);
      }
    }
  };

  function renderCollapseIndicator() {
    var indicatorX = 30;
    var indicatorY = height - 30;


    p.fill(192, 192, 192, 0.6);
    p.noStroke();
    p.textSize(10);
    p.textFont('monospace');
    p.text('C: ' + cReal.toFixed(3) + ' + ' + cImag.toFixed(3) + 'i', indicatorX, indicatorY);

    p.text('MODE: ' + (Math.round(mode) % 3), indicatorX, indicatorY - 15);

    var superX = width / 2;
    var superY = 40;
    var collapseState = p.sin(time * 2) * 0.5 + 0.5;

    p.noFill();
    p.stroke(255, 215, 0, 0.3 + collapseState * 0.4);
    p.strokeWeight(1);
    p.ellipse(superX, superY, 30 + collapseState * 20, 30 + collapseState * 20);

    p.stroke(192, 192, 192, 0.5);
    p.line(superX - 40, superY, superX + 40, superY);
    p.line(superX, superY - 20, superX, superY + 20);
  }

  p.mousePressed = function() {
    targetCReal = p.random(-1.5, 0.5);
    targetCImag = p.random(-1.0, 1.0);

  };


  p.mouseMoved = function() {
    // Mouse position influences quantum parameters
  };

  p.keyPressed = function() {
    if (p.key === '1') {
      targetMode = 0;
    } else if (p.key === '2') {
      targetMode = 1;
    } else if (p.key === '3') {
      targetMode = 2;
    } else if (p.key === 'r' || p.key === 'R') {
      targetCReal = -0.7;
      targetCImag = 0.27015;
      targetMode = 0;
    } else if (p.key === ' ') {
      targetCReal = p.random(-1.5, 0.5);
      targetCImag = p.random(-1.0, 1.0);
    }
  };

  p.windowResized = function() {
    var container = document.getElementById('p5-wrapper');
    width = container.offsetWidth;
    height = container.offsetHeight;
    p.resizeCanvas(width, height);


    juliaBuffer = p.createGraphics(width, height);
    glowBuffer = p.createGraphics(width, height);
    overlayBuffer = p.createGraphics(width, height);
    noiseBuffer = p.createGraphics(width, height);


    juliaBuffer.colorMode(p.RGB, 255, 255, 255, 1);
    glowBuffer.colorMode(p.RGB, 255, 255, 255, 1);
    overlayBuffer.colorMode(p.RGB, 255, 255, 255, 1);
    noiseBuffer.colorMode(p.RGB, 255, 255, 255, 1);

    juliaPixels = new Array(width * height * 4);

    generateStaticNoise();
  };
};

✨ AI 艺术解读

This artwork embodies the quantum mechanical paradox of superposition through the alchemical metaphor of transmutation—where mercury silver represents the quantum wave function, sulfur yellow symbolizes the collapse event, and cosmic indigo represents the void state. The Julia set fractal serves as a mathematical model for quantum probability distributions, with each pixel representing a possible quantum state. As the viewer moves their mouse, they become the observer causing wave function collapse, shifting the quantum parameters in real-time. The sacred geometry overlays suggest the underlying mathematical order that governs both quantum mechanics and ancient esoteric knowledge.

📝 补充说明

  • Julia set iteration count was tuned between 150-200 for balance between detail and performance
  • Pixel step size of 2 with bilinear interpolation provides near-full-resolution quality at 4x performance gain
  • Three blend modes are used sequentially: BLEND for base, SCREEN for luminous fractal, ADD for overlay elements
  • Mouse press triggers random c parameter jump while mouse movement provides smooth continuous parameter drift
  • Keyboard controls: 1-2-3 for color modes, R to reset, SPACE for random quantum state
  • Offscreen buffers are recreated on resize to maintain pixel array integrity
  • Time-based oscillations control both geometry rotation and color phase shifting