ITS RAINING TACOS

This sketch creates a festive animation where colorful tacos rain down the screen continuously while a mouse-following circle trails across the canvas with colors that shift based on cursor position. It combines object-oriented programming with p5.js animation to create an interactive, playful visual experience.

🧪 Try This!

Experiment with the code by making these changes:

  1. Make tacos fall much faster
  2. Fill the sky with twice as many tacos — Doubling numTacos creates a denser, more intense rain effect that fills the screen.
  3. Paint the sky bright red
  4. Remove the mouse-following circle — Commenting out the ellipse() line lets you focus entirely on the falling tacos without the interactive element.
  5. Make the circle huge and colorful — A much larger circle with a stronger color response creates a bold interactive element that dominates the canvas.
  6. Grow the mouse circle based on vertical position — Adding a dynamic size calculation makes the circle respond to mouse movement in a new way.
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch animates dozens of tacos falling from the top of the screen while a colorful circle follows your mouse around the canvas. It's a joyful introduction to object-oriented programming in p5.js because every taco is an instance of a Taco class with its own position, speed, size, and rotation. The mouse circle uses map() to translate cursor coordinates into RGB colors, creating an interactive color-changing effect.

The code is organized around three main ideas: a setup() function that initializes the Taco array, a draw() function that updates and displays every taco each frame, and a Taco class that encapsulates all the logic for moving and drawing individual tacos. By reading this sketch, you will learn how classes organize repeated objects, how push() and pop() isolate transformations, and how map() connects interaction to visual properties.

⚙️ How It Works

  1. When the sketch loads, setup() creates a canvas matching the browser window size and initializes an array of 50 Taco objects with random starting positions above the screen, random falling speeds, and random sizes.
  2. Every frame, draw() clears the background with a semi-transparent overlay and maps the mouse's x and y coordinates to RGB color values using map(), then draws a color-changing circle at the cursor.
  3. For each taco in the array, the move() method increments its y position by its speed, making it fall downward; when a taco exits the bottom of the screen, it resets to the top with a new random position, speed, and size.
  4. The display() method draws each taco using push() to save the drawing state, translate() and rotate() to position and spin it, then draws an arc for the shell and simple shapes (rectangles, triangles, circles) for meat, lettuce, and tomato.
  5. If the browser window resizes, windowResized() regenerates the canvas and taco array to fit the new dimensions, ensuring the animation scales responsively.

🎓 Concepts You'll Learn

Object-oriented programming with classesArrays of objectsConstructor and methodsTransform stack: push() and pop()Coordinate transformation: translate() and rotate()Parametric animation with frameCountInteractive color mapping with map()Responsive canvas resizing

📝 Code Breakdown

setup()

setup() runs exactly once when the sketch starts. It's where you initialize your canvas, set drawing modes, and prepare any objects or variables needed for the animation. The taco initialization loop is a classic pattern: loop N times, create an object with random properties, and push it into an array for later use.

function setup() {
  // Create a canvas that fills the entire browser window
  createCanvas(windowWidth, windowHeight);
  // Set the background color once in setup to dark blue
  background(20, 20, 60);
  // Prevent drawing a border around shapes
  noStroke();
  // Set color mode to RGB for consistent color handling (R, G, B, Alpha)
  colorMode(RGB, 255, 255, 255, 255);

  // Initialize the tacos array
  for (let i = 0; i < numTacos; i++) {
    // Start tacos at random positions, some above the screen
    let x = random(width);
    let y = random(-height, 0);
    // Give each taco a random falling speed
    let speed = random(2, 6);
    tacos.push(new Taco(x, y, speed));
  }
}
Line-by-line explanation (9 lines)

🔧 Subcomponents:

initialization Canvas Setup createCanvas(windowWidth, windowHeight);

Creates a canvas that fills the entire browser window, making the animation responsive to different screen sizes

for-loop Taco Array Population for (let i = 0; i < numTacos; i++) { let x = random(width); let y = random(-height, 0); let speed = random(2, 6); tacos.push(new Taco(x, y, speed)); }

Creates 50 Taco objects with randomized starting positions, speeds, and pushes them into the tacos array

