Este tutorial, de una serie de ellos, pretende servir de apoyo para la convocatoria #tinycode que está activa hasta el 12 de Diciembre de 2020. El mismo día que termina proyectaremos estos pequeños sketches hechos con Processing que caben en un tweet en la fachada digital de Medialab Prado (Madrid). Aquí podrás encontrar más info sobre la convocatoria. Anímate y participa! Happy coding! 😉
Cuando descubrí este tuit me quedé hipnotizado por el movimiento de los tentáculos. El movimiento que consigue @Hau_kun es muy fluido y natural, y lo ha logrado con un código relativamente sencillo.
Vamos a construir este sketch paso a paso, desde cero, para entender cómo funciona e incorporar alguna de sus técnicas y trucos a nuestro repertorio.
Sólo necesitamos unos conocimientos básicos de Processing (variables y bucles) y saber cómo funciona el «Perlin noise«. Hay un video estupendo de The Coding Train donde Daniel Shiffman explica en detalle cómo funciona y cómo usarlo.
Si modificas el sketch original, comparte los resultados con nosotros publicando el código en Twitter, con el hashtag #tinycode y citando a @CCodeMadrid
El proceso
1- El canvas
Creamos un canvas de 720 x 720, y en cada frame, lo primero, borramos su contenido.
void setup() {
size(720, 720);
}
void draw() {
background(0);
}

2- El tentáculo (I)
Empezamos con un tentáculo. Lo pintamos empezando desde el centro del canvas (720/2 = 360) El tentáculo está formado por 50 círculos de radio 5, separados 7 píxeles.
void setup() {
size(720, 720);
}
void draw() {
background(0);
float x = 360;
float y = 360;
for (float d=0; d<50; d++) {
circle(x, y, 5);
x += 7;
}
}

3- El tentáculo (II)
Vamos a variar el diámetro de los círculos, y desplazarlos para que tampoco estén a la misma distancia. Para ello aprovechamos la variable d
.
Para variar el diámetro de momento usamos un map
. Cuando d
pasa de 0 a 50, el diámetro cambiará de 20 a 0. Así pintaremos círculos grandes en el centro y más pequeños en el extremo, imitando la forma de un tentáculo.
Para variar la distancia usamos otro map
, en este caso variando de 5 a 10, para que los círculos de los extremos estén más alejados entre si. Luego nos cargaremos esta parte.
void setup() {
size(720, 720);
}
void draw() {
background(0);
float x = 360;
float y = 360;
for (float d=0; d<50; d++) {
circle(x, y, map(d,0,50,20,0));
x += map(d,0,50,5,10);
}
}

4- Primera reducción y un efecto
Si quitamos espacios y ponemos todo en una línea estamos ya en 153 caracteres, un 54% del espacio total y ni siquiera estamos animando nada. Empezamos a reducir el código, tratando –de momento– de mantener la legibilidad.
Lo primero que podemos hacer es mover todas las declaraciones a una misma línea. Además, como x e y empiezan valiendo lo mismo, podemos compactar la asignación.
En vez de background(0)
, podemos usar clear()
, que es casi lo mismo y nos ahorra algunos caracteres. Y de paso, eliminamos los map
, sustituyéndolos por las correspondientes fórmulas matemáticas. Hemos bajado a 122 caracteres, el 43%, sin perder demasiada legibilidad.
De paso, añadimos blendMode(DIFFERENCE)
para que se invierta el color de la superposición de los círculos (Si pinto sobre negro lo hago en blanco, si pinto sobre blanco, lo hago en negro). Aunque en el código original blendMode
aparece dentro de la función draw()
, por mi paz mental lo he movido a setup()
, ya que realmente no hace falta llamarlo en cada fotograma.
float d, x, y;
void setup() {
size(720, 720);
blendMode(DIFFERENCE);
}
void draw() {
clear();
x = y = 360;
for (d=.5; d>0; d-=.01) {
circle(x, y, d*40);
x += (1-d)*5+4;
}
}

