p5.jsによるクリエイティブコーディング入門

ウェブ技術を用いたクリエイティブコーディング環境にはさまざまなものがありますが、プロトタイピングや2D表現には、Canvas 2DをラップしたJavaScriptライブラリ「p5.js」が非常に便利です。この記事ではp5.jsの強み、便利な関数と作例を紹介し、自分で作成した作品の公開方法について解説します。

p5.jsとは

p5.jsは、ウェブ上でグラフィカルな表現ができるJavaScriptライブラリで、基礎的なプログラミングの知識さえあれば手軽に創作を始められるのが魅力です。

またp5js.orgが提供しているウェブエディターを使うと、環境構築なしにブラウザ上でクリエイティブコーディングが始められます。たった数十行のみのコードでもメディアアート作品のような出力が得られる、とても興味深いライブラリです。今回はp5.js Web Editorを使って簡単なアニメーションを制作しましょう。

p5.js Web Editorの使い方

p5.js Web Editorにアクセスすると、setup(), draw()というJavaScriptの関数が書かれたテキストエディターとPreviewに分かれたウェブエディターが表示されます。さまざまな機能がありますが、今回触れるのは①テキストエディター、②プレビュー、③実行ボタン、④Auto-refresh(自動更新)の4箇所です。

p5.js Web Editor

主な使い方は、テキストエディター(①)にコードを記述 → 実行ボタン(③)をクリックし、実行 → Preview(②)で実行結果を確認 → 修正、という流れになります。

p5.jsではsetup()関数の中で描画に関する設定や初期化を行い、draw()関数の中で図形の描画やデータの更新を行います。setup()はプログラム実行時に一度だけ実行され、draw()何度も実行され続けます。キャンバスの大きさや図形を描く色の設定、draw()の実行頻度の設定など、一度だけ実行したい処理はsetup()に記述します。逆に、描画図形の座標を変化させるなど、何度も更新してアニメーションさせたい部分はdraw()内に記述します。

またAuto-refresh(④)のチェックボックスをオンにすると、プログラム更新時に実行ボタンを押すことなく自動でPreviewを更新できます。一方で、draw()が無限ループである性質上、計算量の多いプログラムを書くと動作が重くなることがあります。はじめのうちはAuto-refreshをオフにすることをオススメします。

準備が整ったら、今回は「ランダムに動く点群とそれらをつなぎ合わせる線」という作例を実装しながらp5.jsの活用方法について理解を深めましょう。

作例:ランダムに動く点群とそれらをつなぎ合わせる線

今回作るのは以下のアニメーションです。これは、白色で描かれた100個の点群がそれぞれランダムな速度で移動し、点どうしの距離が近くなると白い線で結ばれるというものです。それでは5つのステップに分けてこのアニメーションを実装しましょう。

1. 変数の定義

まず、プログラム全体で使用する変数を定義します。プログラム全体で使いたい変数は、setup(), draw()の外側に記述します。今回の作例であれば、点群の数や、2点間に線を引く閾値、点群を管理する配列を定義します。

/* 変数の定義 */
const NUM_POINTS = 100;
const MAX_LINE_DIST = 100;
const points = [];

点群の数や線を引く距離の閾値は、setup()draw()内に直接書くこともできますが、あとで値を調整しやすくするために、関数の外に定義しています。

2. キャンバスの初期化

次に図形を描画する領域となるキャンバスや図形を描く色の設定を行いましょう。createCanvas()関数で640px x 640pxの描画領域を確保し、線の塗り色をstroke()を使って指定します。

function setup() {
  /* キャンバス・描画設定 */
  createCanvas(640, 640);
  stroke(255, 100);
}

p5.jsではstroke()以外にもfill()background()など、さまざまな色指定の関数が用意されています。色の指定方法もさまざまなのですが、引数に整数を1つ渡す場合はグレースケール3つ渡す場合はRGBで値を設定できると覚えておくとよいです。

今回はstroke(255, 100)という色指定を選びました。このように引数に整数を2つ渡す場合は、1つ目が0〜255の256段階で表されたグレースケールの色、2つ目は同様の256段階で表されたアルファ値になります。なのでstroke(255, 100)はやや透明度のある白、ということになりますね。

ちなみに、引数に整数を4つ渡す場合は、1〜3つ目の引数でR・G・Bの値をそれぞれ256段階で指定し、4つ目でアルファ値を設定できます。

この段階で実行ボタンを押すと640px x 640pxの真っ黒なキャンバスが描画されることが確認できます。注意すべき点として、p5.jsのキャンバスはy軸の正の方向が下向きです。つまり、yの値が大きいほどキャンバスの下側、小さいほどキャンバスの上側にものが描画されます。

