Crear arte con JavaScript y P5js

La programación creativa es posible. En este artíclulo veremos cómo crear arte con código JavaScript y la librería P5js.

https://p5js.org/ “p5.js is a JavaScript library for creative coding, with a focus on making coding accessible and inclusive for artists, designers, educators, beginners, and anyone else!”

En éste artículo he preparado un tutorial práctico donde te enseño a implementar la librería. A continuación puedes ver el ejercicio una vez completado. Haz click aquí para abrirlo en una ventana nueva

Haz click sobre la imagen para ver el ejercicio en marcha

¿Quieres aprender a hacer este ejercicio? Saltar directamente al tutorial

Programar es una habilidad enormemente versátil, podríamos decir, simplificando mucho el concepto, que se trata de conocer y combinar correctamente bloques. 

Entendiendo esos bloques como la unidad mínima indivisible, que permite construir proyectos de prácticamente cualquier índole.

Por poner un símil, es como jugar a Lego. No hay límite en lo que se puede llegar a crear con sus piezas, a pesar de que siempre se combinan las mismas.

Nadie dudaría de que se puede ser muy creativo construyendo con Lego. Sin embargo, en ocasiones se comete el error de pensar que la programación es una disciplina donde la creatividad y el arte no tienen cabida.

Nada más lejos de la verdad. Librerías como P5.js existen para acercar el código a artistas, diseñadores o educadores, ofreciéndoles una herramienta más para explotar su creatividad.

El navegador es un lienzo en blanco

P5.js convierte nuestro navegador en un lienzo en blanco, donde a través del lenguaje JavaScript, se pueden crear experiencias artísticas, como por ejemplo, instalaciones interactivas. Dicho de otro modo, crear arte con JavaScript y P5js

El concepto de programación creativa o “creative coding”, existe desde hace años, y en realidad, han sido otros lenguajes como Processing, quienes lo popularizaron.

De hecho, P5.js en sí mismo, es una interpretación de Processing para la web de hoy en día. Tal y como se explica en la web oficial.

Tiene como objetivo ser una librería de referencia, para toda esa comunidad interesada en la creación del arte y el diseño a través de la tecnología.

P5.js es una librería open source y gratuita. Creada por Lauren Lee McCarthy, y activamente mantenida por una comunidad de más de 450 personas, bajo el soporte de Processing Foundation.

Actualmente tiene unas 16.500 estrellas en su repositorio de GitHub, y no ocupa más de 815Kb en su versión comprimida.

La librería puede ser utilizada por todo el mundo, independientemente del nivel de conocimiento que tenga del ecosistema JavaScript. 

Siguiendo su sencillo tutorial, cualquiera puede empezar a crear, sin invertir mucho tiempo en preparar un entorno de desarrollo.

Es importante mencionar que, para aquellos que busquen integrar la librería en su proyecto, la documentación no detalla bien cómo hacerlo. Sin embargo, es posible mediante herramientas como NPM. Se puede ejecutar la instrucción “npm i p5 –save”, que nos va a permitir importar la clase “p5” como un módulo en nuestro script principal. Y posteriormente, instanciarlo para empezar a trabajar.

En el siguiente enlace se detalla cómo:

https://discourse.processing.org/t/how-do-i-import-p5-via-npm/11543

Cada proyecto hecho con P5js es un «sketch»

Nos podemos referir a cada proyecto creado con P5Js como “sketch”. Y cada “sketch” se estructura a partir de dos funciones básicas, “setup()” y “draw()”.

La primera, se ejecutará una vez al inicio. Va a definir propiedades que no se van a alterar a lo largo del tiempo de ejecución. Como el tamaño del canvas, o el “frame rate” de refresco.

La segunda se ejecutará una vez por cada iteración en un “loop” infinito hasta cerrar el programa. Lanzando todas las instrucciones que se hayan descrito en su interior, una a una y por orden.

