きゃべログ

p5.jsのアドオンライブラリを作ろう

p5.jsjavascript

この記事ではとてもシンプルなp5.jsのアドオンライブラリをつくり、それを配信する一覧の流れを体験する方法を解説します。OpenProcessingなどのWebライブラリから読み込むことで簡単に使えるライブラリを作ることができます。

先日、p5.axesというライブラリを作りました。 X,Y,Zの3軸を線で描画するだけのシンプルなユーティリティライブラリです。

p5.jsで3Dの作品をつくる際に、どちらが何軸方向かわからず困ったので、スケッチ内に便利関数を作るところから始めました。 インポートするだけで簡単に使えるライブラリにできたら便利かな、と思い立ち、ライブラリを作ることにしました。 p5.jsの公式ドキュメントにアドオンライブラリの作り方を詳しく解説した記事があり、2時間かからないくらいでライブラリの作成から公開までできました。 やってみると意外と簡単にできたので、広く知ってもらいたい思いから、この記事を書くに至りました。簡易化のため、難しい部分は説明を端折っていたりしますので、ご了承ください。

前置き

アドオンライブラリって?

端的に説明すると機能拡張用のソフトウェアです。p5.jsには最初から便利な機能がたくさん含まれています。後からこのライブラリを読み込むことで、それらの機能に加えて、できることを増やしたり、便利にすることができます。p5.js自体もそのものがライブラリなので、それをさらに便利にするライブラリ、とも言えます。

あなたにとって、または特定のユースケースにおいて、p5.js本体の機能だけでは不便に感じることもあるでしょう。p5.jsは幅広いユーザーをターゲットとしているので、ニッチな機能を本体に組み込むことはあまりいいアイデアではないと理解できると思います。そこで、アドオンライブラリの出番です。p5.jsで扱いやすい便利機能を、自由に作ることができます。

アドオンライブラリでできること

アドオンライブラリにより使いやすい形で機能をまとめ、提供することができます。 ブラウザにはJavaScriptにより扱える膨大な数のAPIが実装されています(参考: Web API - MDN)。このAPIを組み合わせることでさまざまなことが実現できます。 スケッチの中に自由にJavaScriptが書けるので、本質的にできること自体は変わらないのですが、それを簡単に使えるようになることで実質的にできることが増える、というのがポイントです。

網羅はできていないですが、以下のような観点でライブラリでできることを整理することができると思います。具体例を示してみます。

  • 新しい機能を使えるようにする
    • p5.jsでは本来カバーされていない新しい機能を、p5.jsで扱いやすい形で提供します。
      • ml5.js: https://ml5js.org/
        • ポーズ認識や画像分類などの機械学習の機能を提供します。
  • スケッチの表現を拡張する
    • p5.jsの基本機能を使って、より高度な表現を実現できる機能を追加するものです。
  • 自分で実装すると大変な機能を、簡単に使えるようにする
    • ブラウザで提供されている機能の中には、コンセプト自体は難しくはないものの、仕様の理解には少し時間がかかるようなものが存在します。そうした細かいAPIの仕様を知らなくても、簡単にJavaScriptで提供されている機能が扱えるようになります。
      • p5.capture: https://github.com/tapioca24/p5.capture
        • スケッチを動画としてキャプチャするためのライブラリです。MediaStream Recording APIを使えば自分でもスケッチを録画することは可能ですが、自前でやるのは結構大変です。それがライブラリをインポートすることで楽になります。
      • p5.joystick: https://github.com/Vamoss/p5.joystick
        • ジョイスティックの入力を扱うためのライブラリです。コントローラーにアクセスするためのGamepad APIを直接使わなくても簡単な記述で扱えるようになります。

他にもいろんなアドオンライブラリがあります。p5.js公式のライブラリ一覧をご覧ください。https://p5js.org/libraries/

どんなときにライブラリを作るのか

