p5.js 2.0についての記事（2回目）

# はじめに
　あのあといろいろ追加されたので追記という形で記事にします。p5.js/2.0.1のレファレンス：

https://beta.p5js.org/reference/

そうです。あのあと2.0.1になりました。最新版：

https://github.com/processing/p5.js/releases/tag/v2.0.1

とはいえリンク先をたどっても、デモが付いていなかったりして何が変更されたのかいまいち分かりませんでした。まあそのうちわかることもあるかと思います。

　現在もデフォルトは1.11.5です。しかし予告されているように来年の夏ごろには2.0以降がデフォルトになります。1.11.5以前のバージョンでしかコードを書きたくない場合、更新後はいちいち過去バージョンに戻さなければならなくなります。面倒なので今のうちに慣れておいた方がいい場合もあるかもしれないです。とはいえ作品によっては過去バージョンがいい場合もあるでしょう。どのみち、作品制作時に勝手にバージョンが変更されるというわけでは無いので、毎回の制作に集中する上では全く問題にならないとも言えます。

## いろいろ

### 2.0.1でもimage()とcreateVector()のバグは直っていない
　前回紹介した2つのフレンドリーエラーによるバグは2.0.1でも直っていません。

![sample001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437349/faa77bb6-5467-4304-812f-7c8b4bf64464.png)

なおいずれも捕捉済みで、issueが提示されています。
createVector:

https://github.com/processing/p5.js/issues/7741

image:

https://github.com/processing/p5.js/issues/7579

しかしいずれもactiveではないので、いつ解決するのか見通しが見えません。興味が無いのでしょうか？

　なお、このようにissueを上げようとしてもすでに捕捉済みの場合というのは多いので、提出する際はきちんとすでに捕捉されているかどうかをできる範囲で調べるようにしましょう。自分がバグを直したときも、直した後にも関わらず、リリースしてもいないのに直ってないじゃないかという質問をされたことがあったりしました。

　ちなみにimage関数の第一引数がcreateGraphicsで作ったやつだとエラーが出るのはグローバルの場合だけです。オフキャンバスの関数として呼び出す場合、エラーは生じません。

![graphics1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437349/156d51d3-4652-4c81-91c5-bd38447df130.png)

とはいえ最終的には地に落とさないといけないのであれですが。

　createVectorについてですが、使う人は毎フレーム何百回とか何千回とか呼んでるわけです。今のままこれがデフォルトになったら地獄ですよ。興味が無いとか言ってないで早く直すべきです。開発サイドは新機能にばかり目が行ってしまっていて、従来通りのスケッチを描きたい人にあまりにも冷たい印象を受けます。ほんとうにデフォルトの設定にする気があるのか疑問です。もういつまでもオプションのままでいいような気がします。

### オフキャンバスの関数としてのcolorMode()が廃止されてグローバルで変える仕様になった
　どういうことかというと、以前はcreateGraphicsで作ったキャンバスにcolorModeを指定する場合、それの関数として呼び出していたわけです。

![colorMode3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437349/d6cac24d-cdcb-4abf-8dea-f4e1975d36e8.png)

グローバルで呼び出しても変化がありませんでした。それはグローバルのfill()などに影響します。

![colorMode2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437349/93d3d1c3-cc9d-4994-8a9a-1bd327e33693.png)

それが2.0以降で変更になりました。2.0以降は、そもそもgr自身によりcolorMode()という関数を扱うことができません。

![colorMode4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437349/16c5740f-e602-4793-8e48-d9cff9991f5d.png)

このように変化しません。そして、グローバルで呼び出すとgrのcolorMode「も」変更されます。

![colorMode5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437349/d445b4be-53c0-447b-83e7-d5d435b96db7.png)

すべて共通になったというわけですね。なお有志の方が調べてくれましたがblendModeは個別のままのようです。

![bbbmmm0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437349/ece554e1-4e30-4e87-ab25-5db80173928a.png)

![bbbmmm1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437349/ac8ed7dd-65f4-4dc0-a4ae-f44d4e6dd117.png)

