Day 31 - Art 1

Pixel sorting

Shifting Spectrum

Shifting Spectrum is a generative art piece where vibrant colors blend and shift across the canvas, creating a dynamic gradient animation. The artwork begins with random colors and evolves through phases of smooth transitions, where pixel positions are shuffled and colors are gradually sorted. The result is a fluid, ever-changing spectrum of colors that continuously morphs into new patterns, offering a captivating visual experience that evolves over time.

  // Array to store pixel information (original and shuffled positions)
  let pixelsArray = [];
  // Controls the progress of the animation (0 = start, 1 = end)
  let animationProgress = 0;
  // Speed at which the animation progresses
  let animationSpeed = 0.01;
  // Current phase of the animation (either "generate", "sorting", or "waiting")
  let phase = "generate";
  // Reference to the canvas element
  let canvas;
  
  // Colors for the gradient
  let startColor, endColorX, endColorY;
  
  function setup() {
    // Create a 400x400 canvas and attach it to the parent container
    canvas = createCanvas(400, 400);
    canvas.parent('canvas-container');
    // Set pixel density to 1 for performance
    pixelDensity(1);
    // Start a new animation cycle
    startNewCycle();
  }
  
  function draw() {
    // If we are in the "sorting" phase, gradually transition the pixel positions
    if (phase === "sorting") {
      // Increase the animation progress
      animationProgress += animationSpeed;
      // Clamp the animation progress to a maximum value of 1
      if (animationProgress >= 1) {
        animationProgress = 1;
        // Once the animation is complete, switch to the "waiting" phase
        phase = "waiting";
        // Start a new cycle after a brief delay (2000 ms)
        setTimeout(startNewCycle, 2000);
      }
    }
  
    // Load pixel data into the array
    loadPixels();
    // Loop through all stored pixels and interpolate their positions based on animation progress
    for (let i = 0; i < pixelsArray.length; i++) {
      let p = pixelsArray[i];
      // Interpolate the x and y positions of each pixel
      let x = lerp(p.sx, p.ox, animationProgress);
      let y = lerp(p.sy, p.oy, animationProgress);
  
      // Calculate the pixel index and update the pixel data with the correct RGB values
      let index = (floor(x) + floor(y) * width) * 4;
      pixels[index] = p.r;
      pixels[index + 1] = p.g;
      pixels[index + 2] = p.b;
      pixels[index + 3] = 255;
    }
    // Apply the updated pixel data to the canvas
    updatePixels();
  }
  
  // Function to initiate a new cycle, including generating a new gradient and starting the sorting animation
  function startNewCycle() {
    phase = "generate"; // Set phase to "generate"
    animationProgress = 0; // Reset animation progress
  
    // Randomly generate start and end colors for the gradient
    startColor = color(random(255), random(255), random(255));
    endColorX = color(random(255), random(255), random(255));
    endColorY = color(random(255), random(255), random(255));
  
    // Generate the gradient
    generateGradient();
    // Store the pixel information (positions and colors)
    storePixels();
    // Shuffle the pixel positions to create a dynamic effect
    shufflePixels();
  
    phase = "sorting"; // Set phase to "sorting" to begin the animation
  }
  
  // Function to generate a smooth gradient across the canvas
  function generateGradient() {
    for (let y = 0; y < height; y++) {
      for (let x = 0; x < width; x++) {
        // Interpolate colors based on the x and y coordinates
        let cX = lerpColor(startColor, endColorX, x / width);
        let cY = lerpColor(startColor, endColorY, y / height);
        let finalColor = lerpColor(cX, cY, 0.5);
  
        // Set the color of each pixel in the canvas based on the calculated gradient
        set(x, y, finalColor);
      }
    }
    updatePixels();
  }
  
  // Function to store the original pixel data (position and color)
  function storePixels() {
    pixelsArray = []; // Clear the pixels array
    loadPixels(); // Load current pixel data into the array
    // Loop through every pixel and store its position and color
    for (let y = 0; y < height; y++) {
      for (let x = 0; x < width; x++) {
        let index = (x + y * width) * 4;
        let r = pixels[index];
        let g = pixels[index + 1];
        let b = pixels[index + 2];
  
        // Store the pixel's original position (ox, oy) and the shuffled position (sx, sy)
        pixelsArray.push({
          ox: x,
          oy: y,
          sx: x,
          sy: y,
          r,
          g,
          b,
        });
      }
    }
  }
  
  // Function to shuffle the pixel positions to create a dynamic visual effect
  function shufflePixels() {
    // Shuffle the pixelsArray using Fisher-Yates algorithm
    for (let i = pixelsArray.length - 1; i > 0; i--) {
      let j = floor(random(i + 1));
      // Swap the x and y positions of the pixels
      [pixelsArray[i].sx, pixelsArray[j].sx] = [pixelsArray[j].sx, pixelsArray[i].sx];
      [pixelsArray[i].sy, pixelsArray[j].sy] = [pixelsArray[j].sy, pixelsArray[i].sy];
    }
  }