Abyssal Luminous Grove

📅 April 16, 2026 🏷️ art
bioluminescent deep sea generative art organic animation particles forest abstract
Generated by GridFlow AI | Tags: bioluminescent, deep sea, generative art, organic, animation, particles, forest, abstract

📊 视觉感受量化评分

0
0
0
0

💡 AI 提示词

Bioluminescent Deep Sea Forest

🧠 核心算法要点

  • A dark, semi-transparent background provides a deep-sea atmosphere and creates subtle, ghosting trails for movement.
  • Multiple 'Tendril' objects simulate swaying kelp or plant-like structures, anchored at the bottom of the canvas.
  • Each tendril consists of numerous `p.curveVertex` segments, dynamically updated with sinusoidal sway and Perlin noise for organic, fluid motion.
  • Tendrils glow via `p.drawingContext.shadowBlur`, emitting light in various cool bioluminescent hues (cyan, blue-green).
  • A particle system of 'Particle' objects represents glowing plankton or deep-sea organisms, drifting upwards.
  • Particles have varied lifespans, sizes, and colors (pinks, yellows, purples), and their movement is influenced by random vectors and Perlin noise for organic flow.
  • Particle glow is enhanced using `p.blendMode(p.SCREEN)`, allowing overlapping particles to appear brighter and creating a luminous haze.

💻 原始 p5.js 代码

var sketch = function(p) {
    let tendrils = [];
    let particles = [];
    const NUM_TENDKILS = 12; // Number of kelp-like tendrils
    const NUM_PARTICLES = 150; // Number of glowing plankton particles

    p.setup = function() {
        let container = document.getElementById('p5-wrapper'); p.createCanvas(container.offsetWidth, container.offsetHeight).parent('p5-wrapper');
        p.pixelDensity(1); // Ensures consistent rendering across screens

        // Initialize tendrils
        for (let i = 0; i < NUM_TENDKILS; i++) {
            tendrils.push(new Tendril(p, i));
        }

        // Initialize particles
        for (let i = 0; i < NUM_PARTICLES; i++) {
            particles.push(new Particle(p));
        }
    };

    p.draw = function() {
        // Dark background with slight transparency for subtle trails, mimicking water depth
        p.background(5, 10, 30, 180); 

        // Update and display tendrils (the 'forest' elements)
        for (let tendril of tendrils) {
            tendril.update();
            tendril.display();
        }

        // Update and display particles (the 'bioluminescent' elements)
        // Use blend mode for glow effect, making colors brighter when overlapped
        p.blendMode(p.SCREEN); 
        for (let particle of particles) {
            particle.update();
            particle.display();
        }
        p.blendMode(p.BLEND); // Reset blend mode for subsequent drawings or UI if any
    };

    p.windowResized = function() {
        let container = document.getElementById('p5-wrapper'); p.resizeCanvas(container.offsetWidth, container.offsetHeight);
        // On resize, tendrils will auto-adjust as their base positions and amplitudes are relative.
        // Particles will reset themselves when off-screen, naturally adapting.
    };

    // --- Classes for Tendrils and Particles ---

    // Represents a single swaying kelp-like bioluminescent plant
    class Tendril {
        constructor(p, id) {
            this.p = p;
            this.id = id;
            // Base position at the bottom of the canvas, spread horizontally
            this.baseX = p.width * (0.05 + id * 0.9 / (NUM_TENDKILS - 1 || 1));
            this.baseY = p.height + p.random(0, p.height * 0.1); // Slightly off-screen at bottom
            this.segmentCount = p.floor(p.map(id, 0, NUM_TENDKILS - 1, 15, 30)); // Vary segment count
            this.segments = [];
            this.amplitude = p.random(p.width * 0.02, p.width * 0.06); // Max sway amplitude
            this.swaySpeed = p.random(0.005, 0.015); // Speed of the swaying motion
            this.swayOffset = p.random(p.TWO_PI); // Phase offset for individual sway
            this.noiseOffset = p.random(1000); // Perlin noise offset for organic shape
            this.initSegments();
            // Bioluminescent colors for tendrils (cyan, green-blue)
            this.color = p.color(p.random(80, 150), p.random(180, 255), p.random(200, 255), 200);
            this.lengthFactor = p.random(0.7, 1.2); // Variation in tendril height
        }

        initSegments() {
            for (let i = 0; i < this.segmentCount; i++) {
                // Initialize segments vertically above the base
                this.segments.push(this.p.createVector(this.baseX, this.baseY - (this.p.height * 0.75 * this.lengthFactor / this.segmentCount) * i));
            }
        }

        update() {
            let time = this.p.frameCount * this.swaySpeed + this.swayOffset;
            for (let i = 0; i < this.segmentCount; i++) {
                let segmentRatio = i / this.segmentCount; // Ratio along the tendril's length
                
                // Sway motion: stronger at the top, fading towards the base
                let swayAmount = this.p.sin(time + segmentRatio * this.p.PI) * this.amplitude * (this.p.pow(segmentRatio, 2));
                
                // Perlin noise for subtle, organic irregularities in shape
                let noiseX = this.p.map(this.p.noise(this.noiseOffset + i * 0.1, this.p.frameCount * 0.005), 0, 1, -1, 1);
                
                let newX = this.baseX + swayAmount + noiseX * (this.amplitude * 0.1);
                let newY = this.baseY - (this.p.height * 0.75 * this.lengthFactor / this.segmentCount) * i; 
                
                // Update segment position
                this.segments[i].set(newX, newY);
            }
        }

        display() {
            this.p.noFill();
            this.p.stroke(this.color);
            this.p.strokeWeight(this.p.width * 0.001); // Relative stroke weight

            // Apply shadow for a soft glow effect, mimicking bioluminescence
            this.p.drawingContext.shadowBlur = this.p.width * 0.008;
            this.p.drawingContext.shadowColor = this.color.toString(); 

            this.p.beginShape();
            // Use curveVertex for smooth, organic lines
            this.p.curveVertex(this.segments[0].x, this.segments[0].y); // Duplicate first point for smooth start
            for (let s of this.segments) {
                this.p.curveVertex(s.x, s.y);
            }
            this.p.curveVertex(this.segments[this.segments.length - 1].x, this.segments[this.segments.length - 1].y); // Duplicate last point for smooth end
            this.p.endShape();

            this.p.drawingContext.shadowBlur = 0; // Reset shadow blur to avoid affecting other elements
        }
    }

    // Represents a single glowing particle (plankton/spark)
    class Particle {
        constructor(p) {
            this.p = p;
            this.pos = p.createVector(p.random(p.width), p.random(p.height * 0.8, p.height * 1.1)); // Start mostly at bottom or off-screen
            this.vel = p.createVector(p.random(-0.5, 0.5), p.random(-1.5, -0.5)); // Drift upwards with slight horizontal sway
            this.acc = p.createVector(0, 0);
            this.size = p.random(p.width * 0.001, p.width * 0.004); // Relative size
            this.life = p.random(150, 400); // Lifespan of the particle
            this.maxLife = this.life;
            // Bioluminescent colors for particles (pink, yellow-green, orange)
            this.color = p.color(p.random(180, 255), p.random(100, 255), p.random(100, 255), 200); 
            this.noiseScale = p.random(0.005, 0.015); // Scale for Perlin noise movement
            this.timeOffset = p.random(1000); // Perlin noise time offset
        }

        applyForce(force) {
            this.acc.add(force);
        }

        update() {
            // Apply subtle noise-based movement for organic drift
            let noiseX = this.p.map(this.p.noise(this.pos.x * this.noiseScale, this.timeOffset, this.p.frameCount * 0.001), 0, 1, -0.1, 0.1);
            let noiseY = this.p.map(this.p.noise(this.pos.y * this.noiseScale, this.timeOffset + 100, this.p.frameCount * 0.001), 0, 1, -0.1, 0.1);
            this.applyForce(this.p.createVector(noiseX, noiseY));

            this.vel.add(this.acc);
            this.pos.add(this.vel);
            this.acc.mult(0); 
            this.life--;

            // Reset particle if it goes off-screen upwards or its life ends
            if (this.life <= 0 || this.pos.y < -this.size) {
                this.reset();
            }
        }

        display() {
            this.p.noStroke();
            // Fade out particle as its life diminishes
            let alpha = this.p.map(this.life, 0, this.maxLife, 0, 255);
            alpha = this.p.constrain(alpha, 0, 255);
            
            let displayColor = this.color;
            displayColor.setAlpha(alpha);
            this.p.fill(displayColor); // Fill with bioluminescent color
            
            this.p.ellipse(this.pos.x, this.pos.y, this.size, this.size);
        }

        reset() {
            this.pos = this.p.createVector(this.p.random(this.p.width), this.p.random(this.p.height * 0.9, this.p.height * 1.1)); // Reset near bottom
            this.vel = this.p.createVector(this.p.random(-0.5, 0.5), this.p.random(-1.5, -0.5));
            this.life = this.p.random(150, 400);
            this.maxLife = this.life;
            this.size = this.p.random(this.p.width * 0.001, this.p.width * 0.004);
            this.color = this.p.color(this.p.random(180, 255), this.p.random(100, 255), p.random(100, 255), 200); // Randomize color on reset
            this.noiseScale = this.p.random(0.005, 0.015);
            this.timeOffset = this.p.random(1000);
        }
    }
};
new p5(sketch);

