AI Aurora Borealis Generator - Dancing Northern Lights Simulation - xelsed.ai

This sketch simulates the northern lights: a gradient night sky filled with twinkling stars, flowing ribbons of green, teal, and purple aurora light rendered with additive blending and Perlin noise, occasional bright flares that shoot upward, and a silhouette of pine trees along the horizon. Moving the mouse nudges the direction the aurora curtains drift.

🧪 Try This!

Experiment with the code by making these changes:

  1. Fill the sky with more flares — Raising flareRate makes bright sparks shoot up much more often, turning a calm aurora into a busier fireworks-like display.
  2. Double the star field — Increasing numStars scatters far more twinkling points across the upper sky for a denser starfield.
  3. Thicken the aurora streaks — Widening each vertical rectangle from 2px to something bigger makes the aurora look chunkier and more solid, and reveals the underlying rectangle structure.
  4. Exaggerate mouse wind influence — Widening the map() range for windInfluence makes moving the mouse push the aurora sideways much more dramatically.
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch paints a living night sky: a smooth navy-to-black gradient, twinkling stars, and shimmering curtains of aurora light that ripple and glow above a row of pine tree silhouettes. The glowing, layered look comes from combining Perlin noise (via p5's noise() function) with HSB color mode and blendMode(ADD), which makes overlapping light bands brighten each other exactly like real auroral glow. Occasional bright 'flares' spawn and drift upward, fading out over time, and moving the mouse left or right subtly steers the direction the aurora drifts.

The code is organized into three functions: setup() builds a reusable gradient background image and initializes arrays of stars and aurora 'bands', draw() runs 60 times a second to animate stars, aurora streaks, flares, and the tree silhouette, and windowResized() rebuilds the background whenever the browser window changes size. Studying this sketch teaches you how to fake volumetric light with simple 2-pixel-wide rectangles, how layered noise offsets create organic, non-repeating motion, and why pre-rendering static elements into an offscreen p5.Graphics buffer keeps animation smooth.

⚙️ How It Works

  1. When the sketch loads, setup() creates the canvas, switches to HSB color mode, and pre-renders a navy-to-black vertical gradient into an offscreen graphics buffer called backgroundGraphics so it never has to be recalculated during animation.
  2. setup() also fills the stars array with 200 random star positions and twinkle speeds, defines a palette of eight aurora colors, and creates 6 aurora 'bands', each with its own random noise offsets, vertical amplitude, and opacity.
  3. Every frame, draw() first stamps the pre-rendered gradient onto the canvas, then draws each star with a brightness that pulses using sin(frameCount * twinkleSpeed) to create a twinkling effect.
  4. draw() switches to blendMode(ADD) and, for each aurora band, marches across the canvas in 2-pixel steps, sampling 3D Perlin noise to decide each streak's height, brightness, and opacity - this is what makes the aurora ripple and drift rather than looking like a static gradient.
  5. A small random chance each frame spawns a glowing 'flare' particle near one of the aurora bands; every existing flare drifts upward and fades out until its opacity hits zero, at which point it's removed from the flares array.
  6. Finally draw() resets blendMode(BLEND) and draws an opaque ground rectangle plus a row of randomly-sized pine tree silhouettes (trunk plus two stacked triangles) so the aurora appears to hang above a horizon; windowResized() keeps the gradient background matching the canvas whenever the browser window is resized.

🎓 Concepts You'll Learn

HSB color modePerlin noise for organic motionAdditive blend mode for glowing lightOffscreen graphics buffer (createGraphics)Particle systems (flares array)Mouse interaction (mouseX)Responsive canvas with windowResized

📝 Code Breakdown

setup()

setup() runs once and is the right place to do expensive one-time work - like pre-rendering a gradient into a graphics buffer and filling arrays with randomized objects - so the draw() loop can stay fast and focused purely on animation.

🔬 Each band gets random noise offsets so they never move in sync. What happens visually if you set xoff, yoff, and zoff all to 0 instead of random(1000) for every band - do the bands start looking identical?

  for (let i = 0; i < numAuroraBands; i++) {
    auroraBands.push({
      xoff: random(1000), // Noise offset for horizontal movement
      yoff: random(1000), // Noise offset for vertical movement
      zoff: random(1000), // Noise offset for shimmering depth
      colorIndex: i % auroraColors.length, // Cycle through defined colors
      amplitude: random(0.5, 1.5), // How much the band stretches vertically
      opacityScale: random(0.2, 0.8), // Base opacity for the band
      hueOffset: random(-20, 20) // Slight hue variation for each band
    });
  }
function setup() {
  createCanvas(windowWidth, windowHeight);
  colorMode(HSB, 360, 100, 100, 100); // Hue, Saturation, Brightness, Alpha
  noStroke(); // No outlines for shapes

  // --- Create Background Gradient (deep navy to black) ---
  // This is rendered once into a p5.Graphics object to save performance.
  backgroundGraphics = createGraphics(width, height);
  backgroundGraphics.colorMode(HSB, 360, 100, 100, 100);
  backgroundGraphics.noStroke();
  for (let y = 0; y < height; y++) {
    let inter = map(y, 0, height, 0, 1);
    // Deep navy at the top, almost black at the bottom
    let c1 = color(220, 80, 20); // Deep navy (hue, saturation, brightness)
    let c2 = color(220, 80, 0);  // Almost black
    let c = lerpColor(c1, c2, inter);
    backgroundGraphics.fill(c);
    backgroundGraphics.rect(0, y, width, 1); // Draw a 1-pixel high rectangle
  }

  // --- Initialize Stars ---
  for (let i = 0; i < numStars; i++) {
    stars.push({
      x: random(width),
      y: random(height * 0.7), // Stars mostly in the upper 70% of the canvas
      size: random(1, 3), // Random size for stars
      brightness: random(50, 100), // Base brightness
      twinkleSpeed: random(0.01, 0.05) // Speed at which the star twinkles
    });
  }

  // --- Define Aurora Color Palette (HSB values) ---
  // These colors will be used for the different aurora bands.
  auroraColors = [
    color(120, 100, 80), // Vibrant Green
    color(150, 80, 100), // Lighter, more saturated Green
    color(180, 100, 80), // Teal
    color(210, 80, 100), // Lighter, more saturated Teal
    color(270, 100, 80), // Purple
    color(300, 80, 100), // Lighter, more saturated Purple
    color(330, 100, 80), // Pink
    color(360, 80, 100)  // Lighter, more saturated Pink (wraps around to red)
  ];

  // --- Initialize Aurora Bands ---
  // Each band has unique noise offsets and properties to create variation.
  for (let i = 0; i < numAuroraBands; i++) {
    auroraBands.push({
      xoff: random(1000), // Noise offset for horizontal movement
      yoff: random(1000), // Noise offset for vertical movement
      zoff: random(1000), // Noise offset for shimmering depth
      colorIndex: i % auroraColors.length, // Cycle through defined colors
      amplitude: random(0.5, 1.5), // How much the band stretches vertically
      opacityScale: random(0.2, 0.8), // Base opacity for the band
      hueOffset: random(-20, 20) // Slight hue variation for each band
    });
  }
}
Line-by-line explanation (10 lines)

🔧 Subcomponents:

for-loop Background Gradient Loop for (let y = 0; y < height; y++) {

Draws one thin horizontal rectangle per row, blending from deep navy at the top to near-black at the bottom, baked into an offscreen buffer once for performance.

for-loop Star Initialization Loop for (let i = 0; i < numStars; i++) {

Creates numStars star objects with random position, size, brightness, and twinkle speed.

for-loop Aurora Band Initialization Loop for (let i = 0; i < numAuroraBands; i++) {

Creates each aurora band's unique noise offsets and visual properties so every band moves and looks slightly different.

createCanvas(windowWidth, windowHeight);
Makes the canvas fill the entire browser window.
colorMode(HSB, 360, 100, 100, 100);
Switches from the default RGB color model to Hue-Saturation-Brightness, which makes it much easier to shift colors (like aurora hues) smoothly by just changing one number.
backgroundGraphics = createGraphics(width, height);
Creates a separate offscreen canvas (a p5.Graphics object) so the gradient can be drawn once here instead of every frame in draw(), which would be wasteful.
let c1 = color(220, 80, 20); // Deep navy (hue, saturation, brightness)
Defines the sky color at the top of the gradient using HSB values (hue 220 is blue, low brightness keeps it dark).
let c = lerpColor(c1, c2, inter);
Blends smoothly between the top color and bottom color based on how far down the canvas this row is (inter goes from 0 to 1).
backgroundGraphics.rect(0, y, width, 1); // Draw a 1-pixel high rectangle
Draws one row of the gradient at a time onto the offscreen buffer, building the full gradient line by line.
y: random(height * 0.7), // Stars mostly in the upper 70% of the canvas
Keeps stars out of the bottom 30% of the screen so they don't appear inside the tree silhouette area.
auroraColors = [
Defines the fixed palette of colors (green, teal, purple, pink) that aurora bands will cycle through.
colorIndex: i % auroraColors.length, // Cycle through defined colors
Uses the modulo operator so if there are more bands than colors, the palette simply repeats.
amplitude: random(0.5, 1.5), // How much the band stretches vertically
Gives each band a random vertical stretch factor so some ribbons of light are taller and more dramatic than others.

draw()

draw() is the animation heartbeat of any p5.js sketch, running roughly 60 times per second. This draw() shows a common pattern: draw static/cheap elements first (background, stars), switch blend modes for a glow effect on the expensive animated element (aurora), manage a growing-and-shrinking particle array (flares) safely by looping backwards, then reset state before drawing solid foreground elements (trees).

🔬 This block decides where a new flare appears. What happens if you change the y calculation to always spawn flares near the bottom of the screen (like height * 0.9) instead of near the aurora?

  if (random(1) < flareRate) {
    let band = random(auroraBands); // Pick a random aurora band to base the flare on
    // Determine flare's y position based on the selected band's noise
    let noiseVal = noise(band.xoff + width * 0.005, band.yoff, band.zoff);
    let noiseHeight = map(noiseVal, 0, 1, -height * band.amplitude / 2, height * band.amplitude / 2);
    let y = height * 0.6 + noiseHeight;

🔬 yoff and zoff advance automatically every frame, while xoff only changes based on mouse position. What happens if you also add a small constant like 0.002 to band.xoff so the aurora drifts sideways even without moving the mouse?

    let windInfluence = map(mouseX, 0, width, -0.01, 0.01);
    band.xoff += windInfluence; // Mouse X affects horizontal noise offset
    band.yoff += 0.005;        // Continuous vertical noise progression
    band.zoff += 0.0001;       // Very slow depth noise progression for shimmering
function draw() {
  // --- Draw Background Gradient ---
  // Display the pre-rendered gradient background.
  image(backgroundGraphics, 0, 0);

  // --- Draw Twinkling Stars ---
  for (let star of stars) {
    // Make stars twinkle by pulsing their brightness using a sine wave.
    let b = map(sin(frameCount * star.twinkleSpeed), -1, 1, star.brightness * 0.5, star.brightness);
    fill(0, 0, b, 100); // White color, varying brightness
    circle(star.x, star.y, star.size);
  }

  // --- Draw Aurora Bands ---
  blendMode(ADD); // This is crucial for the glowing, accumulating light effect!

  for (let band of auroraBands) {
    // Influence horizontal movement (wind direction) with mouseX
    let windInfluence = map(mouseX, 0, width, -0.01, 0.01);
    band.xoff += windInfluence; // Mouse X affects horizontal noise offset
    band.yoff += 0.005;        // Continuous vertical noise progression
    band.zoff += 0.0001;       // Very slow depth noise progression for shimmering

    let baseColor = auroraColors[band.colorIndex];
    // Calculate hue with offset and slight noise variation
    let h = (hue(baseColor) + band.hueOffset + map(noise(band.zoff), 0, 1, -10, 10)) % 360;
    if (h < 0) h += 360; // Ensure hue stays within 0-360
    let s = saturation(baseColor);
    let b = brightness(baseColor);

    // Draw vertical streaks for the aurora band
    let lineHeight = height * band.amplitude; // Total vertical stretch of the band
    for (let x = 0; x < width; x += 2) { // Draw every 2 pixels for performance
      // Get a Perlin noise value for this x position, creating waves
      let noiseVal = noise(band.xoff + x * 0.005, band.yoff, band.zoff);
      let noiseHeight = map(noiseVal, 0, 1, -lineHeight / 2, lineHeight / 2);
      let noiseOpacity = map(noiseVal, 0, 1, 0, 100); // Noise influences opacity
      let noiseBrightness = map(noiseVal, 0, 1, 50, 100); // Noise influences brightness

      let y = height * 0.6 + noiseHeight; // Base height of aurora + noise displacement
      // Pulse opacity and brightness using sine waves for organic variation
      let opacity = noiseOpacity * band.opacityScale * map(sin(frameCount * 0.01 + band.xoff), -1, 1, 0.5, 1.5);
      let brightness = noiseBrightness * map(sin(frameCount * 0.02 + band.yoff), -1, 1, 0.8, 1.2);

      fill(h, s, brightness, opacity);
      rect(x, y - lineHeight / 2, 2, lineHeight); // Draw a thin vertical rectangle
    }
  }

  // --- Generate Flares ---
  // Occasionally spawn bright flares that travel up the aurora.
  if (random(1) < flareRate) {
    let band = random(auroraBands); // Pick a random aurora band to base the flare on
    // Determine flare's y position based on the selected band's noise
    let noiseVal = noise(band.xoff + width * 0.005, band.yoff, band.zoff);
    let noiseHeight = map(noiseVal, 0, 1, -height * band.amplitude / 2, height * band.amplitude / 2);
    let y = height * 0.6 + noiseHeight;
    flares.push({
      x: random(width), // Random x position
      y: y,
      size: random(10, 30), // Random size
      speed: random(1, 3), // Speed at which it travels upwards
      hue: (hue(auroraColors[band.colorIndex]) + random(-30, 30)) % 360, // Color based on band, with variation
      saturation: 100, // Fully saturated
      brightness: 100, // Fully bright
      alpha: 100,      // Starts fully opaque
      decayRate: random(1, 3) // How fast the flare fades out
    });
  }

  // --- Draw and Update Flares ---
  // Iterate backwards to safely remove flares that have faded.
  for (let i = flares.length - 1; i >= 0; i--) {
    let flare = flares[i];
    fill(flare.hue, flare.saturation, flare.brightness, flare.alpha);
    circle(flare.x, flare.y, flare.size);

    flare.y -= flare.speed; // Move flare upwards
    flare.alpha -= flare.decayRate; // Fade flare out

    if (flare.alpha <= 0 || flare.y < 0) {
      flares.splice(i, 1); // Remove flare if completely faded or off-screen
    }
  }

  blendMode(BLEND); // Reset blend mode for silhouette and other non-glowing elements

  // --- Draw Silhouette of Pine Trees/Mountains ---
  fill(220, 80, 0); // Black color for the silhouette

  // Base ground rectangle
  rect(0, height * 0.8, width, height * 0.2);

  // Simple stylized pine trees
  for (let x = 50; x < width; x += random(50, 150)) {
    let treeHeight = random(height * 0.1, height * 0.2);
    let treeWidth = treeHeight * 0.6;
    let y = height * 0.8; // Base of the tree

    // Trunk
    rect(x - treeWidth * 0.1, y, treeWidth * 0.2, treeHeight * 0.3);

    // Leaves (layered triangles)
    triangle(x, y - treeHeight, x - treeWidth / 2, y, x + treeWidth / 2, y);
    // Slightly smaller triangle on top for multi-layered look
    triangle(x, y - treeHeight * 0.7, x - treeWidth / 2 * 0.8, y * 1.05, x + treeWidth / 2 * 0.8, y * 1.05);
  }
}
Line-by-line explanation (13 lines)

🔧 Subcomponents:

for-loop Star Twinkle Loop for (let star of stars) {

Draws every star with a brightness that oscillates over time using sin(), creating a twinkling effect.

for-loop Aurora Band Loop for (let band of auroraBands) {

Updates each band's noise offsets (advancing the animation and applying mouse-driven wind) and computes its current hue before drawing its streaks.

for-loop Aurora Streak Drawing Loop for (let x = 0; x < width; x += 2) { // Draw every 2 pixels for performance

Samples 3D Perlin noise at each x position to decide the height, opacity, and brightness of a thin vertical light streak, building up the wavy aurora shape.

conditional Flare Spawn Check if (random(1) < flareRate) {

Randomly decides, with probability flareRate, whether to create a new bright flare particle this frame.

for-loop Flare Update & Cleanup Loop for (let i = flares.length - 1; i >= 0; i--) {

Draws each flare, moves it upward, fades its alpha, and removes it from the array once it's invisible or off-screen - looping backwards so removal doesn't skip elements.

for-loop Pine Tree Silhouette Loop for (let x = 50; x < width; x += random(50, 150)) {

Places randomly-sized pine tree silhouettes at randomly-spaced intervals across the horizon.

image(backgroundGraphics, 0, 0);
Stamps the pre-rendered gradient buffer onto the visible canvas, clearing the previous frame.
let b = map(sin(frameCount * star.twinkleSpeed), -1, 1, star.brightness * 0.5, star.brightness);
Uses a sine wave driven by frameCount to smoothly oscillate each star's brightness between half its base value and its full value, creating a twinkle.
blendMode(ADD); // This is crucial for the glowing, accumulating light effect!
Switches to additive blending so overlapping colors add their brightness together instead of covering each other, producing a glowing look where aurora streaks overlap.
let windInfluence = map(mouseX, 0, width, -0.01, 0.01);
Converts the mouse's horizontal position into a small positive or negative value used to nudge the noise offset, letting the mouse steer the aurora's apparent wind direction.
let h = (hue(baseColor) + band.hueOffset + map(noise(band.zoff), 0, 1, -10, 10)) % 360;
Computes this band's current hue by combining its base palette color, a fixed per-band offset, and a slowly shifting noise value, then wraps it with modulo so it stays within the valid 0-360 hue range.
let noiseVal = noise(band.xoff + x * 0.005, band.yoff, band.zoff);
Samples 3D Perlin noise using the x position plus the band's evolving offsets - this is the core trick that makes the aurora look like smooth, organic waves instead of random jitter.
let y = height * 0.6 + noiseHeight; // Base height of aurora + noise displacement
Places the aurora's vertical center around 60% down the screen, then displaces it up or down based on the noise value.
rect(x, y - lineHeight / 2, 2, lineHeight); // Draw a thin vertical rectangle
Draws one 2-pixel-wide vertical bar of light; thousands of these bars side by side, each with slightly different height/opacity, together form the flowing aurora curtain.
if (random(1) < flareRate) {
Rolls a random number between 0 and 1 each frame; if it's less than flareRate, a new flare particle is spawned this frame.
for (let i = flares.length - 1; i >= 0; i--) {
Loops through the flares array backwards so that removing an item with splice() doesn't cause the loop to skip the next element.
if (flare.alpha <= 0 || flare.y < 0) {
Checks whether a flare has fully faded out or drifted above the top of the screen, and if so removes it from the array to free memory.
blendMode(BLEND); // Reset blend mode for silhouette and other non-glowing elements
Switches back to normal blending so the solid black tree silhouette isn't affected by the additive glow used for the aurora.
for (let x = 50; x < width; x += random(50, 150)) {
Walks across the canvas placing a tree roughly every 50-150 pixels, with the random gap making the forest look natural rather than a perfect grid.

windowResized()

windowResized() is a special p5.js function that fires automatically on browser resize. Because the aurora's background was pre-rendered into a graphics buffer for performance, that buffer has to be manually rebuilt here whenever the canvas size changes - a good example of the tradeoff between caching for speed and needing to invalidate that cache when dimensions change.

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  // Re-create background gradient with new canvas dimensions
  backgroundGraphics = createGraphics(width, height);
  backgroundGraphics.colorMode(HSB, 360, 100, 100, 100);
  backgroundGraphics.noStroke();
  for (let y = 0; y < height; y++) {
    let inter = map(y, 0, height, 0, 1);
    let c1 = color(220, 80, 20); // Deep navy
    let c2 = color(220, 80, 0);  // Almost black
    let c = lerpColor(c1, c2, inter);
    backgroundGraphics.fill(c);
    backgroundGraphics.rect(0, y, width, 1);
  }
}
Line-by-line explanation (3 lines)

🔧 Subcomponents:

for-loop Gradient Rebuild Loop for (let y = 0; y < height; y++) {

Regenerates the background gradient row by row to match the new canvas dimensions after a browser resize.

resizeCanvas(windowWidth, windowHeight);
This built-in p5.js function is automatically called whenever the browser window changes size; here it resizes the main canvas to match the new window dimensions.
backgroundGraphics = createGraphics(width, height);
Throws away the old offscreen gradient buffer and creates a brand new one sized to match the resized canvas - otherwise the gradient would look stretched or too small.
let c = lerpColor(c1, c2, inter);
Recomputes the same navy-to-black blend as in setup(), row by row, for the new canvas height.

📦 Key Variables

backgroundGraphics object

Holds a pre-rendered p5.Graphics image of the navy-to-black sky gradient, drawn once (and on resize) instead of every frame, to save performance.

let backgroundGraphics;
stars array

Stores an object for each star with its position, size, base brightness, and twinkle speed.

let stars = [];
numStars number

How many stars to generate and draw in the sky.

let numStars = 200;
auroraBands array

Stores an object for each aurora 'ribbon' with its own noise offsets, color index, amplitude, opacity, and hue offset, so every band animates differently.

let auroraBands = [];
numAuroraBands number

How many overlapping aurora ribbons are layered on top of each other.

let numAuroraBands = 6;
auroraColors array

The fixed palette of HSB colors (greens, teals, purples, pinks) that aurora bands cycle through.

let auroraColors = [];
flares array

Stores active flare particle objects (position, size, speed, color, fading alpha) that are updated and removed each frame.

let flares = [];
flareRate number

The probability (0 to 1) that a new flare spawns on any given frame.

let flareRate = 0.005;

🔧 Potential Improvements (3)

Here are some ways this code could be enhanced:

BUG windowResized()

When the window is resized, the background gradient and canvas are updated, but the stars array is never regenerated - stars keep their original x/y positions from the old canvas size, which can leave large empty patches of sky or clip stars off-screen if the window grows or shrinks significantly.

💡 Re-generate the stars array (or rescale existing star positions proportionally to the new width/height) inside windowResized(), similar to how the background gradient is rebuilt.

PERFORMANCE draw() aurora streak loop

For every band, the code calls noise() once per 2 pixels across the entire canvas width every single frame - on a wide 4K window with several bands this can add up to tens of thousands of noise samples per frame, which may strain slower devices.

💡 Consider increasing the x-step for wider canvases, caching noise values for a coarser grid and interpolating between them, or reducing numAuroraBands on smaller/mobile screens using windowWidth as a hint.

STYLE draw()

Layout constants like height * 0.6 (aurora center), height * 0.8 (ground start), and height * 0.7 (star zone) are repeated as magic numbers scattered through setup() and draw(), making it harder to tweak the overall composition consistently.

💡 Extract these into named constants at the top of the sketch, e.g. const auroraCenterY = 0.6, groundY = 0.8, so changing the horizon line only requires editing one value.

🔄 Code Flow

Code flow showing setup, draw, windowresized

💡 Click on function names in the diagram to jump to their code

graph TD start[Start] --> setup[setup] setup --> draw[draw loop] draw --> gradientloop[gradient-loop] draw --> starinitloop[star-init-loop] draw --> bandinitloop[band-init-loop] draw --> stardrawloop[star-draw-loop] draw --> aurorabandloop[aurora-band-loop] draw --> flarespawnconditional[flare-spawn-conditional] draw --> flareupdateloop[flare-update-loop] draw --> treeloop[tree-loop] click setup href "#fn-setup" click draw href "#fn-draw" click gradientloop href "#sub-gradient-loop" click starinitloop href "#sub-star-init-loop" click bandinitloop href "#sub-band-init-loop" click stardrawloop href "#sub-star-draw-loop" click aurorabandloop href "#sub-aurora-band-loop" click flarespawnconditional href "#sub-flare-spawn-conditional" click flareupdateloop href "#sub-flare-update-loop" click treeloop href "#sub-tree-loop" gradientloop --> rowloop[Row Loop] rowloop --> drawrect[Draw Rectangle] starinitloop --> starloop[Star Initialization Loop] starloop --> createStar[Create Star Object] bandinitloop --> bandloop[Aurora Band Initialization Loop] bandloop --> createBand[Create Aurora Band Properties] stardrawloop --> twinkleloop[Star Twinkle Loop] twinkleloop --> drawStar[Draw Star with Twinkle] aurorabandloop --> auroraloop[Aurora Band Loop] auroraloop --> updateNoise[Update Noise Offsets] auroraloop --> computeHue[Compute Current Hue] aurorabandloop --> aurorastreakloop[Aurora Streak Drawing Loop] aurorastreakloop --> sampleNoise[Sample 3D Perlin Noise] aurorastreakloop --> drawStreak[Draw Aurora Streak] flarespawnconditional --> checkFlare[Flare Spawn Check] checkFlare -->|Yes| createFlare[Create New Flare] flareupdateloop --> flareloop[Flare Update & Cleanup Loop] flareloop --> drawFlare[Draw Flare] flareloop --> moveFlare[Move Flare Upward] flareloop --> fadeFlare[Fade Alpha] flareloop --> removeFlare[Remove Invisible Flare] treeloop --> treeinitloop[Pine Tree Silhouette Loop] treeinitloop --> placeTree[Place Randomly-Sized Trees] setup --> windowresized[windowResized] windowresized --> resizegradientloop[resize-gradient-loop] resizegradientloop --> resizeRowLoop[Gradient Rebuild Loop] resizeRowLoop --> resizeDrawRect[Regenerate Background Gradient]

❓ Frequently Asked Questions

What visual experience does the AI Aurora Borealis Generator create?

This sketch simulates the northern lights with flowing curtains of green and teal light dancing across a starlit sky, accompanied by silhouettes of pine trees.

How can users interact with the Dancing Northern Lights simulation?

Users can influence the flow of the aurora by moving their mouse, which adds an interactive element to the visual display.

What creative coding concepts does this p5.js sketch demonstrate?

The sketch showcases techniques such as layering graphics, creating gradients, and using color modes to simulate natural phenomena like the aurora borealis.

Preview

AI Aurora Borealis Generator - Dancing Northern Lights Simulation - xelsed.ai - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of AI Aurora Borealis Generator - Dancing Northern Lights Simulation - xelsed.ai - Code flow showing setup, draw, windowresized
Code Flow Diagram