createCanvas(windowWidth, windowHeight);
Creates a canvas that matches the full width and height of the browser window, so the animation fills the screen
background(20, 20, 60);
Fills the entire canvas with a dark blue color (red=20, green=20, blue=60) to set the initial background
noStroke();
Removes outlines from all shapes drawn afterward, so tacos and circles appear solid without borders
colorMode(RGB, 255, 255, 255, 255);
Sets color values to range from 0-255 for red, green, blue, and alpha (transparency), the standard RGB color model
for (let i = 0; i < numTacos; i++) {
Loops 50 times (the value of numTacos) to create 50 individual taco objects
let x = random(width);
Assigns each taco a random horizontal position anywhere across the canvas width
let y = random(-height, 0);
Assigns each taco a random vertical position between the top of the screen (0) and above it (negative values), so tacos start above the visible canvas
let speed = random(2, 6);
Gives each taco a random falling speed between 2 and 6 pixels per frame
tacos.push(new Taco(x, y, speed));
Creates a new Taco object with the random x, y, and speed values and adds it to the tacos array

draw()

draw() is the animation heartbeat, running 60 times per second by default. The semi-transparent background creates a motion-trail effect by partially preserving previous frames. The map() function is the key to making the circle respond to the mouse—it takes one range of values and smoothly translates them into another. The taco loop demonstrates the power of object-oriented programming: a single for loop animates dozens of independent objects, each with its own state and behavior.

🔬 This loop calls move() then display() on every taco. What happens if you comment out taco.display() (add // before it) so tacos update their position but never draw?

  for (let taco of tacos) {
    taco.move();
    taco.display();
  }
function draw() {
  // Draw a semi-transparent dark rectangle over the entire canvas
  // This creates a fading trail effect for the mouse-following circle
  background(20, 20, 60, 10); // The '10' is the alpha (transparency) value

  // --- Mouse-following circle logic ---
  // Set the fill color based on the mouse position using RGB mapping
  // Hue changes across the canvas width, saturation changes with height
  let circleHue = map(mouseX, 0, width, 0, 255); // Hue from red (0) to red (255)
  let circleSaturation = map(mouseY, 0, height, 100, 255); // Saturation from 100 to 255
  let circleBrightness = 230; // Keep brightness high
  fill(circleHue, circleSaturation, circleBrightness);

  // Draw an ellipse (circle) at the current mouse position
  // The size of the circle is fixed at 50 pixels
  ellipse(mouseX, mouseY, 50, 50);

  // --- Raining tacos logic ---
  // Update and display each taco
  for (let taco of tacos) {
    taco.move();
    taco.display();
  }
}
Line-by-line explanation (9 lines)

🔧 Subcomponents:

calculation Trail Effect Background background(20, 20, 60, 10);

Draws a nearly transparent overlay every frame, slowly fading previous frames and creating motion trails for the mouse circle

calculation Mouse-to-Color Mapping let circleHue = map(mouseX, 0, width, 0, 255); let circleSaturation = map(mouseY, 0, height, 100, 255);

Converts mouse coordinates into RGB color values so the circle's color changes as you move the cursor

for-loop Taco Update and Display for (let taco of tacos) { taco.move(); taco.display(); }

Iterates through every taco in the array, calling move() to update its position and display() to draw it

background(20, 20, 60, 10);
Draws a nearly transparent dark blue rectangle over the entire canvas; the alpha value of 10 (out of 255) means it barely covers previous frames, creating a fading motion trail effect
let circleHue = map(mouseX, 0, width, 0, 255);
Uses map() to convert the mouse's x position (0 to canvas width) into a red value (0 to 255); moving left/right changes the red channel
let circleSaturation = map(mouseY, 0, height, 100, 255);
Uses map() to convert the mouse's y position (0 to canvas height) into a green value (100 to 255); moving up/down changes the green channel
let circleBrightness = 230;
Sets the blue channel to a fixed high value (230) so the circle is always bright regardless of mouse position
fill(circleHue, circleSaturation, circleBrightness);
Sets the fill color using the three RGB values derived from mouse position, so the circle's color responds to where you move
ellipse(mouseX, mouseY, 50, 50);
Draws a circle with diameter 50 pixels at the current mouse position; the color was just set by fill()
for (let taco of tacos) {
Loops through every Taco object in the tacos array one by one; the variable 'taco' becomes each object in turn
taco.move();
Calls the move() method on the current taco, which updates its y position and checks if it has fallen off the screen
taco.display();
Calls the display() method on the current taco, which draws it at its current position with push()/pop() to handle transformations safely

windowResized()

windowResized() is a special p5.js function that the library calls automatically whenever the browser window changes size. Without it, the canvas would stay the same size and tacos might spawn in invisible areas. Reinitializing the taco array ensures the animation adapts smoothly to any screen size—a key technique for responsive, professional sketches.

function windowResized() {
  // Resize the canvas to fill the new window dimensions
  resizeCanvas(windowWidth, windowHeight);
  // Redraw the background completely after resizing to avoid artifacts
  background(20, 20, 60);

  // Re-initialize tacos to fit the new screen size
  tacos = []; // Clear existing tacos
  for (let i = 0; i < numTacos; i++) {
    let x = random(width);
    let y = random(-height, 0);
    let speed = random(2, 6);
    tacos.push(new Taco(x, y, speed));
  }
}
Line-by-line explanation (7 lines)

🔧 Subcomponents:

calculation Canvas Dimension Update resizeCanvas(windowWidth, windowHeight);

Updates the canvas size to match the new browser window dimensions when the user resizes their window

for-loop Taco Re-initialization for (let i = 0; i < numTacos; i++) { let x = random(width); let y = random(-height, 0); let speed = random(2, 6); tacos.push(new Taco(x, y, speed)); }

Recreates all tacos with positions scaled to the new canvas dimensions so they don't spawn off-screen

resizeCanvas(windowWidth, windowHeight);
Updates the canvas to match the new browser window size, stretching or shrinking it as needed
background(20, 20, 60);
Redraws the background to remove any visual artifacts that might appear during the resize transition
tacos = [];
Clears the tacos array completely, deleting all old taco objects so they don't cause errors or unexpected behavior
for (let i = 0; i < numTacos; i++) {
Loops 50 times to recreate the same number of tacos with the new canvas dimensions in mind
let x = random(width);
Creates a random x position using the new width, ensuring tacos don't spawn off the edge of the resized canvas
let y = random(-height, 0);
Creates a random y position above the screen using the new height, scaling the spawn area to the resized canvas
tacos.push(new Taco(x, y, speed));
Adds the newly created taco to the (now-empty) tacos array

class Taco

The Taco class is the heart of this sketch's object-oriented design. Each taco is an independent object with its own properties (x, y, speed, size, rotation) and methods (move, display). The move() method demonstrates a common pattern: update state each frame, then check if a reset is needed. The display() method shows how push()/pop() isolate transformations—without them, all subsequent drawings would rotate along with the taco. This class pattern scales perfectly: whether you have 50 tacos or 5,000, a single for loop can animate them all.

🔬 The move() method increases y by speed each frame to make the taco fall. What happens if you change += to -= so tacos move upward instead of downward?

  move() {
    this.y += this.speed; // Move downward

    // If taco goes off-screen, reset it to the top
    if (this.y > height + this.size / 2) {
      this.y = -this.size / 2;
      this.x = random(width); // New random horizontal position

🔬 These lines draw the shell and meat. What happens if you swap the RGB numbers in fill() to make the shell brown and meat yellow—does the order matter?

    fill(255, 204, 0); // Yellow/orange for the shell
    arc(0, 0, this.size, this.size * 0.7, PI, TWO_PI, CHORD); // Curved shell

    // --- Draw the taco filling (simple representation) ---
    // Meat
    fill(139, 69, 19); // Brown for meat
    rect(-this.size * 0.2, 0, this.size * 0.4, this.size * 0.15);
class Taco {
  constructor(x, y, speed) {
    this.x = x;
    this.y = y;
    this.speed = speed;
    this.size = random(30, 60); // Random size for variety
    this.rotation = random(-PI / 8, PI / 8); // Slight random rotation
  }

  // Method to update taco's position
  move() {
    this.y += this.speed; // Move downward

    // If taco goes off-screen, reset it to the top
    if (this.y > height + this.size / 2) {
      this.y = -this.size / 2;
      this.x = random(width); // New random horizontal position
      this.speed = random(2, 6); // New random speed
      this.size = random(30, 60); // New random size
      this.rotation = random(-PI / 8, PI / 8); // New random rotation
    }
  }

  // Method to draw the taco
  display() {
    push(); // Save the current drawing state
    translate(this.x, this.y); // Move to the taco's position
    rotate(this.rotation); // Apply rotation

    // --- Draw the taco shell ---
    fill(255, 204, 0); // Yellow/orange for the shell
    arc(0, 0, this.size, this.size * 0.7, PI, TWO_PI, CHORD); // Curved shell

    // --- Draw the taco filling (simple representation) ---
    // Meat
    fill(139, 69, 19); // Brown for meat
    rect(-this.size * 0.2, 0, this.size * 0.4, this.size * 0.15);

    // Lettuce (small green triangles)
    fill(0, 128, 0); // Green for lettuce
    triangle(-this.size * 0.25, -this.size * 0.05, -this.size * 0.1, -this.size * 0.15, -this.size * 0.05, -this.size * 0.05);
    triangle(this.size * 0.25, -this.size * 0.05, this.size * 0.1, -this.size * 0.15, this.size * 0.05, -this.size * 0.05);

    // Tomato (small red circle)
    fill(255, 0, 0); // Red for tomato
    circle(0, -this.size * 0.1, this.size * 0.05);

    pop(); // Restore the previous drawing state
  }
}
Line-by-line explanation (17 lines)

🔧 Subcomponents:

initialization Taco Constructor constructor(x, y, speed) { this.x = x; this.y = y; this.speed = speed; this.size = random(30, 60); this.rotation = random(-PI / 8, PI / 8); }

Initializes a new Taco object with starting position, speed, random size, and random rotation angle

calculation Move Method with Reset Logic this.y += this.speed; if (this.y > height + this.size / 2) { this.y = -this.size / 2; this.x = random(width); this.speed = random(2, 6); this.size = random(30, 60); this.rotation = random(-PI / 8, PI / 8); }

Updates the taco's vertical position each frame and resets it to the top with new random properties when it falls off-screen

drawing Display Method with Transforms push(); translate(this.x, this.y); rotate(this.rotation);

Saves the drawing state, moves the origin to the taco's position, and rotates the coordinate system for drawing rotated taco parts

drawing Taco Component Shapes arc(0, 0, this.size, this.size * 0.7, PI, TWO_PI, CHORD); rect(-this.size * 0.2, 0, this.size * 0.4, this.size * 0.15); triangle(...); circle(0, -this.size * 0.1, this.size * 0.05);

Draws the taco shell as an arc, meat as a rectangle, lettuce as triangles, and tomato as a circle, all scaled proportionally to the taco's size

class Taco {
Declares a new class named Taco that will hold all the data and methods for a single falling taco
constructor(x, y, speed) {
The constructor is a special method that runs when you create a new Taco object—it accepts x, y, and speed as parameters
this.x = x;
Stores the x parameter as a property of this Taco object so it can be used in move() and display() methods later
this.size = random(30, 60);
Assigns each taco a random size between 30 and 60 pixels, creating visual variety even though they're all the same class
this.rotation = random(-PI / 8, PI / 8);
Gives each taco a slight random rotation between -22.5° and +22.5° (PI/8 radians) so they don't all fall straight down
this.y += this.speed;
Increases the taco's y position by its speed value each frame, making it move downward (higher y = lower on screen)
if (this.y > height + this.size / 2) {
Checks if the taco has fallen completely below the bottom of the canvas (accounting for half its size); if true, reset it
this.y = -this.size / 2;
Teleports the taco back above the top of the screen so it starts falling again
this.x = random(width);
Assigns the taco a new random horizontal position when it resets, spreading tacos across the width
push();
Saves the current drawing state (position, rotation, fill color, etc.) so changes inside display() don't affect drawing outside
translate(this.x, this.y);
Moves the origin (0, 0) to the taco's position, so all drawing commands afterward are relative to the taco's center
rotate(this.rotation);
Rotates the coordinate system by the taco's rotation angle, causing all subsequent shapes to be drawn rotated
arc(0, 0, this.size, this.size * 0.7, PI, TWO_PI, CHORD);
Draws an arc centered at (0, 0) with width equal to this.size and height 70% of that—the curved taco shell
rect(-this.size * 0.2, 0, this.size * 0.4, this.size * 0.15);
Draws a brown rectangle centered below the shell to represent the meat filling
triangle(-this.size * 0.25, -this.size * 0.05, -this.size * 0.1, -this.size * 0.15, -this.size * 0.05, -this.size * 0.05);
Draws a green triangle on the left side of the taco to represent lettuce poking out
circle(0, -this.size * 0.1, this.size * 0.05);
Draws a small red circle above the meat to represent a tomato slice
pop();
Restores the drawing state saved by push(), undoing all the transformations so they don't affect anything drawn afterward

📦 Key Variables

tacos array

Stores all Taco objects currently on screen; this array is updated every time a taco resets or the window resizes

let tacos = [];
numTacos number

Controls how many taco objects are created and maintained in the animation; a higher value means more tacos falling at once

let numTacos = 50;

🔧 Potential Improvements (4)

Here are some ways this code could be enhanced:

PERFORMANCE setup() and windowResized() - taco initialization

The taco initialization loop is duplicated in two places (setup and windowResized), violating the DRY principle and making future changes error-prone

💡 Extract the initialization loop into a helper function like initializeTacos() and call it from both places to eliminate duplication and improve maintainability

STYLE Taco class - display method

The taco's visual appearance is hardcoded with magic numbers and color values spread throughout the display() method, making it difficult to adjust the taco design globally

💡 Define color constants at the class level (e.g., shellColor, meatColor, lettuceColor, tomatoColor) and extract magic numbers into descriptive scale factors (e.g., meatHeightScale = 0.15)

BUG Taco class - move method reset condition

The reset condition uses 'height + this.size / 2' which can allow large tacos to be mostly off-screen before resetting, creating a brief gap where they vanish

💡 Change to 'height + this.size' to ensure tacos reset as soon as their bottom edge passes the screen, keeping the rain consistent

FEATURE Taco class

All tacos fall straight down with no horizontal drift, making the animation feel static and predictable

💡 Add a wobble effect by giving each taco a horizontal drift component that changes its x position slightly each frame, creating more organic falling motion

🔄 Code Flow

Code flow showing setup, draw, windowresized, taco

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

graph TD start[Start] --> setup[setup] setup --> canvas-creation[Canvas Setup] canvas-creation --> taco-initialization-loop[Taco Array Population] taco-initialization-loop --> draw[draw loop] draw --> semi-transparent-background[Trail Effect Background] semi-transparent-background --> color-mapping[Mouse-to-Color Mapping] color-mapping --> taco-loop[Taco Update and Display] taco-loop --> taco[loop through tacos] taco --> move-method[Move Method with Reset Logic] move-method --> display-method[Display Method with Transforms] display-method --> taco-parts[Taco Component Shapes] taco-loop --> draw draw --> windowresized[windowResized] windowresized --> canvas-resize[Canvas Dimension Update] canvas-resize --> taco-reinit-loop[Taco Re-initialization] taco-reinit-loop --> taco-initialization-loop click setup href "#fn-setup" click draw href "#fn-draw" click windowresized href "#fn-windowresized" click taco href "#fn-taco" click canvas-creation href "#sub-canvas-creation" click taco-initialization-loop href "#sub-taco-initialization-loop" click semi-transparent-background href "#sub-semi-transparent-background" click color-mapping href "#sub-color-mapping" click taco-loop href "#sub-taco-loop" click canvas-resize href "#sub-canvas-resize" click taco-reinit-loop href "#sub-taco-reinit-loop" click constructor href "#sub-constructor" click move-method href "#sub-move-method" click display-method href "#sub-display-method" click taco-parts href "#sub-taco-parts"

❓ Frequently Asked Questions

What visual experience does the 'ITS RAINING TACOS' sketch create?

The sketch visually simulates tacos falling from the top of the screen against a dark blue background, accompanied by a colorful circle that follows the mouse cursor.

How can users interact with the 'ITS RAINING TACOS' sketch?

Users can interact with the sketch by moving their mouse, which will cause a colorful circle to follow the cursor around the screen.

What creative coding concepts does the 'ITS RAINING TACOS' sketch demonstrate?

This sketch demonstrates the use of arrays to manage multiple objects, random positioning for dynamic visuals, and color mapping based on mouse position.

Preview

ITS RAINING TACOS - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of ITS RAINING TACOS - Code flow showing setup, draw, windowresized, taco
Code Flow Diagram