ライブラリを作るモチベーションに正解はないと思いますが、一例として以下のような時はライブラリ作成を検討してみても良いかなと思います。

  • いつも同じようなコードを繰り返し書いているなぁという時
  • p5.jsの関数みたいに簡単にこんなことができたらいいのになぁ、というとき

ハンズオン

前置きが長くなりましたが、実際に作ってみるのが一番理解が早いです。やってみましょう。流れはこんなかんじです。

  • どんなライブラリを作るか考える
  • ライブラリを作成する
  • 動作確認: スケッチに埋め込んで使ってみる
  • オプション: ライブラリを公開する
  • オプション: ドキュメントを書こう
  • オプション: サンプルコードを書こう

どんなライブラリを作るか考えよう

目的を考える

どんなことができるライブラリかを考えましょう。これが一番大事です。作り始める前に、どんなことができるといいな、と考えてみましょう。普段コーディングしているときの困りごとや、こんなことができると面白いかも、という経験があるとなお良いです。

使い方を考える

ライブラリを使う人はどのようにこのライブラリを使うのかを考えます。たとえば、以下のような使い方が考えられます。

  • スケッチの中で呼び出して使える
  • インポートすれば自動で機能が有効になる
  • draw関数の処理の初めに実行される

1つ目のスケッチの中で呼び出して使える、というのは最も作り方がシンプルです。p5.jsのいろんなメソッドのように、シンプルにglobalな関数として呼び出せるタイプのものです。

2つ目と3つ目は、p5.jsで定義されるsetupdrawなどのライフサイクルに合わせて実行されるものです。setup関数が実行される前後や、draw関数が実行される前後などに指定した処理を実行することができます。今回のサンプルでは簡潔化のため、イベントに応じた処理の登録は割愛します。もし興味のある方は、p5.js公式ページのCreating an Addon Library Step6をご覧ください。

ライブラリを作成しよう

例として画面を方眼紙のように分割する線を描画する関数を追加するライブラリを作ってみましょう。画面を整数numで指定した数に分割するとして、grid(num)の形式で使うことを想定します。

スケッチ上でベースを作る

まずは通常のスケッチを書くように、p5.js Web Editorgrid関数を作るところから始めましょう。

以下のようにgrid関数を作って、それをdraw関数内で呼び出してみます。方眼紙のような線が描けているのではないかと思います。

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

function draw() {
  background(220);
  grid(5);
}

// n: 分割数 number of division(2 <= n)
function grid(n){
  // タテ線の間隔(X軸方向の間隔)
  const X_PITCH = width/n;
  // ヨコ線の間隔(Y軸方向の間隔)
  const Y_PITCH = height/n;
  
  // ヨコ線 vertical lines
  for(let i=1; i<n; i++){
    line(0, i*Y_PITCH, width, i*Y_PITCH)
  }
  
  // タテ線 vertical lines
  for(let i=1; i<n; i++){
    line(i*X_PITCH, 0, i*X_PITCH, height)
  }
}

作成した機能をライブラリに切り出す

新しいJavaScriptファイルを作成します。ここではaddon.jsという名前にしましょう。

p5.js Web Editorで新しいファイルを作成する方法… 再生ボタンの下にある「>」ボタンを押して「Sketch File」メニューを開き、出てきた「+」ボタンを押します。するとさらにメニューが出てくるので「Create file」を選択し、出てきたポップアップにファイル名を入力します。「Add File」ボタンを押せばファイル作成完了です。

先ほど作ったgrid関数をそのままaddon.jsに移動します。

/* addon.js */
// n: 分割数 number of division(2 <= n)
function grid(n){
  // タテ線の間隔(X軸方向の間隔)
  const X_PITCH = width/n;
  // ヨコ線の間隔(Y軸方向の間隔)
  const Y_PITCH = height/n;
  
  // ヨコ線 vertical lines
  for(let i=1; i<n; i++){
    line(0, i*Y_PITCH, width, i*Y_PITCH)
  }
  
  // タテ線 vertical lines
  for(let i=1; i<n; i++){
    line(i*X_PITCH, 0, i*X_PITCH, height)
  }
}