🎨 AI 艺术解读

This artwork plunges the viewer into the mysterious depths of an alien ocean, where light is born from life itself. Swaying bioluminescent tendrils form an ethereal forest, their soft glow illuminating the perpetual twilight of the abyss. Countless motes of glowing plankton drift and pulse, embodying the delicate yet vibrant ecosystem thriving far beyond the reach of the sun. It's a serene yet wonder-filled exploration of nature's hidden beauty and the intricate dance of light in the dark.

📝 补充说明

  • Using `p.background(..., alpha)` creates a subtle fading trail effect, essential for the 'deep sea' movement and glow, while avoiding full screen clears.
  • `p.drawingContext.shadowBlur` and `p.drawingContext.shadowColor` are powerful for creating glow effects on specific shapes, but remember to reset `shadowBlur` to 0 afterwards to prevent it from affecting subsequent drawings.
  • `p.blendMode(p.SCREEN)` (or `p.ADD`) is crucial for additive light effects, where overlapping colors combine to become brighter, effectively simulating true light sources or luminous phenomena.
  • All dimensions, speeds, and forces are calculated relatively (e.g., `p.width * 0.001`), ensuring the artwork scales gracefully and maintains its aesthetic across different screen sizes.
  • Perlin noise (`p.noise()`) is used extensively to introduce organic, non-repeating variations in motion and form, preventing a 'canned' or repetitive look and enhancing the natural feel.