Ego-Death Fractal Tunnel Approaching Singularity

📅 April 25, 2026 🏷️ art
fractal-tunnel domain-warping singularity ego-death ethereal hallucinogenic recursive-void psychedelic-geometry pending-review
Generated by GridFlow AI | Tags: fractal-tunnel, domain-warping, singularity, ego-death, ethereal, hallucinogenic, recursive-void, psychedelic-geometry

💡 AI 提示词

Create a visually stunning p5.js artwork titled "Ego-Death Fractal Tunnel Approaching Singularity" with a hallucinatory monochrome aesthetic. Use pixel-level domain warping as the primary rendering technique, sampling noise at noise-distorted coordinates repeatedly. Implement multi-layer compositing with offscreen buffers, blend modes, and per-pixel computation. Include recursive functions evoking cosmic vastness and ego-death through sacred geometry, vortex rings, and spiral tendrils. The color palette should be a single hue (deep violet-blue at HSB 280) with ethereal bloom and deep shadows. Interactive elements include mouse movement distortion and clicks triggering particle bursts.

🔧 核心算法要点

  1. Fractal Brownian Motion with 5-6 octaves generates multi-scale noise values used for domain warping coordinates
  2. Domain warping applies noise-sampled coordinates as input to subsequent noise evaluations, creating recursive deformation
  3. Per-pixel rendering with loadPixels()/updatePixels() computes tunnel depth, spiral angles, and fractal values at each screen coordinate
  4. Multi-layer compositing uses three offscreen createGraphics() buffers: tunnel base, bloom layer, and composite
  5. Additive blend mode composites bloom over base tunnel, while MULTIPLY applies vignette mask for depth
  6. Procedural curves with bezierVertex() and curveVertex() render organic tendrils and sacred geometry overlays
  7. Singularity points create radial gradients whose influence warps nearby pixels based on distance falloff
  8. Click bursts spawn particles that travel outward while being warped by noise fields and radial forces from center

🎨 原始代码