1行書き換えて、p5.jsのプロトタイプオブジェクトのメソッドにこの関数を追加します。 よくわからない場合はひとまず、p5.jsの他のメソッドのように、どこでもgridを呼び出せるようにするおまじないと思っていただいてOKです。 オブジェクトのプロトタイプについて詳しく知りたい方は下記リンクをご参照ください。

オブジェクトのプロトタイプ | MDN

ここで一点だけ、関数名がp5.jsの他のメソッドやプロパティと重複しないように気をつけましょう。もし既存のメソッドやプロパティと名前が重複していると動作を上書きすることになってしまいます。(ただし、敢えて動作を置き換えることを意図したライブラリであればOK)

// n: 分割数 number of division(2 <= n)
p5.prototype.grid = function (n){  // タテ線の間隔(X軸方向の間隔)
  const X_PITCH = width/n;
  // ヨコ線の間隔(Y軸方向の間隔)
  const Y_PITCH = height/n;
  
  // ヨコ線 vertical lines
  for(let i=1; i<n; i++){
    line(0, i*Y_PITCH, width, i*Y_PITCH)
  }
  
  // タテ線 vertical lines
  for(let i=1; i<n; i++){
    line(i*X_PITCH, 0, i*X_PITCH, height)
  }
}

そして、index.htmlファイルを書き換えて、sketch.jsの後にaddon.jsを読み込むようにします。

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.4/p5.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.4/addons/p5.sound.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>
    <script src="addon.js"></script>  </body>
</html>

これでgrid関数が使える、とてもシンプルなライブラリができました。

[応用編 Part1: スキップ可] draw関数の終わりに処理が自動で呼び出されるようにする

上記のような、使える関数を増やすだけのライブラリを作るだけなら上記までで完成です。 物足りないな、という方はこのセクションにもチャレンジしてみてください。

ここでは、draw関数内で明示的にgrid関数を呼び出さなくてもマス目が描かれるようなライブラリに変えます。p5.jsのsetupdrawのライフサイクルに合わせて動作するライブラリは、p5.jsのアドオンライブラリを作る醍醐味かもしれません。

drawイベントの後にマス目を描くようにライブラリを変更しましょう。そのためにp5.prototype.registerMethodというメソッドを使います。 1つ目の引数にはどのタイミングに呼び出されるべきかを、2つ目の引数には呼び出す関数を指定します。 renderGridPreDrawという関数を追加しています。p5.prototype.registerMethodの2つめの引数は引数なしで呼び出せる関数である必要があるため、gridをラップする関数にしています。ここでは3x3のマス目となるようにしました。

drawの後意外にも関数を実行させられるタイミングがいろいろあります。Creating an Addon Library Step6 | p5.jsをご覧ください。

// addon.js
// n: 分割数 number of division(2 <= n)
p5.prototype.grid = function (n){
  // タテ線の間隔(X軸方向の間隔)
  const X_PITCH = width/n;
  // ヨコ線の間隔(Y軸方向の間隔)
  const Y_PITCH = height/n;
  
  // ヨコ線 vertical lines
  for(let i=1; i<n; i++){
    line(0, i*Y_PITCH, width, i*Y_PITCH)
  }
  
  // タテ線 vertical lines
  for(let i=1; i<n; i++){
    line(i*X_PITCH, 0, i*X_PITCH, height)
  }
}

p5.prototype.renderGridPreDraw = function(){  p5.prototype.grid(3)}p5.prototype.registerMethod("post", p5.prototype.renderGridPreDraw)

では、sketch.jsでgridを呼び出さなくても、drawの都度マス目が描画されることを確認してみましょう。 以下のように、gridをコメントアウトして、前後関係がわかるようにcircleを追加しました。

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