P5.js viene con una infinidad de funciones. Por nombrar algunas, permite pintar gráficos 2D y 3D, importar y renderizar imágenes y vídeos, o ejecutar audio.

Y por si fuera poco, la comunidad ha creado librerías adicionales que amplían P5.js. Ofreciendo un abanico enorme de recursos para nuestros “sketches”. Algunos como conectividad con dispositivos Arduino, geolocalización, o acceso a algoritmos de “machine learning”.

Ciertos ejercicios hechos con P5.js hasta se podrían considerar videojuegos, a pesar de que la librería tampoco se define como un “game engine”. Si lo que te interesa es una librería específica para crear videojuegos, aquí tienes un enlace al vistazo que hicimos a PhaserJs

Crear videojuegos con JavaScript y PhaserJs

Como se puede apreciar, crear arte es posible con JavaScript y P5js.

Para ver un poco en acción la librería, vamos a crear un pequeño “sketch” artístico.

Vamos a crear un efecto de lluvia invertida

Nuestro ejercicio consistirá en crear un efecto de lluvia, con la peculiaridad que una parte estará invertida.

Como siempre, empezaremos definiendo un sencillo layout HTML. Con un contenedor div al que le asignaremos un id, y con una imágen para darle un toque corporativo.

  <body>
    <img src="imgs/p5js.svg" alt="">
    <div id="p5-container"></div>
  </body>

La parte de estilos CSS tampoco va ser gran cosa. Nos aseguramos que el contenedor ocupa el 100% del visor, y superponemos la imágen en el centro, mediante una posición absoluta.

body {
  margin: 0;
  padding: 0;
  height: 100vh;
  background: #000;
}
#p5-container {
  height: 100%;
  box-sizing: border-box;
  position: relative;
}
img {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 1;
  width: 150px;
  height: auto;
  transform: translate(-50%, -50%);
}

Vamos a describir el comportamiento de nuestro efecto de lluvia en tres clases distintas. Y mediante el uso de la librería P5.js, decidiremos cómo y cuándo usarlas.

RainDrop, es una clase que describe el comportamiento de cada gota de lluvia. Seguidamente iremos método a método explicando su función.

Dentro del método «reset()» definiremos varias propiedades. A través de la función createVector de P5.js, crearemos vectores para controlar su posición, aceleración y velocidad de movimiento.

Adicionalmente, definiremos el color de la partícula, y las áreas donde la gota debe desaparecer.

reset() {
    const s = this.s;
    this.position = s.createVector(s.random(s.width), s.height / 2);
    this.acceleration = s.createVector(0, s.random(-0.5, 0.5));
    this.velocity = s.createVector(0, s.random(-0.3, 0.3));
    this.deadAtBottom = s.random(s.height - s.height / 5, s.height);
    this.deadAtTop = s.random(0, s.height / 5);
    this.color = "#fff";
  }

Mediante el método “isDead()” comprobaremos si la partícula se encuentra dentro de esas áreas.

isDead() {
    return (
      this.position.y > this.deadAtBottom || this.position.y < this.deadAtTop
    );
}

Los métodos “update()” y “display()” se van a encargar de actualizar su posición. Y pedir a la instancia del “sketch” que “pinte” la partícula en forma de elipse.

  update() {
    this.velocity.add(this.acceleration);
    this.position.add(this.velocity);
  }
  display() {
    this.s.fill(this.color);
    this.s.ellipse(
      this.position.x,
      this.position.y,
      1,
      Math.abs(this.velocity.y * 1.5) + 5
    );
  }

El método “run()” se va a encargar de agrupar los otros métodos.

run() {
    this.update();
    this.display();
}

Ripple, es otra clase para describir la onda que genera la gota al impactar en el agua.

La clase es muy parecida a RainDrop. Pero con algunas propiedades editadas, para controlar el radio de crecimiento de la onda, así como la intensidad en su color.