キャンバスの初期化

3. 点群の初期化

次に描画する点群の配列を初期化します。1つ1つの点を2次元平面内で動かしたいため、点の座標を表すプロパティx, yとそれぞれ方向の速度を表すプロパティvx, vyを用意しましょう。たとえば、右方向に進んでいく点をキャンバスの中央に配置したい場合、以下のようなプロパティを点にもたせます。

const point = {
  x: 320,
  y: 320,
  vx: 1,
  vy: 0,
};

今回はキャンバス内に100個の点群を、バラバラな速度で動かしたいので、p5.jsの乱数を表す関数であるrandom()を使ってバラバラな位置に配置し、バラバラな速度で動かすようにしてみましょう。p5.jsのrandom()は、0から与えられた引数未満のランダムな小数を返す関数です。たとえばrandom(4)とした場合、0以上4未満の小数を返します。また、引数をrandom(a, b)のように2つ与えた場合、a以上b未満の小数を返します。createCanvas()で定義したキャンバスの横幅、縦幅はそれぞれwidth, heightという変数に自動的に代入されます。よって、点は以下のコードで実現できます。

const point = {
  x: random(width),
  y: random(height),
  vx: random(-1, 1),
  vy: random(-1, 1),
};

そしてこれらを最初に用意した配列pointsに100個格納しましょう。

function setup() {
  /* 省略 */
  for (let i = 0; i < NUM_POINTS; i++) {
    /* 点群の初期化 */
    const point = {
      x: random(width),
      y: random(height),
      vx: random(-1, 1),
      vy: random(-1, 1),
    };
    points.push(point);
  }
}

4. 点群のアップデート

ここまでのコードで今回の作例のセットアップができました。ここからは点群や線の描画、データ更新を記述してアニメーションさせましょう。はじめに、draw()内で、先ほど初期化した点群の更新・描画を行います。初期化したpointsに以下の処理を実装しましょう。

① 与えられた速度で移動する

② キャンバスの端まできたら同じ速さで跳ね返る

③ 点を描画する

まず、pointsで表現した点群のそれぞれに同様の処理を行うため、forループで点を1つずつ取り出しましょう。

①の処理に関しては、点のそれぞれの座標に速度を足します。

次に、②の処理に関しては4つの画面端 (x=0, x=640,y=0, y=640) を考慮する必要があります。今回は点の座標が端に達した際に速度に-1をかけて反対方向に動くようにします。

そして③の処理に関しては、p5.jsの点を描画する関数であるpoint(x, y)を使用します。これは与えられた引数x, yの位置に点を描画する関数です。今回はpoints内の点オブジェクトがもっているx, yプロパティの位置に点を描画しましょう。また、draw()は連続して何回も実行され続けるため、前回実行したdraw()の描画が残りキャンバスが真っ白になってしまいます。そのため、background(0)を実行することで、canvasを真っ黒に初期化する必要があります。これらを実装すると以下のようなコードになります。

function draw() {
  background(0); // canvasを初期化
  for (const p of points) {
    /* ① 与えられた速度で移動する */
    p.x += p.vx;
    p.y += p.vy;

    /* ② キャンバスの端まできたら同じ速さで跳ね返る */
    if (p.x < 0 || p.x > width) {
      p.vx *= -1;
    }
    if (p.y < 0 || p.y > height) {
      p.vy *= -1;
    }

    /* ③ 点を描画する */
    point(p.x, p.y);
  }
}

5. 点を線で繋ぐ

最後に、2つの点の距離が閾値よりも近い場合に線で結ぶ処理を実装しましょう。この処理を実装するには、まずすべての点群の間の距離を計算する必要があります。以下の二重for文で網羅的に2点を取り出します。

for (let i = 0; i < points.length; i++) {
  for (let j = i + 1; j < points.length; j++) {
    //ループ変数i,j を使い、points内の要素を総当たりで取り出す
  }
}

次にforループのループ変数i, jで取り出した2点間の距離を計算します。p5.jsでは2点の座標を引数に渡すだけで距離を計算できる関数dist()が用意されているのでこれを利用します。

dist(points[i].x, points[i].y, points[j].x, points[j].y);

あとはこの距離が閾値を下回っている場合に線を引きます。線を引く際にはp5.jsの関数line()を使用します。この関数もdist()のように、2点間の距離を引数に渡すとその2点間を直線で結びます。