まあこれはそもそもコンテキスト属性で、p5が関係ないので、ある意味当然かもしれません。同じ理由で本来コンテキスト属性であるfontSizeやfillStyle,storkeStyleに該当するあらゆる属性はグラフィック固有のままです。colorModeはp5が絡むので、仕様を変えてもいいということになったのかもしれないですね。

　該当するissueが見つかりませんでした。気が向いたら探してみてください。探す必要性を感じないのでスルーします。

### オフキャンバスのtext関連の関数の戻り値が変更され、チェインで書けなくなった
　以前の仕様では、createGraphicsで作ったグラフィックにtext関数を実行させる場合、戻り値はグラフィック自身になっていました。そのため、チェインで書けたりしました。

![text0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437349/0bac3504-a7d5-4890-ab7b-a5739836fdc1.png)

![text1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437349/c0177236-6a3e-4982-82ba-c3cb8245bec8.png)


2.0以降、この戻り値が変更され、これは不可能になりました。

![text4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437349/949a9fa5-b858-4d86-b19c-6acdb29b4c84.png)

ざっくりいうと、textの戻り値はなく、それ以外はp5のインスタンスが返るようになりました。これも、いつ行われたのかは不明です。まあ特に不便では無いので、どうでもいいですね。

### textBounds()がフォントの関数では無くなり、フォントをロードしてなくても使えるようになった
　以前のtextBoundsはフォントを指定しないと使えないという不便さがありました。

https://p5js.org/reference/p5.Font/textBounds/

また、絵文字でバグるのも合わせて使い勝手が悪かったです。それが変更になりました、

https://beta.p5js.org/reference/p5/textbounds/

このようにフォントを用意しなくても使えるようになりました。なお絵文字にも対応しており、小さい場合は小さくもなります。

![font1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437349/d4774d69-16b3-4ed3-a72b-027f8eaaf4da.png)

ふい字などのカスタムと絵文字が混じる場合でも安心です。

![font2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437349/c6e4febf-1e87-4ab9-a1d9-49a5dd033aaf.png)

便利ですね～

### OpenProcessingのエディタで2.0以降を指定すると、キャンバス外の色がbacckground()などに追従しなくなる
　具体的にはこういうこと：

