1. ¿Qué es p5.js?

p5.js es una librería de JavaScript para la programación creativa. Nació como una reinterpretación web del lenguaje Processing, con el objetivo de hacer accesible el código a artistas, diseñadores y cualquier persona que quiera aprender.

Funciona dibujando píxel a píxel en un elemento <canvas> de HTML5. Todo ocurre en el navegador, sin necesidad de instalaciones.

Filosofía de p5.js: el código no es solo una herramienta para resolver problemas técnicos — es también un medio de expresión artística y creativa. p5.js fue diseñada por y para personas que quieren explorar ese espacio.

¿Cómo se incluye en un proyecto?

<!-- Opción 1: CDN (más fácil para empezar) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.3/p5.min.js"></script>

<!-- Opción 2: npm (proyectos con bundler) -->
// npm install p5
import p5 from 'p5';

La estructura mínima de un sketch

// Un sketch p5.js siempre tiene dos funciones principales:

function setup() {
  // Se ejecuta UNA sola vez al inicio
  createCanvas(400, 400);  // ancho, alto en píxeles
}

function draw() {
  // Se ejecuta en BUCLE (60 fps por defecto)
  background(220);           // limpiar el canvas cada frame
  ellipse(200, 200, 80, 80); // x, y, ancho, alto
}

Coordenadas en p5.js: el punto (0, 0) está en la esquina superior izquierda. El eje X crece hacia la derecha y el eje Y crece hacia abajo. Esto es diferente al sistema de coordenadas matemático, donde Y crece hacia arriba.

2. setup() y draw() — El ciclo de vida

Estos dos son los pilares de cualquier sketch. Entender cuándo se ejecuta cada uno es fundamental.

FunciónCuándo se ejecutaPara qué se usa
setup() Una sola vez, al cargar el sketch Crear el canvas, definir tamaño, modo de color, cargar recursos
draw() En bucle, ~60 veces por segundo Dibujar formas, actualizar posiciones, leer mouse/teclado
preload() Antes de setup() Cargar imágenes, fuentes y archivos de datos
mousePressed() Al hacer clic Reaccionar al clic del ratón
keyPressed() Al presionar una tecla Reaccionar al teclado
windowResized() Al cambiar el tamaño de la ventana Ajustar el canvas al nuevo tamaño
function setup() {
  createCanvas(400, 300);
  frameRate(30);        // limitar a 30 fps
  colorMode(HSB);       // modo de color: Hue, Saturation, Brightness
  noStroke();           // quitar contornos globalmente
}

function draw() {
  background(0, 0, 15);  // fondo oscuro en modo HSB

  // frameCount: número de frames transcurridos desde el inicio
  let x = (frameCount * 2) % width;
  fill(frameCount % 360, 80, 90);
  ellipse(x, height / 2, 40, 40);
}

function mousePressed() {
  // Se llama automáticamente cada vez que se hace clic
  console.log(`Clic en: ${mouseX}, ${mouseY}`);
}
▶ Demo — setup() y draw() en acción LIVE
frameCount avanza cada frame · mouseX / mouseY siguen al cursor

3. Formas básicas

p5.js tiene funciones para dibujar las formas geométricas más comunes. Todos los parámetros se miden en píxeles.

// PUNTO
point(x, y);

// LÍNEA
line(x1, y1, x2, y2);

// RECTÁNGULO — por defecto desde la esquina superior izquierda
rect(x, y, ancho, alto);
rect(x, y, ancho, alto, radio);         // con bordes redondeados
rectMode(CENTER);                       // x,y pasan a ser el centro

// ELIPSE / CÍRCULO — el origen es siempre el centro
ellipse(x, y, ancho, alto);
circle(x, y, diametro);                 // shorthand

// TRIÁNGULO
triangle(x1, y1, x2, y2, x3, y3);

// CUADRILÁTERO (4 puntos)
quad(x1,y1, x2,y2, x3,y3, x4,y4);

// ARCO — startAngle y endAngle en radianes
arc(x, y, ancho, alto, 0, PI);         // semicírculo inferior
arc(x, y, ancho, alto, 0, TWO_PI, PIE); // modo pastel

// FORMA PERSONALIZADA (vértices libres)
beginShape();
  vertex(50, 20);
  vertex(150, 20);
  vertex(100, 120);
endShape(CLOSE);
▶ Demo — Formas básicas LIVE
Las formas se superponen según el orden en que se dibujan