5- Movimiento en un eje
Vamos con el movimiento. Para ello, necesitamos una variable t
que simule el paso del tiempo y aumente un poco en cada iteración. Empezamos extendiendo y contrayendo el tentáculo en el eje x. Para que el movimiento sea fluido, llamamos a la función noise
En este caso, la reescalamos entre -15 y 15 y la usamos para modificar la posición de cada círculo. En cada iteración la llamamos 50 veces, pero sumamos el valor de t
, de tal manera que en la primera vuelta noise
nos devuelve 50 valores entre 0.5 y 0, la segunda entre 0.505 y 0.005, la tercera entre 0.510 y 0.010… Así garantizamos que tanto la posición de los círculos como su animación es fluida y continua.
float t, d, x, y;
void setup() {
size(720, 720);
blendMode(DIFFERENCE);
}
void draw() {
clear();
t+=.005;
x = y = 360;
for (d=.5; d>0; d-=.01) {
circle(x, y, d*40);
x += (noise(d+t)-.5)*30;
}
}

6- Movimiento en dos ejes
Repetimos el paso anterior, pero ahora con el eje y. Para que los valores no sean los mismos que los del eje x, pasamos otras coordenadas a la función noise
. El autor ha usado 9 como punto de partida, pero cualquier otro número valdría.
float t, d, x, y;
void setup() {
size(720, 720);
blendMode(DIFFERENCE);
}
void draw() {
clear();
t+=.005;
x = y = 360;
for (d=.5; d>0; d-=.01) {
circle(x, y, d*40);
x += (noise(d+t)-.5)*30;
y += (noise(d+t,9)-.5)*30;
}
}

7- Y ahora ¡con más tentáculos!
Añadir más tentáculos es sorprendentemente simple: basta con meter el bucle que pinta el tentáculo (el que modifica la variable d
) dentro de otro bucle, que se repetirá tantas veces como tentáculos queramos –9 en este caso, asignado a la variable i
– y añadir i
a la función noise
como una tercera coordenada.
Aunque i
es un entero, lo declaramos como float, para ahorrarnos los caracteres extra que supondría una declaración de tipo int i;
float t, i, d, x, y;
void setup() {
size(720, 720);
blendMode(DIFFERENCE);
}
void draw() {
clear();
t+=.005;
for (i=0; i<9; i++) {
x=y=360;
for (d=.5; d>0; d-=.01) {
circle(x, y, d*40);
x += (noise(i, d+t)-.5)*30;
y += (noise(i, d+t,9)-.5)*30;
}
}
}

8- Segunda reducción
En esta segunda reducción metemos la asignación de x
e y
dentro del circle
, y quitamos todos los espacios y saltos de línea innecesarios (dejo un salto de línea por legibilidad), quedándonos en unos holgados 206 caracteres, así que lo mismo es interesante volver a los espacios y saltos de línea 😅.
float t,i,d,x,y;void setup(){size(720,720);blendMode(DIFFERENCE);}void draw(){clear();t+=.005;
for(i=0;i<9;i++){x=y=360;for(d=.5;d>0;d-=.01){circle(x+=(noise(i,d+t)-.5)*30,y+=(noise(i,d+t,9)-.5)*30,d*40);}}}
Trucos y técnicas empleadas
💡Agrupa todas las declaraciones en una línea, y usa variables del mismo tipo (todas float
o todas int
)
float t, i, d, x, y;
💡Usa los operadores ++
, +=
, … siempre que sea posible.
t+=.005;
...
i++
...
x+=(noise(i,d+t)-.5)*30
💡Para conseguir un movimiento fluido y que tenga continuidad entre fotogramas, usa la función noise.
x+=noise(d+t)*30
💡Usa clear()
en vez de background(0)
clear();
💡Agrupa las asignaciones.
x=y=360;
circle(
x+=(noise(i,d+t)-.5)*30,
y+=(noise(i,d+t,9)-.5)*30,
d*40)
¡Recuerda! Si haces algo raro, bonito, interesante o glitchy, y te cabe en un tuit, compártelo con el hashtag #tinycode y citando a @CCodeMadrid
Juan Alonso (@kokuma) – Noviembre 2020
Muy bueno. Gracias!