var sketch = function(p) {
  var tunnelBuffer, bloomBuffer, compositeBuffer, finalMaskBuffer;
  var time = 0;
  var mouseInfluence = 0;
  var clickBurst = [];
  var mode = 0;
  var palette = [];
  var singularities = [];
  var tendrilPaths = [];
  var tunnelSegments = [];
  var noiseTex;
  var intensity = 0;
  var targetIntensity = 0.5;
  
  p.setup = function() {
    var container = document.getElementById('p5-wrapper');
    p.createCanvas(container.offsetWidth, container.offsetHeight).parent(container);
    p.colorMode(p.HSB, 360, 100, 100, 1);
    p.noStroke();
    
    tunnelBuffer = p.createGraphics(p.width, p.height);
    bloomBuffer = p.createGraphics(p.width, p.height);
    compositeBuffer = p.createGraphics(p.width, p.height);
    finalMaskBuffer = p.createGraphics(p.width, p.height);
    
    noiseTex = p.createGraphics(256, 256);
    generateNoiseTexture();
    
    var hue = 280;
    palette = [
      p.color(hue, 15, 8, 0.9),
      p.color(hue, 25, 18, 0.8),
      p.color(hue, 35, 35, 0.7),
      p.color(hue, 45, 55, 0.6),
      p.color(hue, 55, 75, 0.5),
      p.color(hue, 65, 92, 0.4),
      p.color(hue, 70, 100, 0.25)
    ];
    
    for(var i = 0; i < 7; i++) {
      singularities.push({
        x: p.width * (0.35 + 0.3 * i),
        y: p.height * (0.4 + 0.2 * Math.sin(i * 0.7)),
        baseX: p.width * (0.35 + 0.3 * i),
        baseY: p.height * (0.4 + 0.2 * Math.sin(i * 0.7)),
        size: 20 + i * 15,
        phase: i * 1.3,
        depth: i * 0.12
      });
    }
    
    for(var i = 0; i < 12; i++) {
      tendrilPaths.push(createTendrilPath(i));
    }
    
    for(var i = 0; i < 180; i++) {
      tunnelSegments.push({
        angle: (i / 180) * p.TWO_PI,
        radius: 0.6 + Math.random() * 0.35,
        noiseOff: Math.random() * 1000,
        phase: Math.random() * p.TWO_PI
      });
    }
  };
  
  function generateNoiseTexture() {
    noiseTex.loadPixels();
    for(var i = 0; i < noiseTex.pixels.length; i += 4) {
      var v = Math.random() * 255;
      noiseTex.pixels[i] = v;
      noiseTex.pixels[i + 1] = v;
      noiseTex.pixels[i + 2] = v;
      noiseTex.pixels[i + 3] = 255;
    }
    noiseTex.updatePixels();
  }
  
  function createTendrilPath(index) {
    var points = [];
    var startAngle = (index / 12) * p.TWO_PI + Math.random() * 0.3;
    var cx = p.width / 2;
    var cy = p.height / 2;
    
    for(var i = 0; i < 80; i++) {
      var t = i / 80;
      var angle = startAngle + t * 2.5 + p.noise(index * 10, t * 3) * 1.5;
      var radius = p.width * (0.15 + t * 0.55) * (0.8 + p.noise(index, t * 2) * 0.4);
      
      points.push({
        x: cx + Math.cos(angle) * radius,
        y: cy + Math.sin(angle) * radius,
        t: t,
        col: palette[Math.floor(p.random(palette.length))]
      });
    }
    return points;
  }
  
  function fractalNoise(x, y, octaves, persistence) {
    var total = 0;
    var frequency = 1;
    var amplitude = 1;
    var maxValue = 0;
    
    for(var i = 0; i < octaves; i++) {
      total += amplitude * (p.noise(x * frequency, y * frequency) - 0.5) * 2;
      maxValue += amplitude;
      amplitude *= persistence;
      frequency *= 2.1;
    }
    
    return total / maxValue;
  }
  
  function domainWarp(px, py, t, warpStrength) {
    var wx = px / p.width;
    var wy = py / p.height;
    
    var warp1 = fractalNoise(wx * 2.5 + t * 0.08, wy * 2.5, 5, 0.5);
    var warp2 = fractalNoise(wx * 4 + warp1 * 2 + t * 0.12, wy * 4 + warp1 * 2, 4, 0.45);
    
    var finalWarpX = warp1 + warp2 * warpStrength;
    var finalWarpY = warp2 + warp1 * warpStrength;
    
    var newX = px + finalWarpX * warpStrength * 180;
    var newY = py + finalWarpY * warpStrength * 180;
    
    return {x: newX, y: newY, warp: Math.sqrt(finalWarpX * finalWarpX + finalWarpY * finalWarpY)};
  }
  
  function drawTunnelPixels(buf, t) {
    buf.loadPixels();
    var step = 3;
    
    for(var py = 0; py < buf.height; py += step) {
      for(var px = 0; px < buf.width; px += step) {
        var cx = buf.width / 2;
        var cy = buf.height / 2;
        var dx = px - cx;
        var dy = py - cy;
        var dist = Math.sqrt(dx * dx + dy * dy);
        var angle = Math.atan2(dy, dx);
        
        var warped = domainWarp(px, py, t, intensity);
        
        var depth = dist / (p.width * 0.7);
        depth = Math.pow(depth, 0.6);
        
        var spiralAngle = angle + t * 0.5 + warped.warp * 3;
        var spiralRadius = (0.3 + 0.4 * (1 - depth)) * p.width * warped.warp;
        
        var spiralOffset = Math.sin(spiralAngle * 8 + t) * spiralRadius * 0.3;
        var tunnelInfluence = Math.sin(spiralAngle * 3 + t * 0.3) * 0.5 + 0.5;
        
        var fractalVal = fractalNoise(
          px * 0.008 + spiralAngle * 0.5,
          py * 0.008 + depth * 3 + t * 0.2,
          6, 0.55
        );
        
        var singularitiesTotal = 0;
        for(var s = 0; s < singularities.length; s++) {
          var sg = singularities[s];
          var sdx = warped.x - sg.x;
          var sdy = warped.y - sg.y;
          var sdist = Math.sqrt(sdx * sdx + sdy * sdy);
          var influence = Math.max(0, 1 - sdist / (sg.size * (1 + warped.warp * 0.5)));
          singularitiesTotal += influence * influence * sg.depth;
        }
        
        var value = fractalVal * 0.4 + tunnelInfluence * 0.35 + singularitiesTotal * 0.25;
        value *= (1 - depth * 0.7);
        value *= 0.7 + mouseInfluence * 0.3;
        
        var mouseDist = Math.sqrt(
          Math.pow(px - p.mouseX, 2) + Math.pow(py - p.mouseY, 2)
        );
        var mouseGlow = Math.max(0, 1 - mouseDist / (p.width * 0.25));
        value += mouseGlow * 0.15 * intensity;
        
        var hueShift = fractalNoise(px * 0.003, py * 0.003 + t * 0.1, 3, 0.5) * 20;
        var col = p.color(280 + hueShift, 35 + depth * 30, value * 100, value * 0.9);
        
        for(var dy2 = 0; dy2 < step && py + dy2 < buf.height; dy2++) {
          for(var dx2 = 0; dx2 < step && px + dx2 < buf.width; dx2++) {
            var idx = ((py + dy2) * buf.width + (px + dx2)) * 4;
            buf.pixels[idx] = p.red(col);
            buf.pixels[idx + 1] = p.green(col);
            buf.pixels[idx + 2] = p.blue(col);
            buf.pixels[idx + 3] = p.alpha(col);
          }
        }
      }
    }
    
    buf.updatePixels();
  }
  
  function drawBloom(buf, source, t) {
    buf.background(0);
    buf.image(source, 0, 0);
    buf.filter(p.BLUR, 12 + Math.sin(t * 0.5) * 4);
    buf.loadPixels();
    for(var i = 0; i < buf.pixels.length; i += 16) {
      var brightness = (buf.pixels[i] + buf.pixels[i + 1] + buf.pixels[i + 2]) / 3;
      if(brightness > 60) {
        var boost = 1 + (brightness - 60) / 40;
        buf.pixels[i] = Math.min(255, buf.pixels[i] * boost);
        buf.pixels[i + 1] = Math.min(255, buf.pixels[i + 1] * boost);
        buf.pixels[i + 2] = Math.min(255, buf.pixels[i + 2] * boost);
      }
    }
    buf.updatePixels();
  }
  
  function drawProceduralCurves(buf, t) {
    buf.push();
    buf.translate(buf.width / 2, buf.height / 2);
    
    for(var ti = 0; ti < tendrilPaths.length; ti++) {
      var path = tendrilPaths[ti];
      if(path.length < 2) continue;
      
      buf.noFill();
      
      for(var seg = 0; seg < path.length - 1; seg++) {
        var p1 = path[seg];
        var p2 = path[seg + 1];
        
        var segDist = Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
        var segAngle = Math.atan2(p2.y - p1.y, p2.x - p1.x);
        
        var dynamicAngle1 = segAngle + p.noise(ti * 0.3, seg * 0.1, t * 0.2) * 0.4;
        var dynamicAngle2 = dynamicAngle1 + p.noise(ti * 0.3, (seg + 1) * 0.1, t * 0.2) * 0.4;
        
        var cp1x = p1.x + Math.cos(dynamicAngle1) * segDist * 0.4;
        var cp1y = p1.y + Math.sin(dynamicAngle1) * segDist * 0.4;
        var cp2x = p2.x - Math.cos(dynamicAngle2) * segDist * 0.4;
        var cp2y = p2.y - Math.sin(dynamicAngle2) * segDist * 0.4;
        
        var brightness = (1 - p1.t * 0.6) * 80 * intensity;
        var strokeAlpha = (1 - p1.t * 0.8) * 0.6;
        
        buf.stroke(p1.col);
        buf.strokeWeight((1 - p1.t * 0.7) * 3 * intensity);
        buf.bezier(p1.x, p1.y, cp1x, cp1y, cp2x, cp2y, p2.x, p2.y);
      }
    }
    
    buf.noStroke();
    for(var s = 0; s < singularities.length; s++) {
      var sg = singularities[s];
      var breathe = 0.85 + Math.sin(t * 0.8 + sg.phase) * 0.15;
      var sgSize = sg.size * breathe * (0.8 + mouseInfluence * 0.4);
      
      var gradient = buf.drawingContext.createRadialGradient(sg.x, sg.y, 0, sg.x, sg.y, sgSize);
      gradient.addColorStop(0, 'rgba(310, 80%, 100%, 0.9)');
      gradient.addColorStop(0.3, 'rgba(300, 60%, 70%, 0.5)');
      gradient.addColorStop(0.6, 'rgba(290, 40%, 40%, 0.2)');
      gradient.addColorStop(1, 'rgba(280, 20%, 10%, 0)');
      
      buf.drawingContext.fillStyle = gradient;
      buf.drawingContext.beginPath();
      buf.drawingContext.arc(sg.x, sg.y, sgSize, 0, Math.PI * 2);
      buf.drawingContext.fill();
    }
    
    buf.pop();
  }
  
  function drawVortexRings(buf, t) {
    buf.push();
    buf.translate(buf.width / 2, buf.height / 2);
    
    for(var r = 0; r < 8; r++) {
      var radius = 30 + r * 45 + Math.sin(t * 0.3 + r) * 15;
      var ringBrightness = (1 - r / 8) * 0.6 * intensity;
      
      buf.noFill();
      buf.stroke(280, 40, ringBrightness * 100, ringBrightness * 0.7);
      buf.strokeWeight(2 - r * 0.15);
      
      buf.beginShape();
      for(var a = 0; a <= 64; a++) {
        var angle = (a / 64) * p.TWO_PI;
        var noiseVal = p.noise(
          Math.cos(angle) * 2 + r,
          Math.sin(angle) * 2 + r,
          t * 0.1
        );
        var warp = 1 + noiseVal * 0.4;
        var rx = Math.cos(angle) * radius * warp;
        var ry = Math.sin(angle) * radius * warp;
        buf.curveVertex(rx, ry);
        if(a > 0) buf.curveVertex(rx, ry);
      }
      buf.endShape();
    }
    buf.pop();
  }
  
  function drawSacredGeometry(buf, t) {
    buf.push();
    buf.translate(buf.width / 2, buf.height / 2);
    buf.rotate(t * 0.05);
    
    for(var layer = 0; layer < 5; layer++) {
      var layerScale = 0.4 + layer * 0.15;
      var baseRadius = p.width * layerScale * 0.25;
      var rotation = layer * 0.4 + t * 0.1;
      
      buf.noFill();
      var alpha = (1 - layer / 5) * 0.4 * intensity;
      buf.stroke(290, 50, 70, alpha);
      buf.strokeWeight(1.5 - layer * 0.2);
      
      buf.beginShape();
      for(var i = 0; i <= 12; i++) {
        var angle = (i / 12) * p.TWO_PI + rotation;
        var noiseVal = p.noise(i * 0.3, layer, t * 0.15);
        var radius = baseRadius * (0.9 + noiseVal * 0.2);
        var x = Math.cos(angle) * radius;
        var y = Math.sin(angle) * radius;
        buf.curveVertex(x, y);
        if(i > 0) buf.curveVertex(x, y);
      }
      buf.endShape();
      
      if(layer % 2 === 0) {
        buf.beginShape();
        for(var i = 0; i <= 6; i++) {
          var angle = (i / 6) * p.TWO_PI + rotation * 1.5;
          var x = Math.cos(angle) * baseRadius * 0.7;
          var y = Math.sin(angle) * baseRadius * 0.7;
          buf.curveVertex(x, y);
          if(i > 0) buf.curveVertex(x, y);
        }
        buf.endShape();
      }
    }
    buf.pop();
  }
  
  function drawClickBurst(buf) {
    buf.push();
    for(var b = clickBurst.length - 1; b >= 0; b--) {
      var burst = clickBurst[b];
      burst.life -= 0.015;
      
      if(burst.life <= 0) {
        clickBurst.splice(b, 1);
        continue;
      }
      
      var count = Math.floor(burst.particles.length);
      for(var i = 0; i < count; i++) {
        var pt = burst.particles[i];
        pt.x += pt.vx;
        pt.y += pt.vy;
        pt.vx *= 0.96;
        pt.vy *= 0.96;
        pt.vy += 0.02;
        
        var dist = Math.sqrt(Math.pow(pt.x - p.width / 2, 2) + Math.pow(pt.y - p.height / 2, 2));
        var angle = Math.atan2(pt.y - p.height / 2, pt.x - p.width / 2);
        
        var warp = p.noise(pt.x * 0.01 + time * 0.1, pt.y * 0.01) * 20;
        pt.x += Math.cos(angle) * warp * 0.1;
        pt.y += Math.sin(angle) * warp * 0.1;
        
        var col = p.color(280, 50, burst.life * 100, burst.life * 0.6);
        buf.noStroke();
        buf.fill(col);
        var sz = pt.size * burst.life * intensity;
        buf.ellipse(pt.x, pt.y, sz, sz);
      }
    }
    buf.pop();
  }
  
  function updateSingularities() {
    for(var i = 0; i < singularities.length; i++) {
      var sg = singularities[i];
      var targetX = sg.baseX + Math.sin(time * 0.02 + sg.phase) * 50;
      var targetY = sg.baseY + Math.cos(time * 0.015 + sg.phase * 0.7) * 40;
      
      var mouseAngle = Math.atan2(p.mouseY - sg.baseY, p.mouseX - sg.baseX);
      var mouseDist = Math.sqrt(Math.pow(p.mouseX - sg.baseX, 2) + Math.pow(p.mouseY - sg.baseY, 2));
      var mousePull = Math.max(0, 1 - mouseDist / (p.width * 0.5)) * 80;
      
      targetX += Math.cos(mouseAngle) * mousePull;
      targetY += Math.sin(mouseAngle) * mousePull;
      
      sg.x += (targetX - sg.x) * 0.03;
      sg.y += (targetY - sg.y) * 0.03;
    }
  }
  
  function triggerBurst(x, y) {
    var count = 120;
    var particles = [];
    
    for(var i = 0; i < count; i++) {
      var angle = (i / count) * p.TWO_PI + Math.random() * 0.5;
      var speed = 2 + Math.random() * 6;
      particles.push({
        x: x,
        y: y,
        vx: Math.cos(angle) * speed,
        vy: Math.sin(angle) * speed,
        size: 3 + Math.random() * 8
      });
    }
    
    clickBurst.push({particles: particles, life: 1});
  }
  
  p.draw = function() {
    intensity += (targetIntensity - intensity) * 0.02;
    mouseInfluence = Math.max(0, mouseInfluence - 0.02);
    
    updateSingularities();
    
    drawTunnelPixels(tunnelBuffer, time);
    drawBloom(bloomBuffer, tunnelBuffer, time);
    
    compositeBuffer.background(0, 0, 0, 1);
    compositeBuffer.image(bloomBuffer, 0, 0);
    compositeBuffer.blendMode(p.ADD);
    compositeBuffer.image(tunnelBuffer, 0, 0);
    compositeBuffer.blendMode(p.BLEND);
    
    drawProceduralCurves(compositeBuffer, time);
    
    compositeBuffer.push();
    compositeBuffer.translate(compositeBuffer.width / 2, compositeBuffer.height / 2);
    var zoom = 1 + Math.sin(time * 0.1) * 0.05;
    compositeBuffer.scale(zoom);
    compositeBuffer.translate(-compositeBuffer.width / 2, -compositeBuffer.height / 2);
    
    drawVortexRings(compositeBuffer, time);
    drawSacredGeometry(compositeBuffer, time);
    compositeBuffer.pop();
    
    drawClickBurst(compositeBuffer);
    
    finalMaskBuffer.background(0);
    finalMaskBuffer.noStroke();
    
    var vignette = finalMaskBuffer.drawingContext.createRadialGradient(
      p.width / 2, p.height / 2, p.width * 0.1,
      p.width / 2, p.height / 2, p.width * 0.8
    );
    vignette.addColorStop(0, 'rgba(0, 0, 0, 0)');
    vignette.addColorStop(0.5, 'rgba(0, 0, 0, 0.3)');
    vignette.addColorStop(1, 'rgba(0, 0, 0, 0.95)');
    finalMaskBuffer.drawingContext.fillStyle = vignette;
    finalMaskBuffer.drawingContext.fillRect(0, 0, p.width, p.height);
    
    p.background(5, 5, 10);
    p.image(compositeBuffer, 0, 0);
    p.blendMode(p.MULTIPLY);
    p.image(finalMaskBuffer, 0, 0);
    p.blendMode(p.BLEND);
    
    var depthLines = 40;
    p.stroke(280, 60, 15, 0.15 * intensity);
    p.strokeWeight(1);
    for(var d = 0; d < depthLines; d++) {
      var y = (d / depthLines) * p.height;
      var perspective = d / depthLines;
      p.line(0, y, p.width * (0.1 + perspective * 0.8), y);
    }
    
    time += 0.5;
  };
  
  p.mouseMoved = function() {
    mouseInfluence = Math.min(1, mouseInfluence + 0.15);
    
    targetIntensity = 0.6 + Math.sin(p.mouseX * 0.003 + p.mouseY * 0.002) * 0.2;
  };
  
  p.mousePressed = function() {
    triggerBurst(p.mouseX, p.mouseY);
    targetIntensity = 1;
  };
  
  p.keyPressed = function() {
    if(p.key === ' ' || p.key === 'Spacebar') {
      mode = (mode + 1) % 3;
      if(mode === 0) targetIntensity = 0.5;
      else if(mode === 1) targetIntensity = 0.8;
      else targetIntensity = 1;
    }
    else if(p.key === 'r' || p.key === 'R') {
      clickBurst = [];
      for(var i = 0; i < singularities.length; i++) {
        singularities[i].x = singularities[i].baseX;
        singularities[i].y = singularities[i].baseY;
      }
      targetIntensity = 0.5;
    }
    else if(p.key === '1') {
      targetIntensity = 0.3;
    }
    else if(p.key === '2') {
      targetIntensity = 0.7;
    }
    else if(p.key === '3') {
      targetIntensity = 1;
    }
  };
  
  p.windowResized = function() {
    var container = document.getElementById('p5-wrapper');
    p.resizeCanvas(container.offsetWidth, container.offsetHeight);
    
    tunnelBuffer = p.createGraphics(p.width, p.height);
    bloomBuffer = p.createGraphics(p.width, p.height);
    compositeBuffer = p.createGraphics(p.width, p.height);
    finalMaskBuffer = p.createGraphics(p.width, p.height);
    
    for(var i = 0; i < singularities.length; i++) {
      singularities[i].baseX = p.width * (0.35 + 0.3 * i);
      singularities[i].baseY = p.height * (0.4 + 0.2 * Math.sin(i * 0.7));
    }
    
    tendrilPaths = [];
    for(var i = 0; i < 12; i++) {
      tendrilPaths.push(createTendrilPath(i));
    }
  };
}; // p5 init stripped

✨ AI 艺术解读

This artwork simulates the dissolution of ego consciousness as one approaches a cosmic singularity - the tunnel represents the recursive, self-similar patterns that emerge at the boundary between existence and void. The hallucinatory monochrome palette (deep violet-blue) evokes the liminal state between waking and transcendent consciousness, while the domain-warped fractal geometry suggests the breakdown of linear perception. Singularities within the tunnel pull and distort space around them, representing gravitational wells of consciousness collapsing into infinite depth. The sacred geometry overlays imply sacred mathematical structures underlying reality, echoing ancient mystical traditions that encode universal truths in geometric proportion.

📝 补充说明

  • Step size of 3 pixels in tunnel rendering balances visual quality with performance - gaps interpolate naturally due to high fractal coherence
  • Bloom buffer blur radius oscillates with time to create breathing, living light effect
  • Mouse influence decays slowly over time (0.02 per frame) for smooth transitions after interaction ends
  • Key '1', '2', '3' set intensity levels while Spacebar cycles through mode states for variety
  • Singularity positions recalculate base coordinates on resize to maintain proportional layout across screen sizes