Modos de origen (rectMode, ellipseMode)

// rectMode afecta cómo se interpreta (x, y)
rectMode(CORNER);   // defecto: (x,y) = esquina superior izquierda
rectMode(CENTER);   // (x,y) = centro del rectángulo
rectMode(CORNERS);  // (x1,y1) y (x2,y2) = esquinas opuestas

// ellipseMode — igual que rectMode pero para elipses
ellipseMode(CENTER);  // defecto: (x,y) = centro
ellipseMode(CORNER);  // (x,y) = esquina superior izquierda

4. Color y transparencia

p5.js acepta colores en múltiples formatos: escala de grises, RGB, RGBA, hexadecimal, y en modo HSB/HSL.

// Escala de grises (0–255)
fill(128);              // gris medio
fill(255);              // blanco
fill(0);                // negro

// RGB (0–255 por canal)
fill(255, 100, 50);      // naranja

// RGBA — cuarto parámetro = alpha (0 transparente, 255 opaco)
fill(255, 100, 50, 150); // naranja semitransparente

// Hexadecimal
fill('#e8a020');

// colorMode(HSB) — Hue(0-360), Saturation(0-100), Brightness(0-100)
colorMode(HSB);
fill(200, 80, 90);       // azul brillante

// stroke → color del contorno
stroke(255, 0, 0);        // contorno rojo
strokeWeight(3);          // grosor del contorno en píxeles
noStroke();               // quitar el contorno
noFill();                 // quitar el relleno (solo contorno)
▶ Demo — Paleta de colores con fill y stroke LIVE
Modo HSB: ciclo de matiz con frameCount · transparencias superpuestas

La función color() y lerpColor()

// color() crea un objeto de color que se puede guardar
let rojo  = color(255, 50, 50);
let azul  = color(50, 100, 255);

// lerpColor → interpola entre dos colores
// 0.0 = primer color, 1.0 = segundo color
let mezcla = lerpColor(rojo, azul, 0.5); // → morado

// map() → convierte un valor de un rango a otro
// muy útil para mapear posición → color
let matiz = map(mouseX, 0, width, 0, 360);

5. Animación

La animación en p5.js surge del bucle draw(). Cada vez que se ejecuta, podemos actualizar variables de estado y redibujar el canvas con nuevas posiciones o colores.

Variables de estado y movimiento

// Las variables FUERA de draw() persisten entre frames
let x = 0;
let velocidad = 2;

function draw() {
  background(20);           // limpiar cada frame

  fill(255);
  circle(x, height / 2, 40);

  x += velocidad;           // mover a la derecha

  // Rebotar en los bordes
  if (x > width || x < 0) {
    velocidad *= -1;        // invertir dirección
  }
}

Funciones matemáticas útiles

// sin() y cos() → movimiento ondulatorio / circular
let angulo = 0;

function draw() {
  background(20);

  // Movimiento senoidal → sube y baja suavemente
  let y = height / 2 + sin(angulo) * 80;
  circle(width / 2, y, 40);

  angulo += 0.05;          // velocidad de la onda
}

// Otras funciones útiles
random(0, 100);           // número aleatorio entre 0 y 100
noise(x);                 // Perlin noise — más suave que random()
map(valor, min1, max1, min2, max2); // reasignar rangos
lerp(a, b, t);            // interpolación lineal (t entre 0 y 1)
dist(x1, y1, x2, y2);    // distancia entre dos puntos
constrain(val, min, max); // limitar un valor a un rango
▶ Rebote con velocidad LIVE
Variable x + velocidad · inversión en bordes
▶ Movimiento senoidal LIVE
sin() + frameCount · onda suave

background() con transparencia — efecto de estela

function draw() {
  // En vez de limpiar el fondo completamente,
  // dibujamos un rectángulo semitransparente encima.
  // Los frames anteriores se desvanecen gradualmente.
  fill(20, 20, 20, 25);   // RGBA: último número = alpha
  noStroke();
  rect(0, 0, width, height);

  // Ahora dibujas tu forma — deja una estela
  fill(255, 100, 100);
  circle(mouseX, mouseY, 20);
}

6. Interactividad

p5.js expone variables globales y funciones callback que se actualizan automáticamente con la entrada del usuario.

Variables del ratón y el teclado