function draw() {
  background(220);
  // grid(5);  circle(100,100,100)}

試しに実行してみましょう。 grid関数を命じ的に呼び出さなくても、マス目が描かれていることが確認できるはずです。 なお、円よりも前面にマス目が描かれていることから、draw関数の処理後にマス目が描かれていることがわかります。

スケッチの実行結果

[応用編 Part2: スキップ可] ライブラリに設定を追加する

1つ上のセクションで、draw関数の処理の後にマス目を描くことができるようになりました。 1点モヤモヤすることがあると思います。マス目の数が3x3で固定になってしまっており、好きな分割数にできないということです。 分割数を設定を追加できるようにしてみましょう。

以下のようにaddon.jsを編集します。

// n: 分割数 number of division(2 <= n)
p5.prototype.grid = function (n){
  // タテ線の間隔(X軸方向の間隔)
  const X_PITCH = width/n;
  // ヨコ線の間隔(Y軸方向の間隔)
  const Y_PITCH = height/n;
  
  // ヨコ線 vertical lines
  for(let i=1; i<n; i++){
    line(0, i*Y_PITCH, width, i*Y_PITCH)
  }
  
  // タテ線 vertical lines
  for(let i=1; i<n; i++){
    line(i*X_PITCH, 0, i*X_PITCH, height)
  }
}

// 設定を保持するプロパティp5.prototype.gridNumber = 3
// 設定を変更するメソッド
p5.prototype.setGridNumber = function (n){  if (n < 2) return  p5.prototype.gridNumber = n}
p5.prototype.renderGridPreDraw = function(){
  p5.prototype.grid(p5.prototype.gridNumber)}

p5.prototype.registerMethod("post", p5.prototype.renderGridPreDraw)

まず、19-20行目で、gridNumberというプロパティを用意します。この値をマス目の分割数として使用することを意図しています。デフォルト値として3を設定しました。

スケッチからこのgridNumberを変更するために、setGridNumberメソッドを作成します。引数には2以上の数値を取り、その値でgridNumberを更新します。

最後に、renderGridPreDrawメソッドの中で呼び出すgridの引数に、先ほど作成したgridNumberを与えるようにします。

では、設定の変更を試すスケッチを書いてみましょう。先ほどのスケッチから変更した箇所をハイライトしています。

//  sketch.js
function setup() {
  createCanvas(400, 400);
  setGridNumber(10)}

function draw() {
  background(220);
  // grid(5);
  circle(100,100,100)
}

setup関数の中でsetGridNumber(10)を実行し、画面を10分割するマス目を描くよう指定しました。スケッチを実行してみると、10分割されるようになったはずです。

スケッチの描画結果

ライブラリを公開しよう

上記セクションで、マス目を描画するライブラリが作成できました。作ったライブラリを公開してみましょう。 このセクションではソースコードのバージョンを管理するGitというシステムを使用します。Gitについて細かく説明すると分厚めの本が書ける分量になってしまうので、今回触れる部分についてのみ、その都度ごく簡単に説明します。

Gitについて詳しく知りたい方はこちらのページが易しくておすすめです。 サル先生のGit入門〜バージョン管理を使いこなそう〜【プロジェクト管理ツールBacklog】

事前準備: GitHubのアカウントを作成

今回ライブラリを公開するためにGitHub Pagesという、GitHub上のリポジトリからWebページをホスティングするサービスを利用します。

利用のためにはGitHubのアカウントが必要となります。GitHubのアカウントをお持ちでなければ作成しましょう。

https://github.com

リポジトリを作成

https://github.com/new から新しいリポジトリを作成します。リポジトリというのはプロジェクトのファイルや変更履歴を保存する場所です。 リポジトリ作成ページを開くといろいろ設定項目が出てきますが、今回は以下のように設定しておきます。

  • リポジトリ名
    • 分かりやすくライブラリ名と合わせるのが良いでしょう。
  • 公開範囲
    • Publicとします。
  • Add a README file
    • チェックを入れましょう。あとのパートで使います。

これらが設定できたら「Create repository」ボタンを押してリポジトリを作成します。

リポジトリの設定変更

リポジトリが作成できたら、リポジトリのトップページに遷移します。ここにはリポジトリに入っているファイルやディレクトリが表示されます。 設定メニューを押して、設定ページを開き、左側に表示されるメニューから「Pages」を選択しましょう。

ここで「Branch」の項目を「main」に設定し、ディレクトリは「/ (root)」となっていることを確認して、「Save」ボタンを押します。 この設定により、リポジトリが更新されると自動的にファイルがWebサイトとして公開されるようになります。

リポジトリのPages設定画面

保存が完了したら、メニューの「Code」を押して、リポジトリのトップページに戻りましょう。

github.dev Webエディタを開く

次に、先ほど作成したライブラリをリポジトリに追加しましょう。コードの編集にはgithub.dev Webベースエディタというソースコードエディタを使用します。github.dev Webベースエディタは、インストール不要でブラウザから利用できる、Githubの機能を使うのに必要な設定が組み込まれたVisual Studio Codeベースのエディタです。

使い方は簡単です。次の画像のようなリポジトリのトップページ(Codeメニューを押した後の画面)で「.(ピリオド)」キーを押してみてください。

リポジトリページ

「Setting up your web editor」というローディングメッセージのあと、次の画像のようなエディタ画面が開くはずです。 ブラウザからピリオドキーをぽちっとするだけで、簡単にエディタが開けます。便利ですね。

github.dev Webエディタの画面

ライブラリファイルをエディタ上で作成する

では配布するアドオンライブラリのJavaScriptファイルを作成しましょう。 「エクスプローラ」と書かれたエリアで右クリックし、JavaScriptファイルを作成しましょう。このまま配布されるライブラリのファイル名になるので、ライブラリ名を含んだファイル名にするのがよいでしょう。今回の例ではp5.grid.jsとします。 ファイル作成する様子

このファイルに、先ほどp5.js Web Editorで作成したaddon.jsの中身をコピペします。 jsファイルを編集

Gitにおけるファイル更新のステップについて

これからいくつかのステップに分けてファイルをリポジトリに反映していきます。厳密ではないですが、それぞれのステップの内容について簡単に説明します。

  1. ステージング
    • あとで説明する「コミット」のための準備です。コミットに含めるファイルを指定することです。
  2. コミット
    • ステージングされたファイルにある変更をローカルリポジトリの変更履歴に記録します。
  3. プッシュ
    • ローカルリポジトリにコミットされた変更をリモートリポジトリにアップロードします。

本来ローカルでGitを扱う場合にはローカルリポジトリとリモートリポジトリの概念が存在します。ローカルリポジトリは自分のPCの中に持っている履歴、リモートリポジトリはインターネット上など外部に持っている履歴です。今回のプロジェクトではリモートリポジトリはGitHubで管理しているリポジトリを意味します。 github.dev Webエディタでは、「コミット」と「プッシュ」が同時に行われコミットすると同時にリモートリポジトリに変更をプッシュします。

ライブラリファイルをリポジトリにコミット&プッシュする

では実際にファイルをリポジトリにプッシュしてみましょう。

左のメニューから3つの円が線でつながっているようなアイコンのメニューを押します。これが「ソース管理」タブです。Gitリポジトリに関する操作ができます。本来、PCのローカルでGitの設定をしようとすると初心者には大変ですが、github.devのエディタは設定不要でリモートリポジトリに直接アクセスできます。便利ですね(2回目)。

先ほど作成したJavaScriptファイルの行の「+」ボタンを押して、ファイルをステージングします。

ソース管理タブ

次に「メッセージ」と書いてあるテキストボックスに今回の変更に関するコミットメッセージを記載します。後からどんな変更をしたのかわかるように書いておく、変更内容と一緒に保存される一言メモのようなものです。例では「ライブラリファイルを追加」としました。

そして「コミットとプッシュ」ボタンを押しましょう。これでファイルをリポジトリにコミット&プッシュできました。

コミットメッセージを入力

ライブラリが公開できたことを確認する

GitHub Pagesが有効になったリポジトリが更新されると、1分から数分程度でそれらがWebに公開されます。ライブラリファイルが更新できていることを確認しましょう。

改めてリポジトリのトップページに戻ります。このページのURLはこのようになっていると思います。

https://github.com/<ユーザー名>/<リポジトリ名>

リポジトリのトップページのURLを参考に、次のようにURLを組み立ててアクセスすると、GitHub Pagesで公開されるWebページにアクセスできます。

https://<ユーザー名>.github.io/<リポジトリ名>

プッシュしたライブラリファイルにアクセスする場合は次のようなURLになります。

https://<ユーザー名>.github.io/<リポジトリ名>/<ライブラリファイル名>

私の場合はこのようになっています。

https://github.com/cabbage63/p5.grid
↓ GitHub PagesのURL
https://cabbage63.github.io/p5.grid
↓ 追加したライブラリのパスを追加
https://cabbage63.github.io/p5.grid/p5.grid.js

組み立てたURLにアクセスすると、ライブラリのJavaScriptファイルが表示できるはずです。次のセクションで使用するので、このURLを控えておきましょう。

ライブラリファイルをブラウザで表示

ライブラリファイルをインポートしてみる

では公開したライブラリが使えるかを確認してみましょう。

まずはp5.js Web Editorで確認してみましょう。 新しいスケッチを作成し、index.htmlに先ほど作ったファイルを読み込みましょう。ハイライト部分をご自身が公開したライブラリに合わせてURLを変更してください。

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.4/p5.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.4/addons/p5.sound.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>
    <script src="https://<ユーザー名>.github.io/<リポジトリ名>/<ライブラリファイル名>"></script>  </body>
</html>

あとはライブラリを使い方に合わせて、必要に応じてスケッチを編集して、プレビューしてみましょう。 ライブラリをインポートする

OpenProcessingのインポートも試してみましょう。新しいスケッチを作成し、右側のSKETCHメニューから「LIBRARIES」の右側にある「+」ボタンを押します。 すると「Paste URL or file name」と書かれたテキストボックスが表示されるので、ここに先ほど控えておいたURLをペーストします。

OpenProcessingのライブラリ設定に自作ライブラリ追加

同様に必要に応じてスケッチを編集して実行してみてください。

動作確認ができたら、以上でライブラリ公開は完了です。ライブラリを編集したい場合は同様にgithub.devウェブエディタからファイルを編集し、ステージング・コミット・プッシュすればOKです。

ドキュメントを書こう

他の人にライブラリを使ってもらうには、どのように使えば良いのかをドキュメントで記しておくことが大事です。 自分だけが使うライブラリの場合も、後から見返して思い出せるようにドキュメントを書いておくと良いと思います。

通常リポジトリのREADME.mdに記載することが多いです。ライブラリファイルを変更し、ステージング・コミット・プッシュしたのと同様の手順で今度はREADME.mdというファイルを編集しましょう。これをプッシュすると、リポジトリのトップページに「README.md」の中身が表示されるようになります。

Markdown記法で書くことで、いい感じにスタイリングしてくれるので、うまく活用しましょう。 Markdown - Wikipedia

記載する内容については、私が先日作成したp5.axesのREADMEも一例として参考にしてみてください。

サンプルコードを書こう

使い方を説明する上で、サンプルコードは一番直観的で分かりやすいです。 ドキュメントにはコピペすれば動くサンプルコードを掲載しておくと使いやすいと思います。

まとめ

この記事ではライブラリを作成して、公開するところまで一通り解説しました。無事公開まで辿り着けたでしょうか。 難しくてわからない、困ったことがあれば、SNSなどフィードバックをいただけると幸いです。

この記事がオリジナルのアドオンライブラリ作成や公開のきっかけになれば嬉しいです。なにか完成したらぜひ教えてください!


きゃべ / Masaya Kurahashi
きゃべ / Masaya Kurahashi
Software Engineer, Product Manager