【JavaScript 2022】chroma.js を使った複数色のカラースケールでカラーパレットを生成して p5.js で描画（chroma.bezier() や chroma.scale() の scale.correctLightness() などを利用）

## はじめに
この記事は、[JavaScript Advent Calendar 2022](https://qiita.com/advent-calendar/2022/javascript) の 20日目の記事です。

内容は、過去に以下の記事でも扱った [chroma.js](https://www.vis4.net/chromajs/) の話です。

●chroma.js を使った HSB（HSV） と16進数カラーコードの変換やその他の処理を p5.js Web Editor上で試す - Qiita
　https://qiita.com/youtoy/items/513009ebfdd04872e059

もう少し補足すると、以下の記事に出てくる chroma.js での複数の色を使ったカラースケールの話と、p5.js でカラーパレットを使う話とを組み合わせた内容になります。

●Mastering Multi-hued Color Scales with Chroma.js
　https://www.vis4.net/blog/2013/09/mastering-multi-hued-color-scales/
![Combining Bezier interpolation and lightness correction](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/68794178-6960-628e-c78e-74fac8bd56a6.png)

## 複数の色を使ったカラースケール
上で掲載していた「[Mastering Multi-hued Color Scales with Chroma.js](https://www.vis4.net/blog/2013/09/mastering-multi-hued-color-scales/)」という記事で、複数の色を使ったカラースケールの話について書かれています。

細かな話は省略しますが、その話の中で、複数の色を使ったカラースケールを改善するための方法について説明されています。
そして、chroma.js の「chroma.bezier()」や「chroma.scale() ＋ scale.correctLightness()」を使った話が出てきていました。これらを使うと、複数の色を使ったスケールの結果を、良い感じにできるようです。
※ 自分が色の理論にそこまで詳しくないので、ざっくりな内容しか把握できてないのですが...

その話が気になったので、具体的にどんな結果が得られるかを、普段よく使っている p5.js を使って確かめてみる、というのが、これ以降の内容です。
（そして、良い感じのカラーパレットが生成できたら、今後、活用していけそうかなという思いもあり...）

## chroma.js関連の話
以下で、今回利用する chroma.js の機能の、公式情報を見ていきます。

### chroma.scale()
指定した 2色以上の色で、カラースケールを作成できます。色指定をしない場合、デフォルトは白黒の 2色になるようです。
https://gka.github.io/chroma.js/#chroma-scale
![chroma.scale()](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/0a58df91-bc34-65e6-faab-8cd9a29d4925.png)

### scale.mode()
カラースケールのモードを切り替えるもので、デフォルトは RGB ですが、HSL など他のものに変更することができます。
https://gka.github.io/chroma.js/#scale-mode
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/ebdd19b9-3b26-3b23-7538-4db06e721d1c.png)

### scale.colors()
カラースケールから、数字指定で色を取り出すためのものです。
https://gka.github.io/chroma.js/#scale-colors
![scale.colors()](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/15a4d0ea-dce9-5231-ddf5-7d41bb4ce1d0.png)

### scale.correctLightness() と chroma.bezier()
上に掲載した記事で出てきた、複数の色のカラースケールを作成する際に、良い感じの出力を得るために用いられていたものです。

https://gka.github.io/chroma.js/#scale-correctlightness
![scale.correctLightness()](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/6a32dabc-8cd2-7492-6336-f0e921b81613.png)

https://gka.github.io/chroma.js/#chroma-bezier
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/7955b30b-b4db-27f6-9614-8784ad3d53b5.png)

## 生成した色を p5.js で描画してみる
ここから、chroma.js でカラースケールを生成し、そこから色を取り出してカラーパレットを作るというのをやってみます。さらに、そのカラーパレットを使った描画を、p5.js を用いて行います。

### p5.js で得た出力
まずは、p5.js で得た出力を掲載してみます。
![生成した色を p5.js で描画](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/6fa5f96c-5ac4-39c1-a1e6-be174d147ff9.png)
6パターンのカラーパレットがありますが、補足などを以下で書いていきます。

### 出力の全てに共通の内容： 元にした色と出力した色の数
カラースケールを作成する際、元になる色として指定した内容を、ソースコードベースで示します。以下のように、「白・黄色・青・黒」の 4色を、元になる色として指定しました。

`const colorList = ["white", "yellow", "blue", "black"];`

また、出力する色の数は 10色としました。

`const outputColorNum = 10;`

### 出力の上 2つ
最初に、以下の　2つについて補足します。
![出力の上 2つ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/2a3f9718-e207-9208-aa70-9b79e15fadd4.png)

chroma.scale().mode("rgb") で得た出力と、scale.correctLightness() を加えたものの 2つです。scale.correctLightness() を加えたことで、左半分の色の変化の段階が、はっきりした感じになりました。

```javascript
  chroma.scale(colorList).mode("rgb").colors(outputColorNum), 
  chroma.scale(colorList).mode("rgb").correctLightness().colors(outputColorNum),
```

### 出力の真ん中の 2つ
次に、以下の　2つについて補足します。
![出力の真ん中の 2つ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/0b10577f-5f84-0dd7-bb22-c14c73a437fc.png)

chroma.scale().mode("hsl") で得た出力と、scale.correctLightness() を加えたものの 2つです。上の事例とは異なり、scale.correctLightness() を加えたことで、左半分の出力がずいぶん異なるものになりました。

```javascript
  chroma.scale(colorList).mode("hsl").colors(outputColorNum),
  chroma.scale(colorList).mode("hsl").correctLightness().colors(outputColorNum),
```


### 出力の下 2つ
最後に、以下の　2つについて補足します。
![出力の下 2つ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/ea810e72-ad37-ba13-6f92-45abcda57503.png)

chroma.bezier().scale() で生成したカラースケールと、それに scale.correctLightness() を加えたものとなっています。左端から真ん中あたりの色が少し補正されているのが分かります。

```javascript
   chroma.bezier(colorList).scale().colors(outputColorNum),
  chroma.bezier(colorList).scale().correctLightness().colors(outputColorNum),
```

ただ、他の事例と比べると、補正の影響が大きくはないようです。
このあたりは、元にする色の組み合わせなどで変わるのか、chroma.bezier().scale() を使うとこういった感じになるのか、そのあたりを判断するにはもう少しお試しをする必用がありそう...

### p5.js Web Editor上での実装
最後に、上で掲載した出力を得るために、p5.js Web Editor上で作った内容を掲載します。CSS　は特に変更してないため、HTML と JavaScript のファイルの内容のみ掲載します。

``` html
<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/chroma-js/2.4.2/chroma.min.js"></script>
    <link rel="stylesheet" type="text/css" href="style.css" />
    <meta charset="utf-8" />
  </head>
  <body>
    <main></main>
    <script src="sketch.js"></script>
  </body>
</html>
```

```javascript
const outputColorNum = 10;

const colorList = ["white", "yellow", "blue", "black"];

const outputColorList = [
  chroma.scale(colorList).mode("rgb").colors(outputColorNum), 
  chroma.scale(colorList).mode("rgb").correctLightness().colors(outputColorNum),
  chroma.scale(colorList).mode("hsl").colors(outputColorNum),
  chroma.scale(colorList).mode("hsl").correctLightness().colors(outputColorNum),
   chroma.bezier(colorList).scale().colors(outputColorNum),
  chroma.bezier(colorList).scale().correctLightness().colors(outputColorNum),
];

function setup() {
  createCanvas(400, 350);
  noStroke();
}

function draw() {
  background(220);

  const size = width / outputColorNum;

  for (i = 0; i < outputColorList.length; i++) {
    for (j = 0; j < outputColorNum; j++) {
      fill(outputColorList[i][j]);
      rect(
        (j * width) / outputColorNum,
        height * (0.05 + i * 0.16),
        size,
        size
      );
    }
  }
}
```

あと上記を試した後に、「塗りつぶしの部分で透明度を設定する」というのもやってみました。

```javascript
fill(outputColorList[i][j]);`
         ↓
`fill(`${outputColorList[i][j]}aa`);`
```