Variable / FunciónDescripción
mouseX / mouseYPosición actual del cursor en el canvas
pmouseX / pmouseYPosición del cursor en el frame anterior
mouseIsPressedtrue mientras se mantiene el botón presionado
mouseButtonLEFT, RIGHT o CENTER
mousePressed()Callback: se llama al hacer clic
mouseReleased()Callback: al soltar el botón
mouseMoved()Callback: al mover el cursor sin clic
mouseDragged()Callback: al mover con botón presionado
keyIsPressedtrue mientras se mantiene una tecla
keyLa tecla presionada (ej: 'a', 'Enter')
keyCodeCódigo numérico (ENTER, BACKSPACE, UP_ARROW…)
keyPressed()Callback: al presionar una tecla
let historial = [];  // guardar posiciones anteriores

function draw() {
  background(20, 20, 20, 30);  // estela suave

  // mouseIsPressed → dibuja solo mientras se hace clic
  if (mouseIsPressed) {
    historial.push({ x: mouseX, y: mouseY });
    if (historial.length > 60) historial.shift();
  }

  // Dibujar el historial con tamaño decreciente
  historial.forEach((p, i) => {
    let t = i / historial.length;
    fill(lerpColor(color(237, 34, 93), color(255, 200, 50), t));
    noStroke();
    circle(p.x, p.y, t * 20);
  });
}

function keyPressed() {
  if (key === 'c') historial = [];  // limpiar con la tecla C
}
▶ Demo — Pincel interactivo con ratón LIVE
Mueve el cursor para pintar · Clic para dejar trazos · Tecla C para limpiar

Transformaciones — translate, rotate, scale

function draw() {
  background(20);
  translate(width / 2, height / 2);  // mover el origen al centro

  for (let i = 0; i < 8; i++) {
    rotate(TWO_PI / 8);         // rotar 45° en cada iteración
    fill(i * 40, 180, 200);
    rect(40, 0, 60, 20);
  }
}

// push() y pop() guardan y restauran el estado de transformación
function draw() {
  push();                    // guardar estado
    translate(100, 100);
    rotate(frameCount * 0.02);
    rect(-25, -25, 50, 50);
  pop();                     // restaurar estado
}
▶ Demo — translate + rotate + push/pop LIVE
Sistema solar: rotate() con velocidades distintas por nivel

7. Proyecto aplicado — Visualizador generativo

Este proyecto combina todos los conceptos de la semana: coordenadas, formas, color HSB, animación con sin() y noise(), interactividad con el ratón y transformaciones.

Conceptos aplicados: colorMode(HSB) · noise() para movimiento orgánico · map() para convertir rangos · mouseX/mouseY · push()/pop() para transformaciones independientes · efecto de estela con fill() semitransparente · frameCount como motor de tiempo

p5

Visualizador de Partículas

Generativo interactivo — mueve el cursor para influir en el campo de fuerza

Velocidad: 4
Partículas: 80

Código del proyecto (simplificado)

let particulas = [];
let velocidad = 4;
let pausa = false;

function setup() {
  createCanvas(600, 360);
  colorMode(HSB, 360, 100, 100, 100);
  for (let i = 0; i < 80; i++) {
    particulas.push(new Particula());
  }
}

function draw() {
  if (pausa) return;

  // Estela: fondo semitransparente
  fill(240, 20, 12, 18);
  noStroke();
  rect(0, 0, width, height);

  particulas.forEach(p => {
    p.actualizar();
    p.dibujar();
  });
}

class Particula {
  constructor() { this.reset(); }

  reset() {
    this.x      = random(width);
    this.y      = random(height);
    this.matiz  = random(360);
    this.offset = random(1000); // semilla de noise única
    this.vida   = random(100, 255);
  }

  actualizar() {
    // Perlin noise para dirección orgánica
    let angulo = noise(
      this.x * 0.003,
      this.y * 0.003,
      frameCount * 0.005
    ) * TWO_PI * 3;

    // Influencia del ratón
    let d = dist(this.x, this.y, mouseX, mouseY);
    if (d < 80) angulo += (PI / d) * 10;

    this.x += cos(angulo) * velocidad;
    this.y += sin(angulo) * velocidad;
    this.vida -= 0.5;

    if (this.vida <= 0 || this.x < 0 || this.x > width
        || this.y < 0 || this.y > height) {
      this.reset();
    }
  }

  dibujar() {
    fill(this.matiz, 80, 95, this.vida / 2);
    noStroke();
    circle(this.x, this.y, 4);
  }
}