En este caso, el método “update()” se va a encargar de incrementar el radio en “x” y “y”. Además de reducir la esperanza de vida de la partícula.

update() {
    this.radius.x += this.radiusVelocity.x;
    this.radius.y += this.radiusVelocity.y;
    this.radiusVelocity.x = this.radiusVelocity.x - this.radiusVelocity.x * 0.05;
    this.radiusVelocity.y = this.radiusVelocity.y - this.radiusVelocity.y * 0.05;
    this.lifespan -= 4;
 }

Por consiguiente, el método “isDead()” decidirá si la onda debe ser eliminada, comprobando si su esperanza de vida está por debajo de cero.

isDead() {
    return this.lifespan < 0;
}

ParticleSystem, creará y guardará instancias de las clases anteriores en arrays independientes. Y mediante el método “run()” lanzará los métodos de actualización y comprobación de su estado.

class ParticleSystem {
  constructor(s) {
    this.s = s; //instance of p5 sketch
    this.particles = [];
    this.ripples = [];
  }
  addParticle() {
    this.particles.push(new RainDrop(this.s));
  }
  addRipple() {
    this.ripples.push(new Ripple(this.s));
  }
  fillSystem(num) {
    for (let i = 0; i < num; i++) {
      this.addParticle();
      this.addRipple();
    }
  }
  run() {
    for (let i = this.particles.length - 1; i >= 0; i--) {
      this.particles[i].run();
      if (this.particles[i].isDead()) {
        for (let r = this.ripples.length - 1; r >= 0; r--) {
          const ripple = this.ripples[r];
          if (ripple.isDead()) {
            ripple.reset(this.particles[i].position);
            break;
          }
        }
        this.particles[i].reset();
      }
      this.ripples[i].run();
    }
  }
}

En caso de que detecte que una partícula está “muerta”, reseteará sus propiedades para su posterior reutilización.

Finalmente, creamos el “sketch” con sus funciones básicas “setup()” y “draw()”.

En la primera vamos a, definir el tamaño del canvas, el frameRate, instanciar la clase ParticleSystem y rellenarla de partículas.

s.setup = function () {
    s.createCanvas(containerElement.clientWidth, containerElement.clientHeight);
    s.frameRate(30);
    system = new ParticleSystem(s);
    system.fillSystem(150);
  };

En la segunda, sencillamente repintamos el fondo de negro, y actualizamos el sistema en cada iteración.

s.draw = function () {
    s.background(0);
    system.run();
 };

No creo que esta pieza se pueda llegar a considerar arte, pero ejemplifica bien lo que se puede crear con JavaScript y P5js.

A pesar de que en este ejecicio no se ha implementado, te animo a ampliarlo con una interfaz de control. En su momento vimos cómo implementarla rápidamente con Lil-GUI, para controlar los parámetros de la animación.

Por otra parte, también quiero destacar que con P5js puedes controlar una pista de audio, pero si realmente quieres un control total en la generación de música, te recomiendo ToneJS.

Crear música con ToneJs y JavaScript

Crea tu arte programáticamente

Casualmente uno de los primeros lenguajes de programación que aprendí fué precisamente Processing. Y a pesar de que nunca creé proyectos especialmente interesantes, sí consiguió despertar en mí un interés por la programación creativa.

Me resulta lógica la aparición de librerías como esta. Sobretodo teniendo en cuenta la evolución tecnológica que han sufrido los navegadores y el lenguaje JavaScript en los últimos años. Al final se trata de adaptar la misma filosofía a nuevos canales. 

Realmente espero que siga contribuyendo a abrir las puertas de la programación a creativos de todo tipo, durante muchos años más.

Este ha sido un pequeño vistazo a la librería P5js. A continuación os dejo enlaces a más recursos, así como el ejercicio subido al repositorio de Github de “Librerías Js”.

Nos vemos pronto, un abrazo desarrolladores!

Deja un comentario