Ouroboros-Recursive-Self-Consumption

📅 April 25, 2026 🏷️ art
ouroboros recursive hyperbolic-geometry non-euclidean alchemical transmutation psychedelic sacred-geometry pending-review
Generated by GridFlow AI | Tags: ouroboros, recursive, hyperbolic-geometry, non-euclidean, alchemical, transmutation, psychedelic, sacred-geometry

💡 AI 提示词

Ouroboros Recursive Self-Consumption - alchemical transmutation with mercury silver, sulfur yellow, and deep cosmic indigo using non-euclidean geometry and hyperbolic transformations

🔧 核心算法要点

  1. Hyperbolic Poincaré disk transformation for non-euclidean coordinate mapping creating recursive geometric tessellation
  2. Multi-layer compositing with three offscreen buffers: cosmic void background, hyperbolic structure, and ouroboros serpents
  3. Pixel-level rendering using loadPixels/updatePixels for fractal noise void texture with depth variation
  4. Recursive serpent spiral generation with bezier-smoothed body segments and gradient color interpolation
  5. Advanced blend modes (ADD and SCREEN) for luminous ethereal glow effects and light accumulation
  6. Sacred geometry patterns with rotating polygonal forms and concentric spiral void core

🎨 原始代码

var sketch = function(p) {
  var canvas, buffer1, buffer2, pixelBuffer;
  var ouroborosPhase = 0;
  var recursiveDepth = 0;
  var hyperbolicStrength = 1;
  var colorVariation = 0;
  var colors = {};

  p.setup = function() {
    var container = document.getElementById('p5-wrapper');
    canvas = p.createCanvas(container.offsetWidth, container.offsetHeight);
    canvas.parent(container);

    p.colorMode(p.RGB, 255, 255, 255, 255);

    buffer1 = p.createGraphics(p.width, p.height);
    buffer2 = p.createGraphics(p.width, p.height);
    pixelBuffer = p.createGraphics(p.width, p.height);

    colors.mercury = p.color(192, 200, 210);
    colors.sulfur = p.color(240, 210, 50);
    colors.indigo = p.color(30, 15, 80);

    p.frameRate(30);
    p.noStroke();
  };

  p.draw = function() {
    drawCosmicVoid();
    drawHyperbolicStructure();
    drawOuroboros();

    p.image(pixelBuffer, 0, 0);
    p.blendMode(p.ADD);
    p.image(buffer1, 0, 0);
    p.blendMode(p.SCREEN);
    p.image(buffer2, 0, 0);
    p.blendMode(p.BLEND);

    drawVoidCore();
    drawRecursionIndicator();

    ouroborosPhase += 0.015;
  };

  function drawCosmicVoid() {
    pixelBuffer.loadPixels();
    var stepSize = 3;

    for (var x = 0; x < pixelBuffer.width; x += stepSize) {
      for (var y = 0; y < pixelBuffer.height; y += stepSize) {
        var noiseVal = p.noise(x * 0.003, y * 0.003, ouroborosPhase * 0.3);
        var noiseVal2 = p.noise(x * 0.007 + 100, y * 0.007, ouroborosPhase * 0.2);

        var idx = (x + y * pixelBuffer.width) * 4;

        var r = p.map(noiseVal, 0, 1, 15, 55);
        var g = p.map(noiseVal, 0, 1, 8, 35);
        var b = p.map(noiseVal2, 0, 1, 45, 120);
        var a = p.map(noiseVal, 0, 1, 180, 220);

        pixelBuffer.pixels[idx] = r;
        pixelBuffer.pixels[idx + 1] = g;
        pixelBuffer.pixels[idx + 2] = b;
        pixelBuffer.pixels[idx + 3] = a;
      }
    }
    pixelBuffer.updatePixels();
  }

  function drawHyperbolicStructure() {
    buffer1.clear();

    var maxDepth = recursiveDepth + 3;
    var discRadius = p.width * 0.45;

    for (var depth = 0; depth < maxDepth; depth++) {
      var baseRadius = p.width * 0.35 * Math.pow(0.65, depth);

      buffer1.push();
      buffer1.translate(p.width / 2, p.height / 2);
      buffer1.rotate(ouroborosPhase * Math.pow(-1, depth) * 0.2);

      var numShapes = 6 + depth * 2;

      for (var i = 0; i < numShapes; i++) {
        var angle = p.TWO_PI / numShapes * i;
        var hyperbolicR = hyperbolicPoincare(baseRadius, angle, hyperbolicStrength * 0.3);

        var x = p.cos(angle) * hyperbolicR;
        var y = p.sin(angle) * hyperbolicR;

        buffer1.push();
        buffer1.translate(x, y);
        buffer1.rotate(angle * 2 + ouroborosPhase * depth * 0.3);

        var shapeSize = baseRadius * 0.25;
        var col = getDepthColor(depth, angle + ouroborosPhase);
        col.setAlpha(p.map(depth, 0, maxDepth - 1, 180, 60));

        buffer1.noStroke();
        buffer1.fill(col);

        if (depth % 3 === 0) {
          drawTriangle(buffer1, shapeSize);
        } else if (depth % 3 === 1) {
          drawPentagon(buffer1, shapeSize);
        } else {
          drawCircleForm(buffer1, shapeSize);
        }

        var glowCol = p.color(p.red(col), p.green(col), p.blue(col), 40);
        buffer1.noFill();
        buffer1.stroke(glowCol);
        buffer1.strokeWeight(2);
        buffer1.ellipse(0, 0, shapeSize * 2.5, shapeSize * 2.5);

        buffer1.pop();
      }

      buffer1.pop();
    }
  }

  function getDepthColor(depth, angle) {
    var t = (p.sin(angle + depth) * 0.5 + 0.5);

    if (depth % 3 === 0) {
      return p.lerpColor(colors.mercury, colors.sulfur, t);
    } else if (depth % 3 === 1) {
      return p.lerpColor(colors.sulfur, colors.indigo, t);
    } else {
      return p.lerpColor(colors.indigo, colors.mercury, t);
    }
  }

  function drawTriangle(buf, size) {
    buf.beginShape();
    for (var i = 0; i < 3; i++) {
      var angle = -p.HALF_PI + p.TWO_PI / 3 * i;
      buf.vertex(p.cos(angle) * size, p.sin(angle) * size);
    }
    buf.endShape(p.CLOSE);
  }

  function drawPentagon(buf, size) {
    buf.beginShape();
    for (var i = 0; i < 5; i++) {
      var angle = -p.HALF_PI + p.TWO_PI / 5 * i;
      buf.vertex(p.cos(angle) * size, p.sin(angle) * size);
    }
    buf.endShape(p.CLOSE);
  }

  function drawCircleForm(buf, size) {
    buf.ellipse(0, 0, size * 1.8, size * 1.8);
  }

  function hyperbolicPoincare(radius, angle, strength) {
    var x = p.cos(angle) * radius;
    var y = p.sin(angle) * radius;

    var zReal = x / (1 + strength * 0.15);
    var zImag = y / (1 + strength * 0.15);
    var zMag = Math.sqrt(zReal * zReal + zImag * zImag);

    var discRadius = p.width * 0.45;
    if (zMag < discRadius) {
      var scale = discRadius / (discRadius - zMag * 0.3);
      return zMag * scale;
    }

    return radius * (1 + p.sin(ouroborosPhase + radius * 0.002) * 0.15);
  }

  function drawOuroboros() {
    buffer2.clear();

    var numSerpents = 2 + recursiveDepth;

    for (var serpent = 0; serpent < numSerpents; serpent++) {
      buffer2.push();
      buffer2.translate(p.width / 2, p.height / 2);
      buffer2.rotate(p.TWO_PI / numSerpents * serpent + ouroborosPhase * 0.2);

      var baseRadius = p.width * (0.12 + serpent * 0.06);

      drawSerpentBody(buffer2, baseRadius, serpent);

      buffer2.pop();
    }
  }

  function drawSerpentBody(buf, baseRadius, serpentIndex) {
    var segments = 180;
    var tailLength = 60;
    var prevTip = null;

    for (var i = 0; i < tailLength; i++) {
      var t = i / tailLength;
      var angle = t * p.TWO_PI * 2.5;
      var radius = baseRadius * Math.pow(t, 0.6);

      var wobble = p.sin(angle * (serpentIndex + 1) * 0.8 + ouroborosPhase * 3) * 8 * (1 - t * 0.5);
      var hyperbolicR = hyperbolicPoincare(radius + wobble, angle, hyperbolicStrength * 0.4);

      var tailTip = p.createVector(
        p.cos(angle) * hyperbolicR,
        p.sin(angle) * hyperbolicR
      );

      var headAngle = angle + p.HALF_PI;
      var eyeOffset = p5.Vector.fromAngle(headAngle + 0.4).mult(7 * (1 - t * 0.3));

      var col = getDepthColor((serpentIndex + Math.floor(t * 3)) % 3, angle + ouroborosPhase);
      var glowCol = p.lerpColor(colors.mercury, colors.sulfur, t);

      buf.push();

      buf.noStroke();
      glowCol.setAlpha(80);
      buf.fill(glowCol);
      buf.ellipse(tailTip.x, tailTip.y, 24 * (1 - t * 0.7), 24 * (1 - t * 0.7));

      col.setAlpha(220);
      buf.fill(col);
      buf.ellipse(tailTip.x, tailTip.y, 16 * (1 - t * 0.6), 16 * (1 - t * 0.6));

      buf.fill(255, 255, 255, 180);
      buf.ellipse(tailTip.x, tailTip.y, 6 * (1 - t * 0.5), 6 * (1 - t * 0.5));

      if (i === 0) {
        buf.fill(colors.indigo);
        buf.ellipse(tailTip.x + eyeOffset.x, tailTip.y + eyeOffset.y, 5, 5);

        buf.fill(255, 240, 100, 230);
        buf.ellipse(tailTip.x + eyeOffset.x + 1, tailTip.y + eyeOffset.y - 1, 2, 2);
      }

      if (prevTip && i < tailLength - 1) {
        col.setAlpha(150);
        buf.stroke(col);
        buf.strokeWeight(12 * (1 - t * 0.7));
        buf.line(tailTip.x, tailTip.y, prevTip.x, prevTip.y);
      }

      buf.pop();

      prevTip = tailTip.copy();
    }
  }

  function drawVoidCore() {
    var depth = recursiveDepth + 2;

    p.push();
    p.translate(p.width / 2, p.height / 2);

    for (var i = 0; i < depth; i++) {
      var radius = 15 * Math.pow(1.6, i);
      var alpha = p.map(i, 0, depth - 1, 200, 25);

      for (var j = 0; j < 3; j++) {
        var angle = ouroborosPhase * 1.5 + j * p.TWO_PI / 3;

        p.push();
        p.rotate(angle);

        var spiralCol = p.lerpColor(colors.indigo, colors.mercury, i / depth);
        spiralCol.setAlpha(alpha);

        p.noFill();
        p.stroke(spiralCol);
        p.strokeWeight(1.5 - i * 0.15);

        p.beginShape();
        for (var k = 0; k < 60; k++) {
          var t = k / 60;
          var spiralR = radius * t;
          var spiralAngle = t * p.PI * 4;

          p.vertex(p.cos(spiralAngle) * spiralR, p.sin(spiralAngle) * spiralR);
        }
        p.endShape();

        p.pop();
      }
    }

    p.pop();
  }

  function drawRecursionIndicator() {
    p.push();
    p.textAlign(p.LEFT);
    p.textSize(11);

    p.fill(200, 190, 80, 200);
    p.noStroke();
    p.text('RECURSION DEPTH: ' + recursiveDepth, 20, p.height - 45);
    p.text('HYPERBOLIC STRENGTH: ' + hyperbolicStrength.toFixed(2), 20, p.height - 28);
    p.text('[CLICK] DEPTH  [R] RESET  [C] CYCLE', 20, p.height - 11);

    p.pop();
  }

  p.mouseMoved = function() {
    var mx = p.map(p.mouseX, 0, p.width, 0.3, 2.5);
    var my = p.map(p.mouseY, 0, p.height, 0.3, 2.5);
    hyperbolicStrength = mx * my;
  };

  p.mouseClicked = function() {
    recursiveDepth = (recursiveDepth + 1) % 4;
  };

  p.keyPressed = function() {
    if (p.key === 'r' || p.key === 'R') {
      recursiveDepth = 0;
      hyperbolicStrength = 1;
    }
    if (p.key === 'c' || p.key === 'C') {
      colorVariation = (colorVariation + 1) % 3;
      if (colorVariation === 1) {
        colors.mercury = p.color(180, 190, 220);
        colors.sulfur = p.color(255, 180, 30);
        colors.indigo = p.color(50, 20, 100);
      } else if (colorVariation === 2) {
        colors.mercury = p.color(220, 220, 240);
        colors.sulfur = p.color(255, 230, 100);
        colors.indigo = p.color(15, 5, 50);
      } else {
        colors.mercury = p.color(192, 200, 210);
        colors.sulfur = p.color(240, 210, 50);
        colors.indigo = p.color(30, 15, 80);
      }
    }
  };

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

    buffer1.resizeCanvas(p.width, p.height);
    buffer2.resizeCanvas(p.width, p.height);
    pixelBuffer.resizeCanvas(p.width, p.height);
  };
};

// p5 init stripped

✨ AI 艺术解读

This artwork manifests the eternal paradox of the Ouroboros through recursive hyperbolic geometry - a serpent that consumes itself while perpetually regenerating through non-euclidean distortions. The cosmic void pulses with fractal noise, while multiple serpents spiral inward through transformed space, their bodies glowing with alchemical mercury and sulfur. The recursive depth controlled by user interaction allows the viewer to peer deeper into the void, experiencing the philosophical vertigo of infinite self-reference.

📝 补充说明

  • Hyperbolic Poincaré transformation maps Cartesian coordinates to hyperbolic space by distorting radius based on distance from center disc
  • Three-layer composite approach isolates rendering passes: background noise (loadPixels), geometric structures (buffer1), and serpent forms (buffer2)
  • Pixel step size of 3 balances visual quality against performance for the cosmic void background
  • Color palette intentionally restricted to three alchemical hues with sinusoidal interpolation for organic transitions
  • Mouse position dynamically modulates hyperbolic distortion strength creating real-time non-euclidean feedback