[該当ツイート](https://x.com/inaba_darkfox/status/1916797150065025088)

background()とか使うと、画面外に色が漏れてしまうわけです。キャンバスの境目が見えづらいので不便です。
それが解消されています。

### OpenProcessingでp5.Soundのリンクが間違っている
　具体的にはこういうことです。

[該当ツイート](https://x.com/inaba_darkfox/status/1916676285658431551)

素直に従来のp5.Soundのスケッチを2.0で実行するとバグります。これを解消するには以下のサイトからCDNを取得すればよいようです。

https://www.jsdelivr.com/package/npm/p5.sound

しかし、一部の関数が使えなくなったりしているので、これが正しいやり方かどうかはちょっと分かんないです。このデモの場合はきちんと動いています。なお、現在も解消されていません。

### 色関連の関数（fill,strokeなど）が重くなった
　一部の関数が2.0以降、重くなっています。とはいえ、毎フレーム2000回とか3000回とか呼び出す場合でないとその差を実感できないレベルなので、普通にスケッチを描いている限りはあまり気にならないかもしれません。とはいえ実際に重くなっているので、ここに書いておこうと思います。調べましたが特にissueも上がっていないようです。
#### ベンチマークテストのテンプレート
　こんな感じで調べています。
```js
function setup() {
  noCanvas();

  // 各種準備を実行する

  let rp = 20;
  let sum = 0;
  const elapseds = [];
  while(rp--){
    let s=millis();
    for(let i=0; i<4000; i++){
      // 計測したい処理をここに記述する
    }
    const elapsed = millis()-s;
    elapseds.push(elapsed);
    sum += elapsed;
  }
  showElapseds(elapseds);
  console.log(`ave:${sum/20}`);
}

function showElapseds(a){
  const res = [];
  for(let i=0; i<20; i++){
    res.push(a[i].toFixed(3));
  }
  for(let y=0; y<4; y++){
    console.log(`${res[y*5]}, ${res[y*5+1]}, ${res[y*5+2]}, ${res[y*5+3]}, ${res[y*5+4]}`);
  }
}
```
たとえばsin関数のベンチマークを取るとこんな感じで表示されます。
```js
  // 計測したい処理部分
  const si = sin(3.14);
```
1.11.5の場合
```
0.300, 0.300, 0.200, 0.100, 0.000 
0.100, 0.000, 0.200, 0.100, 0.000 
0.100, 0.100, 0.000, 0.200, 0.000 
0.100, 0.100, 0.100, 0.000, 0.100 
ave:0.10499999523162842
```
2.0.1の場合
```
4.000, 1.700, 1.700, 1.600, 1.700 
1.600, 4.200, 1.100, 1.300, 1.500 
1.000, 1.100, 1.100, 1.000, 4.500 
1.300, 1.100, 1.000, 0.900, 1.000 
ave:1.7200000047683717 
```
なんか調べたら若干遅くなってる..？なお
```js
  const si = Math.sin(3.14);
```
では...
```
0.100, 0.000, 0.100, 0.000, 0.100 
0.000, 0.100, 0.000, 0.000, 0.000 
0.000, 0.000, 0.100, 0.000, 0.000 
0.000, 0.000, 0.000, 0.000, 0.000 
ave:0.025000011920928954 
```
特に理由が無ければネイティブの方がいいでしょうね。毎フレーム何万回も呼び出す場合などは特に。
　というわけで、サクサク調べていきましょう。

#### colorオブジェクトを作る
　まずは手始めに、色オブジェクトを作ってみましょう。
```js
  // 計測部分
  const col = color("#c0ffee");
```
コーヒーあんま飲まないですが。さて、1.11.5の場合：
```
3.100, 2.100, 2.300, 2.200, 1.800 
4.700, 1.300, 1.300, 1.200, 1.800 
1.500, 2.300, 1.800, 1.700, 1.600 
4.500, 1.700, 1.600, 1.500, 1.500 
ave:2.074999988079071 
```
もちろん誤差もあるので絶対ではないです。何回か試して、これくらいかなぁ、というものを上げています。
2.0.1の場合：
```
24.100, 17.300, 12.900, 13.300, 12.300 
12.000, 12.000, 11.700, 12.000, 12.100 
12.100, 11.800, 11.800, 11.700, 11.900 
12.000, 11.600, 12.000, 12.000, 12.300 
ave:12.94500000476837 
```
ただこれは誤差の範囲を超えています。何度試しても、1.11.5では4すら超えません。確実に遅くなっています。1フレームはどのスケッチでも大体16ミリ秒前後なので、4000回も呼び出すとそのうち75%がこの処理に費やされてしまいます。

#### fill()を実行する（数指定）
　数でfillを指定します。
```js
  // 計測部分
  fill(11,22,200);
```
1.11.5の場合
```
3.500, 1.900, 1.900, 1.800, 1.600 
1.300, 1.400, 4.400, 1.000, 1.200 
1.100, 1.400, 1.100, 1.400, 1.200 
1.200, 1.200, 1.300, 1.200, 4.300 
ave:1.7699999928474426 
```
2.0.1の場合
```
20.400, 16.500, 12.100, 11.600, 11.400 
11.300, 11.900, 11.100, 11.500, 11.300 
10.900, 11.200, 11.100, 11.100, 10.500 
13.200, 11.600, 11.200, 11.700, 12.300 
ave:12.194999992847443
```
どうもfill内部で色オブジェクトを作ってる影響で、それに関する関数は軒並み遅くなってるようです。
#### fill()を実行する（オブジェクト指定）
　次に、あらかじめオブジェクトを作っておきます。
```js
  // 準備部分
  const precol = color("navy");
```
そしてこれを計測部分でfillの引数として使います。
```js
  // 計測部分
  fill(precol);
```
さてどうなるでしょう。1.11.5の場合：
```
2.300, 1.400, 1.500, 1.400, 1.100 
1.100, 1.000, 1.100, 1.100, 0.900 
0.900, 0.800, 0.800, 0.900, 0.800 
0.900, 1.100, 0.900, 3.900, 1.000 
ave:1.2450000047683716 
```
なんと数指定の場合より速くなりました。ちょっと意外ですね。あらかじめ作っておくと嬉しいのでしょうか。
2.0.1ではどうでしょう。
```
37.000, 24.600, 24.400, 23.500, 23.500 
22.900, 40.600, 22.400, 24.100, 22.100 
24.100, 23.400, 23.500, 24.100, 23.600 
24.300, 24.000, 23.900, 23.500, 30.600 
ave:25.50499999523163 
```
ちょっとあり得ないですね。なぜここまで遅くなるのか...

#### strokeの場合
　strokeで同じことをした場合の結果をざっくり載せておきます。
　1.11.5で数指定
```
stroke(11,22,200);
// 1.11.5:
3.300, 1.900, 1.800, 5.800, 1.400 
1.300, 1.400, 1.500, 1.200, 1.300 
1.100, 1.400, 1.500, 1.600, 1.700 
1.400, 1.300, 1.500, 1.600, 1.600 
ave:1.7800000071525575 
// 2.0.1:
23.200, 15.800, 13.900, 33.800, 12.900 
11.400, 11.800, 11.600, 11.300, 11.300 
11.100, 11.300, 11.000, 10.700, 11.100 
10.800, 11.100, 11.000, 11.300, 10.900 
ave:13.364999997615815 
```
```
stroke(col);
// 1.11.5
2.400, 1.500, 2.100, 1.600, 1.200 
1.100, 1.100, 5.700, 0.900, 0.800 
0.900, 0.800, 0.700, 0.900, 0.800 
1.000, 0.800, 0.800, 0.800, 0.900 
ave:1.3400000095367433
// 2.0.1
36.400, 24.500, 23.500, 23.600, 22.900 
23.000, 23.600, 22.800, 22.800, 22.900 
23.300, 21.800, 39.100, 23.100, 22.500 
23.000, 21.800, 22.100, 21.500, 21.200 
ave:24.27000000476837
```

#### おまけ：drawingContextを使う場合
　ちなみにネイティブの処理ではどうなっているかというと、
```
drawingContext.fillStyle = "navy";
0.500, 0.300, 0.300, 0.300, 0.200 
0.200, 0.300, 0.300, 0.300, 0.200 
0.300, 0.200, 0.300, 0.300, 0.200 
0.400, 0.200, 0.300, 0.300, 0.300 
ave:0.28500000238418577
```
```
drawingContext.strokeStyle = "teal";
0.400, 0.300, 0.300, 0.300, 0.200 
0.300, 0.300, 0.200, 0.300, 0.300 
0.200, 0.300, 0.200, 0.400, 0.100 
0.400, 0.200, 0.300, 0.200, 0.300 
ave:0.275
```
アホみたいに速いです。ただ色を決めるだけの処理がなぜこんなに遅くなるのか疑問です。簡易スマホみたいに、余計な機能は良いから速さが欲しい人のためのメソッドがあったら便利な気がします。いちいちdrawingContextを呼び出すのは手間ですから。

#### randomとnoise
　ついでにrandomとnoiseも調べておきます。
```
const r = random();
// 1.11.5
0.500, 0.300, 0.300, 0.400, 0.100 
0.300, 0.300, 0.100, 0.200, 0.100 
0.100, 0.100, 0.100, 0.100, 0.100 
0.200, 0.100, 0.100, 0.100, 0.200 
ave:0.19000000953674318 
// 2.0.1
0.800, 0.500, 0.500, 0.300, 0.300 
0.200, 0.300, 0.200, 0.400, 0.200 
0.200, 0.200, 0.200, 0.200, 0.200 
0.200, 0.300, 0.200, 0.200, 0.200 
ave:0.2899999856948853
```
あんま変わんないですね。noiseについて：
```
const n = noise(1,2,3);
// 1.11.5
2.300, 0.600, 0.500, 0.400, 0.500 
0.500, 0.600, 0.300, 0.400, 0.300 
0.300, 0.300, 0.300, 0.300, 0.200 
0.300, 0.400, 0.300, 0.300, 0.300 
ave:0.46999999284744265 
// 2.0.1
9.700, 14.900, 13.300, 5.400, 4.800 
4.800, 4.400, 5.100, 4.500, 4.700 
4.400, 4.400, 4.500, 4.400, 4.600 
4.500, 4.400, 4.500, 4.400, 4.600 
ave:5.814999985694885 
```
noise遅くなってますね。まあ4000回程度なら気にならないレベルかと思います。

#### lerpColor
これはあんま変わんなかったんですが、一応上げておきます。
```
// 準備：
  const col = color("navy");
  const other = color("teal");
// 計測部分
  const l = lerpColor(col,other,0.5);
// 1.11.5
8.100, 5.800, 9.800, 5.500, 6.700 
5.700, 5.800, 5.600, 6.000, 6.000 
5.600, 5.500, 6.000, 5.400, 5.300 
5.100, 5.200, 5.400, 5.400, 5.700 
ave:5.979999995231628
// 2.0.1

19.800, 16.300, 11.200, 14.200, 13.600 
10.800, 14.100, 13.900, 29.900, 10.800 
12.800, 10.400, 13.700, 13.400, 10.100 
13.200, 13.200, 10.300, 13.100, 13.000 
ave:13.889999985694885 
```
ほぼ1フレーム弱ですね。まあlerpColorを毎フレーム4000回も呼び出すことは無いと思うので、あんま気にならないかもですね。lerpColor自体がそれなりに重い処理なので、それがさらに重くなった感じですかね。

# おわりに
　利点しか伝えないのでは片手落ちだと思います。なのでパフォーマンスについて触れることにしました。もっともほとんど気にならないと思うので、安心して使ったらいいと思います。

　自分が機能実装した時、1.6のベジエ曲線に色を付ける話の時ですが、ベンチマークについて釘を刺されたのを覚えています。なのでパフォーマンスを計測してプレゼンの材料に使いました。その結果実装してもらえました。本来仕様変更はそのくらい慎重にやるものですが、何があったんでしょうか。

　ここまでお読みいただいてありがとうございました。

## 色についての補足
　色について詳細な議論がなされたようです。

https://github.com/processing/p5.js/pull/7406

　ざっくり言うと、現在の（～1.11.5という意味）色オブジェクトは表現力に問題があり、CSSの色を十二分に表現できないからダメ、改善が必要、ということだそうです。で、colorModeにも何らかの変更が必要、これもちょっと問題がある、とのことです。で、破壊的な変更になるので2.0以降が望ましい、とのことで2.0以降の色の仕様を変えることが決まって、詳細な議論が展開されています。しかし画像がほとんど出てこないのできちんと読まないと何が問題なのかわからなさそうです。
　そもそも1.11.5までのp5の色の仕様の何がそんなに問題なのかがピンと来ないですが...
　ここでやってるベンチマークにしても、これは自分の富士通のノートパソコンで **ここに上げたコードでそのまま** 実験した結果を上げているだけです。このやり方でいいかどうかも分かんないですし、正式リリースまでに速度が改善されるかもしれません。未知の部分が多いです。来年にならなければ、すべてが明らかになることはないでしょう。
　先のissueにも「パフォーマンスが凄いから採用しよう」みたいな内容が出てくるので、何か勘違いをしている可能性もあります。慎重になるべきだと思います。

## さらなる追記
　2.0のパフォーマンスがおかしいという話は一応、議論されたようです。

https://github.com/processing/p5.js/issues/7539

そして改善されたことになっています。

https://github.com/processing/p5.js/pull/7543

それでこの速度なので、諦めるしかなさそうです。1.11.5とも仲良くやっていきましょう。以上です。

## 色
　labやoklch形式が使えるようになりました。それが遠因となってfillが遅くなったようなんです、が、なぜかレンダリングは全体的にわずかながら遅くなっているので、問題はそれほど単純な話では無いようです。ハッキングして色々やったんですが、なぜか一番重いはずのカラーオブジェクトの構築処理を省いても遅かったので、全体の機構に問題が...？詳しくないので正直分かりません。偉い人たちに、おまかせするしかないです。