さて、これで今回の作例のコードが完成しました。実行結果を見てみると独立して動く点群とそれらを結ぶ線が生き物のようにも見えますし、星座のようにも見えますね。点群の座標・速度が乱数によって決まるため、毎回実行結果が変わるのもおもしろいですね。

/* 1. 変数の定義 */
const NUM_POINTS = 100;
const MAX_LINE_DIST = 100;
const points = [];

function setup() {
  /* 2. キャンバスの初期化 */
  createCanvas(640, 640);
  background(0);
  stroke(255, 100);

  /* 3. 点群の初期化 */
  for (let i = 0; i < NUM_POINTS; i++) {
    const point = {
      x: random(width),
      y: random(height),
      vx: random(-1, 1),
      vy: random(-1, 1),
    };
    points.push(point);
  }
}

function draw() {
  background(0);

  /* 4. 点群のアップデート */
  for (const p of points) {
    p.x += p.vx;
    p.y += p.vy;
    if (p.x < 0 || p.x > width) {
      p.vx *= -1;
    }
    if (p.y < 0 || p.y > height) {
      p.vy *= -1;
    }
    point(p.x, p.y);
  }

  /* 5. 点を線で繋ぐ */
  for (let i = 0; i < points.length; i++) {
    for (let j = i + 1; j < points.length; j++) {
      const d = dist(points[i].x, points[i].y, points[j].x, points[j].y);
      if (d < MAX_LINE_DIST) {
        line(points[i].x, points[i].y, points[j].x, points[j].y);
      }
    }
  }
}

作品の発展

作例はここで完成ですが、クリエイティブコーディング作品はパラメーターを少しいじるだけで、まったく別の表情を見せることがあります。たとえば、背景色と線の色の透明度を上げると、モヤがかかったようなエフェクトが得られます。

また、線を引く閾値を小さくして点群の数・速度を大きくすると、より粒立った印象を与えられますね。

このように同様のロジックで実装された作品であっても、パラメーターを少し変えるだけでさまざまなビジュアルを楽しめるのがp5.jsを使ったクリエイティブコーディングの面白みの1つです。

作品の公開とコミュニティ

今回の作例で紹介したのはp5.jsの関数のほんの一部です。p5.js公式のExamplesを見ているだけでもさまざまな表現ができることがわかります。作品を自分でコーディングしたら、ぜひ公開しましょう

OpenProcessing

  • OpenProcessingはp5.jsで作成した作品を公開し共有できるプラットフォームです。リアクションやコメントをもらったり、他の人の作品やコードを参考にしたりできます。

NEORT

  • NEORTはデジタルアートを共有するためのプラットフォームで、p5.jsやThree.js、openFrameworksなどを使った作品から、TouchDesignerを使った作品、動画作品まで、ツールにこだわらない横断的な作品が集まっています。

Processing Community Japan

  • Processingやp5.jsを用いたクリエイティブコーディングの日本コミュニティです。コミュニティのDiscordに参加すれば、作品の共有はもちろん、わからないことがあれば質問したり、展示などのイベント情報を得たりできます。

また、作品の画像や映像をXに #p5.js#creativecoding といったタグをつけて投稿することもオススメです。コミュニティの方やクリエイティブコーディングに関心のある方がリアクションしてくれます。

まとめ

この記事ではp5.jsを使ったクリエイティブコーディング作例を通して、p5.jsの関数の使い方や表現力、作品の共有方法を紹介しました。環境構築不要でウェブエディターを使って手軽に作品を作れるので、ぜひ今からでもクリエイティブコーディングを始めましょう。

今回はp5.jsをメインに紹介しましたが、JavaScriptでライブラリを使わずにクリエイティブコーディングに取り組む方法やジェネラティブアートのアルゴリズム、乱数などについて解説している記事もあります。またTypeScriptを使ったp5.jsの利用や他のライブラリとの併用方法についても紹介しています。ぜひ併せてお楽しみください。

p5.js関連

クリエイティブコーディング関連

ジェネラティブアート・アルゴリズム関連

その他参考になるサイト・書籍

より体系的にp5.jsを学びたい、表現の幅を広げたいという場合は以下のサイトや書籍がオススメです。

SNSでシェアしよう
シェアいただくと、サイト運営の励みになります!
X(旧Twitter)へポスト
はてなブックマークへ投稿
URLをコピー
岡 大貴

インタラクティブデベロッパー。プログラミング学習支援システムに関する研究で博士(工学)を取得。ウェブ技術を用いた表現やインタラクティブコンテンツについて探求中。

この担当の記事一覧