# Making of the generative octopuses

By [eziraros](https://paragraph.com/@eziraros) · 2022-03-14

---

![](https://storage.googleapis.com/papyrus_images/9f9e9cd554afd37c19017567757c33b38f7771926fbd66da774df59490f2a630.png)

[中文版](https://mirror.xyz/0xF7E15015D31e1Be374c21E6F1dE91147C8B5db88/VireGXSPQnmBTi9pP9K0Mi45BOHopDJ6SM8sBPDLL3s)　[日本語版](https://note.com/s_r_r_z_/n/n4ff63dd5bfae)

Hi, I’m eziraros. I started making NFT on [fxhash](https://www.fxhash.xyz/) this year.

This article documents part of the development process of my latest drop, _The generative octopuses_. Please check the link before continuing to read.

(This project is done with p5.js)

[https://www.fxhash.xyz/generative/10332](https://www.fxhash.xyz/generative/10332)

Before we start, Why an octopus?
--------------------------------

I watched a [video](https://youtu.be/yyDhnbtmDf8) on Vogue’s Youtube channel that introduced a French actor, Mathilde Warnier, and I found her account on Instagram, came across this [photo](https://www.instagram.com/p/B1WEkVxCtf1). (It’s an octopus. Since I don’t have the copyright of this photo, I didn’t paste it here.)

It strikes me that I should draw an octopus with code.

After googling several images and storing them in the beautiful app **Milanote**, I started to make it.

![Milanote is an excellent tool to collect ideas, organize information](https://storage.googleapis.com/papyrus_images/e62a62cfdde4f766f578e9154dd6d364adee77bbdce5b5eb40498e3f15912fe9.png)

Milanote is an excellent tool to collect ideas, organize information

[https://milanote.com/](https://milanote.com/)

Development process
-------------------

Below are my rough steps

![It was the front face at the beginning.](https://storage.googleapis.com/papyrus_images/5a9ccc72055be994598f515348ac0426f2639ac6c502fec4c605b966ff31ad9d.png)

It was the front face at the beginning.

1.  Start by drawing on paper: I drew a basic octopus to check the structure and found the intersection between body and legs could be the center point.
    
2.  Implementation:
    
    1.  Initialize the main canvas
        
    2.  Implement `Head` & `Leg`
        
    3.  Initialize them as `head` and `legs` (array of instances) in the main canvas
        
    4.  Call the `draw` method of `head` and `legs`. They render on the main canvas separately.
        
    5.  Implement `Frame`
        
    6.  Implement `Bubble`
        
    7.  Make presets
        

**A note for those who are not familiar with this type of generative art**

The piece is drawn frame by frame. In other words, the result is not pre-generated before drawing, but placing the latest result of the real-time calculation on the previous results.

Take the gif below as an example:

![](https://storage.googleapis.com/papyrus_images/72179b5d19850da07bb6d553d45bb0338f47545c83cca7424a9682765a6f8c99.gif)

If I cleared the previous result on each frame, we get this.

![](https://storage.googleapis.com/papyrus_images/ee0449ce61794216488ce2bde520ba92ec7ddc6c0da0ac9a7e0a04b79a3c7e53.gif)

Problems & solutions
--------------------

There are 12 points I noted below.

### 1\. Make legs have “z-index”

I use `createGrphics` to make every leg has its canvas(p5.Graphics) to render. And place it on the main canvas after rendering.

so the `draw` function in the main canvas looks like this:

    for (let i = 0; i < legs.length / 2; i++) {
      legs[i].draw();
      legs[legs.length - 1 - i].draw();
      if (i == 0) {
        head.draw();
      }
    }
    bubbles.do('draw'); // 'do' is my custom function for array
    

![the layers (looks like Damien Hirst’s shark)](https://storage.googleapis.com/papyrus_images/1cc2e7f973524fbe500f17d24a390743034ff516e6f32dae165f0605e38d710d.png)

the layers (looks like Damien Hirst’s shark)

### 2\. Drawing Outlines

I made two classes to draw one leg. `Leg` and `LegNode`. A Leg contains the latest `position`, `direction`, `size` (leg width) and several LegNodes. It generates a LegNode on every N frames, with the `position`, `direction`, and `size` of the frame. Connect the common external tangents of LegNodes to get the outlines.

![the red points are the center of LegNodes](https://storage.googleapis.com/papyrus_images/f80fdd6c12bc7387b49956beb20d9f6eb968cb99a5efdcbcadd1f8b3500633c3.png)

the red points are the center of LegNodes

To make the outlines looks more freehand. Instead of `line`, I drew 20 circles between two endpoints with random size and opacity. I learned this trick from [**Orr Kislev**](https://twitter.com/OrrKislev). (Thanks, Orr Kislev!) He made an excellent animation to explain this.

[https://twitter.com/OrrKislev/status/1483372159640739844](https://twitter.com/OrrKislev/status/1483372159640739844)

### 3\. Suckers position and rotation

The biggest rule is “draw inside”. For example, if `isRightLegs` (`index < 4`) && moving to the right (`direction.x > 0`), I draw them at the bottom. Moving down, draw left. But I don’t draw them while moving up or left.

I set an attribute called `revealRate`. It increases/decreases depending on the frameCount. It changes suckers’ size and position. Sucker’s rotation is following the leg’s direction. I draw ellipses with `scale(1, n)` and `circle`. It’s much easier to control. (compares with `ellipse` function)

### 4\. The pose

The `Leg` has three attributes: `position`(vec2), `direction`(vec2), `rotation`(float). `rotation` changes slightly randomly on every ten frames, and then added to `direction`. Normalized `direction` added to `position`.

    if (frameCount % 10 == 0) {
      this.rotation += fxRandom(-1, 1);
    }
    this.direction.rotate(this.rotation).normalize();
    this.position.add(this.direction);
    

I push rotations into an array. I can check if the leg keeps the same direction while rotating. If so, I can reverse it.

    const n = 50; // the length of cache
    
    this.rotationCache.push(this.rotation);
    
    if (this.rotationCache.length > n) {
      this.rotationCache.shift();
    }
    
    if (this.rotationCache.every(d => d > 0) || this.rotationCache.every(d => d < 0)) {
        this.rotation *= -1;
      }
    }
    

![check the rotations with different array length](https://storage.googleapis.com/papyrus_images/23c816fef4b582a05cb4a76f7c696cab4d83cebc01a4a0846f804ee5aa308f2d.png)

check the rotations with different array length

### 5\. The texture

It’s drawn with countless small rounds. `circle(x, y, size)` is replaced with my custom function `circleNoise(x, y, size)`. It draws dozens of small rounds inside the circle randomly. I can get different results by changing the proportion of small rounds with different brightness.

    for (let i = 0; i < dotLength; i++) {
      radius = fxRandom(0, this.size / 2);
      x = fxRandom(-1, 1) * radius;
      y = Math.sqrt(radius * radius - x * x, 2) * fxRandomSign();
      pg.circle(center.x + x, center.y + y, fxRandom(2, 3));
    }
    

![](https://storage.googleapis.com/papyrus_images/07c74c48daef77f52f59e257d64238999e640633218b3ce32d17c3b9ab5dadc3.png)

![spotted version](https://storage.googleapis.com/papyrus_images/a4c020872ea508b053c9163c82b3ed36ff99e2e4b8e42ce145a20beacb9e8b81.png)

spotted version

### 6\. Changing the face direction

    translate(width/2, height/2);
    scale(-1, 1);
    translate(-width/2, -height/2);
    

### 7\. Head

The hardest part of the entire work. I use the same way as the legs to draw. It’s apparent in a ‘smoke’ version.

![#85 https://www.fxhash.xyz/gentk/505131](https://storage.googleapis.com/papyrus_images/8848bd392d2dfe85f08b0daf6ee28fbb1f2c46e00ebab8610b0d6e4d8e58e2f6.png)

#85 https://www.fxhash.xyz/gentk/505131

### 8\. Frame

![Frame 1](https://storage.googleapis.com/papyrus_images/6bf559f450a92ac4715eeae3bb6d8f58e4928fc4ef1ed2b4c36d408fb99f61de.png)

Frame 1

The `Frame` class has `x`, `y`, `width`, `height`, `frameWeight`. And there is a param called `depth` in the config. (0 = block all, 8 = reveal all). I convert the leg index to leg depth to check if the leg is blocked or not.

In frame 1 mode, stop render if it was blocked && outside the frame.

In frame 2 mode, I erase the pixels with `erase()`.

![Frame 2](https://storage.googleapis.com/papyrus_images/2eafca855a431660b09cc6484d1567cfb94e79bd74c45170090a29491d5ccb84.png)

Frame 2

### 9\. Make leg able to move to the front

I created two canvases on one leg using `createGraphics`. One for the space behind the frame and one for the front.

![look! the middle leg!](https://storage.googleapis.com/papyrus_images/f3cc9fa54f5d62223f7f57dd9c1870f7c391c862641c37b8b8c9bba6682057fc.png)

look! the middle leg!

### 10\. The bubbles

![](https://storage.googleapis.com/papyrus_images/44188ad93f276ebb13f2e9c59bf1da67f9316461be0e13b95af2fbb66a6b3864.png)

The bubbles are not circles or ellipses. Instead, I draw them with random decagons. I think that looks more organic.

    canvas.beginShape();
    
    for (let i = 0; i < 10; i++) {
      radius = this.size * fxRandom(1, 1.4);
      angle = i / 10 * 360;
      x = canvas.sin(angle) * radius;
      y = canvas.cos(angle) * radius * this.compressY;
      canvas.vertex(x, y);
    }
    
    canvas.endShape(canvas.CLOSE);
    

### 11\. Color palettes

I used to pick up colors with a random function. (e.g. `fxRandom(360)` to pick up a hue of the main color, and then plus/minus 120 or 200 to get another hue as secondary.) But this time, I hand-picked the hues.

I created several categories.

    const categorySettings = [
      { name: CATEGORY_BLACK },
      { name: CATEGORY_BLACK2 },
      { name: CATEGORY_WHITE },
      ...
    ];
    

And then pick some hues.

    const hueNameMap = [
      {
        name: COLOR_YELLOW,
        hue: 45,
        briBias: 0,
      },
      {
        name: COLOR_BLUE,
        hue: 210,
        briBias: 0,
      },
      //....
    ];
    

Generate different versions with hue setting

    hueNameMap.forEach(m => {
      let hue = m.hue.length > 1 ? fxRandom(m.hue[0], m.hue[1]) : m.hue;
    
      // pushPreset is a custom function, first param is the name of target category
      pushPreset(CATEGORY_BLACK, {
        name: capitalize(m.name),
        hue,
        briBias: -50,
        noiseBias: -1,
        sat: 0,
        bgColor: [hue, 10, 80],
        bubbleColor: [0, 0, 100, .5],
        frame2Color: ['#fff'],
      });
    });
    
    hueNameMap.forEach(m => {
      pushPreset(CATEGORY_SMOKE, {
        name: capitalize(m.name),
        hue: m.hue.length > 1 ? fxRandom(m.hue[0], m.hue[1]) : m.hue,
        briBias: 0 + m.briBias,
        noiseBias: -2,
        opacity: .06,
        sat: 25,
        bgColor: [0, 0, 20],
        fgColor: [0, 0, 20],
        bubbleColor: ['#fff'],
        frameColor: ['#fff'],
        satFade: -.5 + (m.satFadeBias || 0),
      });
    });
    

Finally, I got 106 presets.

### 12\. Performance tuning

The worst-case was below 10fps. I tweaked the performance by updating only the changed pixels.

Every leg has a `pixelArea`.

    this.pixelArea = {
      x: this.pos.x,
      y: this.pos.y,
      width: 20,
      height: 20,
    };
    

While `Leg` is moving, keep checking if the current point is inside `pixelArea`. If not, update the `pixelArea`. Finally, get the pixels inside `pixelArea` and place them on the main canvas.

**before:**

    masterCanvas.image(this.canvas, 0, 0);
    

**after:**

    this.pixel = this.canvas.get(pa.x, pa.y, pa.width, pa.height);
    masterCanvas.image(this.pixels, pa.x, pa.y);
    

![red is the drawing area for the space behind the frame](https://storage.googleapis.com/papyrus_images/988ba86404710d06bb47467490e4d343a13c5ab0168e4d1afce529dcfb7638dd.gif)

red is the drawing area for the space behind the frame

Apply this to the `Head`, too. It is 30fps now.

Postscript
----------

As a generative art creator, I am not the type that will think clearly before making something, but I prefer to let the generated results lead me. Like a journey. (so the code get messy sometimes.) The above solutions may not be the best solutions to achieve the octopus, but anyway, it’s a way to achieve it. I hope this will be helpful to you.

By the way, the floor price in the secondary market is lower now. It’s time to pick one!

[https://www.fxhash.xyz/marketplace/generative/10332](https://www.fxhash.xyz/marketplace/generative/10332)

---

*Originally published on [eziraros](https://paragraph.com/@eziraros/making-of-the-generative-octopuses)*
