p5.js の canvas 要素を渡して obniz ディスプレイでアニメーションを表示させるメモ

p5.js の canvas 要素を渡して obniz ディスプレイでアニメーションを表示させるメモです。

背景

「obniz のディスプレイでアニメーションさせてみたいんです」といった相談があって、結構厳しいんじゃないか?と思ってやってみたら、当初の見立て通りでうまくいきました。

ディスプレイ – obniz Docs

こちらにあるとおり、

displayでは1ピクセルずつどう表示するのかをdisplay.raw()により配列で送信します。左上を起点に白黒なら1pixel-1bitで白黒を、カラーなら対応している色深度により1pixelが何bitなのかが異なります。

また、Canvasを読み取りdisplayに描画するdisplay.draw()関数が用意されています。Nodejsの場合でもnode-canvasを利用することで同様な描画が可能です。

ということで obniz.display.draw(ctx); といった形で ctx に値に canvas 要素が渡せればうまくいくようです。

動作環境

  • Windows 10
  • Chrome ブラウザから obniz クラウドで実行

良いヒントがあった

画像ファイルをdisplayする方法を教えて下さい | obniz Developer’s Community Forum

obniz フォーラムで GoogleHomer さんが canvas で JPEG 表示をさせるあたりを質問されていました。分かりやすい。

また、こちらの 3D sphere on Display という公式サンプルもよかったんですが、いかんせん three.js をガッツリ入れるだけに canvas 扱い自体をシンプルに見たいときには苦労したので、参考程度に。でも、きれい。

p5.js でやってみる

Positioning your canvas · processing/p5.js Wiki

obniz に渡したい canvas 要素をどう取得するかの話はこちらにありました。

function setup() {
  var cnv = createCanvas(windowWidth, windowHeight);
  cnv.style('display', 'block');
  background(255, 0, 200);
}

サンプルをみてみると、createCanvas の返り値で受け取ればいけるようです。いいですね!

p5.js – Libraries – cdnjs – The #1 free and open source CDN built to make life easier for developers

CDN 情報はこちらから把握して組み込みました。

ということで ラインが動く公式サンプル をベースに以下のように組み込みました。

<html>

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" />
  <script src="https://obniz.io/js/jquery-3.2.1.min.js"></script>
  <script src="https://unpkg.com/obniz@3.25.0/obniz.js" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.min.js" integrity="sha512-3RlxD1bW34eFKPwj9gUXEWtdSMC59QqIqHnD8O/NoTwSJhgxRizdcFVQhUMFyTp5RwLTDL0Lbcqtl8b7bFAzog==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>

<body>
  <script>

    // https://p5js.org/examples/structure-loop.html
    // ラインがループするサンプルをベースに

    let y = 0;

    let cnv;
    function setup() {
      cnv = createCanvas(128 , 64);
      stroke(255);
      frameRate(2);  // 30fps はカクつく
      loop();
    }
    
    function draw() {
      background(0);
      
      y = y - 10;
      if (y < 0) {
        y = height;
      }
      line(0, y, width, y);
    }


    // 今回の obniz を指示するための設定
    const obniz = new Obniz("OBNIZ_ID_HERE");

    obniz.onconnect = async function () {
      obniz.display.clear();
      obniz.display.print("p5.js");

      console.log(cnv);

      obniz.onloop = async function(){
        // この形で canvas の getContext('2d') 的な値を渡せる
        const ctx = cnv.drawingContext;
        obniz.display.draw(ctx);
      }
      
    }
  </script>
</body>

</html>

わりとシンプルに組み込めましたが、少し解説すると、

cnv = createCanvas(128 , 64);

cnv という値を obniz.onloop と p5.js の setup 関数で共通で使えるようなスコープにして、組み込んでいます。さきほどのフォーラムで obniz Board の場合は 128 * 64 のようなので、そうしています。結果バッチリでした。

      obniz.onloop = async function(){
        // この形で canvas の getContext('2d') 的な値を渡せる
        const ctx = cnv.drawingContext;
        obniz.display.draw(ctx);
      }

obiniz.onloop でループを回しており cnv.drawingContext がこの形で canvas の getContext(‘2d’) 的な値を渡せるようなので、あとは obniz.display.draw(ctx); で obniz に渡しています。

こちらを obniz で動かしてみると、こんな風にうまく動きました!

さすがに元サンプルの 30 fps だと PC 内の描画はサクサク動いても obniz で転送が間に合わなくて処理が詰まったようになるので 5 fps 以下だといい具合に同期しました。 WebSocket ベースで canvas から 128 x 64 な画像データ送ってますからね正直 0.5 秒に 1 回でも変わるんだったら素敵です。