p5.js Web Editor で WebSocket を扱ってみる（wscat でローカル環境にサーバーを準備して試したり、OBS連携を実行）

過去の記事では [OBS ](https://obsproject.com/ja)の外部からの操作や、それを Slack と連携させてみたり、その他 MQTT とともに IoT系の話で使っている WebSocket。

●「websocket user:youtoy」の検索結果 - Qiita
　https://qiita.com/search?q=websocket+user%3Ayoutoy

この WebSocket を p5.js Web Editor 上で使って、何か面白い外部連携的なことができないかな、とふと思ったのがこの記事を書いたきっかけです。
それで、まずは WebSocket を利用するプログラムと、お手軽に試せそうな外部連携を試してみます。
（wscat を使った動作確認と、OBS のシーン切り替えをやってみます）

## WebSocket について
リアルタイム通信でおなじみの WebSocket。
Web系の情報を調べるのにいつもお世話になる MDN のサイトでは、以下のページなどに情報が書かれています。

●WebSocket - Web API | MDN
　https://developer.mozilla.org/ja/docs/Web/API/WebSocket

冒頭に書いた MQTT だと、ブラウザ上で動かすのに [MQTT.js](https://github.com/mqttjs/MQTT.js)等のライブラリが必要ですが、WebSocket であればライブラリ不要で呼び出せます。

ひとまず、シンプルな構成のものを試すため、上記のページのサンプルを見つつ実装をしてみます。

### WebSocket の基本の処理
上記の MDN のページに書かれたサンプルを以下に転載してみます。

```javascript
// WebSocket 接続を作成
const socket = new WebSocket('ws://localhost:8080');

// 接続が開いたときのイベント
socket.addEventListener('open', function (event) {
    socket.send('Hello Server!');
});

// メッセージの待ち受け
socket.addEventListener('message', function (event) {
    console.log('Message from server ', event.data);
});
```

上記の1つ目のブロックは、接続を行うための処理で、ローカルにポート番号 8080 でサーバーを立てている前提になっています。
上記の 2つ目のブロックは、接続が確立された際の処理と、その接続確立時にサーバーへメッセージを送る処理が書かれています。
3つ目のブロックは、サーバーにメッセージが送られたものを受けとる部分の処理です。

#### サーバーの準備（wscat を利用）
WebSocket用のサーバーの準備に関して、もし　 npmコマンドが使える状態であれば、[wscat](https://github.com/websockets/wscat) を利用するのが手軽かもしれません。
今回はこの方法を使って進めます。

その他、以下の記事で扱った OBSの外部からの制御については、WebSocketプラグインを導入してそれを立ち上げれば、OBS のプラグインがローカルにサーバーを立ててくれたりします。

●OBS をスマホや M5GO（M5Stack）から遠隔制御 〜 MQTT や obs-websocket を利用 〜 - Qiita
　https://qiita.com/youtoy/items/23fb0e16f1a4428b5c9b

## wscat を使って軽いお試し
とりあえず上で少し書いていた [wscat](https://github.com/websockets/wscat) を使ってサーバーを立ち上げてみます。

### wscat を使った処理
以下の記事を書いた際には、wscat のインストール＆実行に npx コマンドを使って一時的なパッケージインストールを行っていました。

●WebSocket のサーバー・クライアントをコマンドラインで簡単に実行する（npx と wscat） - Qiita
　https://qiita.com/youtoy/items/72bbd5f6b0893756da36

今回は、「ローカルインストールを用いて、それを npx コマンドで実行する」という別のやり方で進めてみます。


そこで今回は、グローバルインストールで wscat を準備して進めていきます。
まずは、適当なフォルダで以下を実行します。

```bash
$ npm install -g wscat
```

その後、以下のコマンドを使い、ポート番号 8080 を指定してサーバーを立ち上げます。

```bash
$ npx wscat -l 8080
```

以下は Mac のターミナル上で実行した例ですが、「8080ポートで待ち受け中」というメッセージが表示された状態になります。
![サーバーを起動.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/12eaeffc-31f0-4bd3-5aea-8d00208bdd13.jpeg)

###  p5.js Web Editor から WebSocketサーバーへメッセージ送信
それでは、p5.js Web Editor でプログラムを書いて、WebSocketサーバーへ接続してメッセージを送信してみます。

キャンバスや背景色の指定は残しつつ、 ` draw()` の中で処理を行わないので一旦 `noLoop();` を適用しています。
そしてメッセージ送信のトリガーはマウスクリックにして、クリック時にサーバーにメッセージが送られるという実装を加えています。

```javascript
let socket;

function setup() {
  createCanvas(400, 400);
  background(220);
  noLoop();
  socket = new WebSocket("ws://localhost:8080");

  socket.addEventListener("open", function (event) {
    console.log("OK!");
  });
}

function draw() {}

function mousePressed() {
  if (socket) {
    socket.send("Hello Server!");
  }
  return false;
}
```

先ほどコマンドで立ち上げたサーバーはそのままで上記のプログラムを実行し、マウスのクリックイベントを 1回発生させると、以下のようになることが確認できます。
![クライアントからの接続とメッセージ送信を行った状態.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/ff880e01-1e94-5896-a914-b6831b146a2b.jpeg)

上の画像 3行目の出力で、p5.js Web Editor側からの接続が完了できたことと、4行目の出力で `socket.send("Hello Server!");` を 1回実行してみた際（マスクのクリックをしてみた際）の出力がサーバー側に届いていることが確認できました。

###  p5.js Web Editor で WebSocketサーバーからのメッセージを受信する
今度は、p5.js Web Editor がメッセージを受けとる側の処理を試してみます。
そのためには、p5.js Web Editor へメッセージを送る役割のものが必要になりますが、先ほど立ち上げた　wscat の WebSocketサーバーがその役割を担えるので、それで進めます。

MDN のサイトのサンプルを見つつ、p5.js Web Editor 上でメッセージを受信する側のプログラムも実装してみます。

```javascript
let socket;


function setup() {
  createCanvas(400, 400);
  background(220);
  noLoop();

  socket = new WebSocket("ws://localhost:8080");

  socket.addEventListener("open", function (event) {
    console.log("OK!");
  });

  socket.addEventListener("message", function (event) {
    console.log("Message from server ", event.data);
  });
}

function draw() {}

function mousePressed() {
  if (socket) {
    socket.send("Hello Server!");
  }
  return false;
}
```

追加したのは、 `socket.addEventListener("message", function (event) {...}` の部分です。

それでは、WebSocketサーバーが立ち上がった状態で、上記のプログラムも実行しつつ、サーバー側からメッセージを送ってみます。

先ほどの状態で、サーバー側の最後の行が「`>`」と表示された状態になっていますが、そこで「Hello!!」など適当な文字列を入力して実行してみます。
![サーバー側からのメッセージ送信.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/4add918c-699d-5c02-d898-1891c0579f0f.jpeg)

そうすると、サーバーからクライアントへのメッセージ送信が行われます。
p5.js Web Editor側のコンソールを確認してみると、以下のような表示がされているのを確認できました。
![p5.js側のコンソール.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/17336aef-ce6a-754f-0d7a-4ab4d3afe797.jpeg)

サーバー側で入力したメッセージを受信できているのが分かります。

### wscat でのクライアントの実行について補足
今回、p5.js Web Editor へメッセージを送る際、wscat で用意したサーバーを用いました。

wscat には、コマンドでクライアントを準備する仕組みもあります。
それを一時的なインストール（※ npx　によるインストール）やローカルでのインストール（※ 上記のやり方）を用いた場合、以下のコマンドでサーバーに接続した際に「`code: 1006, reason: ""`」というメッセージが出て、クライアントが終了してしまう状態になりました。

```bash
$ npx wscat -c ws://localhost:8080
```

現状、wscat でクライアントを準備したい場合、公式のインストール方法で書かれている以下のグローバルインストールを用いるのが無難なようです。

```bash
$ npm install -g wscat
```

上記でインストールした場合、サーバー・クライアントを実行するコマンドは以下となります。

```bash
# サーバー側
$ wscat -l 8080
# クライアント側
$ wscat -c ws://localhost:8080
```

## OBS と p5.js Web Editor を連携させてみる
あともう 1つお試しをしてみます。
具体的には [OBS](https://obsproject.com/ja) のシーン切り替えを p5.js Web Editor から実行してみます。

### OBS のプラグインを準備する
そのためには、[過去の記事](https://qiita.com/youtoy/items/23fb0e16f1a4428b5c9b)でも扱った以下のプラグインを使いますが、詳細は[過去の記事](https://qiita.com/youtoy/items/23fb0e16f1a4428b5c9b)の説明などをご覧ください。

●obs-websocket - Remote-control OBS Studio from WebSockets | OBS Forums
　https://obsproject.com/forum/resources/obs-websocket-remote-control-obs-studio-from-websockets.466/
●Palakis/obs-websocket: Remote-control OBS Studio through WebSockets
　https://github.com/Palakis/obs-websocket

OBS にプラグインを導入すると、OBS のメニューの「ツール」の中に上記のプラグインに関するメニューが追加されます。
![スクリーンショット_2021-08-14_14_36_46.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/3962c42a-b1fa-016c-b192-5c164cc20547.jpeg)

これを開いて、適宜設定を変更して利用します。
「サーバーポート」は他の別のプログラム等で使われてなければ、デフォルト設定で大丈夫ですが、何らか衝突が起こった際は変更をしておきましょう。
また、「認証を有効にする」の項目は、利用手順が少し複雑になるので今回はチェックを外しています。
![Websocket_サーバー設定.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/15a24370-5855-c8be-6a14-86870f4f2945.jpeg)
そもそも今回は　 OBS をローカルで動作させており、OBS上から動作開始をさせている間だけ動くもので、その PC に外部から接続できる経路がない状態なので、この設定を外しています。
（OBS を第3者が利用可能な、クラウド上で動作させている状況などといった場合は、条件が変わってきますので適宜設定を変更してください）

### OBS のシーンを複数準備する
今回は OBS のシーン切り替えを p5.js Web Editor から行います。

その切り替え対象となる複数のシーンを、OBS上で準備します。
自分は、過去の記事を書いた際に用意したものを流用します。
![OBSのシーン.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/4094e022-9caa-7425-1c2a-a03031996212.jpeg)
ここでの設定に関して、「シーンの名称」は、この後で利用することになります。

### p5.js Web Editor のプログラム
上記の通り OBS上で 3つのシーンを用意しました。
その切り替えは、マウスクリックでなく数字キーの入力で行う形にしてみます。

以下の公式リファレンスの中の「`key`」のほうで判別する処理を書いていきます。
●reference | keyCode
　https://p5js.org/reference/#/p5/keyCode
![keyCode.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/5674276f-7539-f622-33b4-214e03448289.jpeg)

また、シーン切り替えを行う際は JSON を送る必要があります。
以下の記事に書いた、公式の仕様で規定されたシーン切り替え用のフォーマットで送信してみます。
●Node-RED を使って OBS を遠隔操作してみた話（obs-websocket を利用）｜豊田 陽介｜note
　https://note.com/youtoy/n/nf404040a91c0

```json
{
   "request-type":"SetCurrentScene",
   "message-id":"（適当に決めたID）",
   "scene-name":"（OBS上で設定したシーンの名前）"
}
```

プログラムは以下の通りです。
`"（適当に決めたID）"` と `"（OBS上で設定したシーンの名前）"` という部分が 3箇所ありますが、ここは適宜書きかえてご利用ください。

```javascript
let socket;
const sceneJSON = [
  {
    "request-type": "SetCurrentScene",
    "message-id": "（適当に決めたID1）",
    "scene-name": "（OBS上で設定したシーンの名前1）",
  },
  {
    "request-type": "SetCurrentScene",
    "message-id": "（適当に決めたID2）",
    "scene-name": "（OBS上で設定したシーンの名前2）",
  },
  {
    "request-type": "SetCurrentScene",
    "message-id": "（適当に決めたID3）",
    "scene-name": "（OBS上で設定したシーンの名前3）",
  },
];

function setup() {
  createCanvas(400, 400);
  background(220);
  noLoop();

  socket = new WebSocket("ws://localhost:4445");

  socket.addEventListener("open", function (event) {
    console.log("OK!");
  });

  socket.addEventListener("message", function (event) {
    console.log("Message from server ", event.data);
  });
}

function draw() {}

function keyPressed() {
  switch (key) {
    case "1":
      socket.send(JSON.stringify(sceneJSON[0]));
      break;
    case "2":
      socket.send(JSON.stringify(sceneJSON[1]));
      break;
    case "3":
      socket.send(JSON.stringify(sceneJSON[2]));
      break;
    default:
      break;
  }
}
```

OBS を起動してプラグインを実行した状態で、さらに上記を実行して数字キー（1 or 2 or 3）を押すと、OBS上のシーンが切り替わるのを確認できました。

## まとめ
今回、p5.js Web Editor上のプログラムで WebSocket を扱い、wscat で立てたローカルの WebSocket との通信や、プラグインを導入した OBS の操作を行ってみました。

とりあえず今回は過去のリソースなどを利用し、手軽に実行できそうな例でやってましたが、「ガジェット・ハードウェア連携」や「PC と スマホのそれぞれのブラウザ間での連携」などで活用すると面白いことができそうです。

また、リアルタイム通信系の JavaScript実装は、個人的には MQTT をよく使っていたので、以前よく使ったこともある [MQTT.js](https://github.com/mqttjs/MQTT.js) のブラウザ版の処理を用いたものも試せればと思います。

●「mqtt user:youtoy」の検索結果 - Qiita
　https://qiita.com/search?q=mqtt+user%3Ayoutoy

## 【追記】 MQTTバージョンも記事にしました！ ＆ お試し
MQTT を使った場合の実装方法について、記事を書きました！

●p5.js Web Editor で MQTT を扱ってみる（MQTT.js や shiftr.io Desktop を利用） - Qiita
　https://qiita.com/youtoy/items/7c58de69b4b20543a5b5

また、このようなことを MQTTバージョンのほうで試したりしました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">先日、 <a href="https://twitter.com/hashtag/p5js?src=hash&amp;ref_src=twsrc%5Etfw">#p5js</a> のプログラムにリアルタイム通信の仕組み（WebSocket、MQTT）を試しに組み込んだものを活用して、MacBook と iPad の間でのリアルタイム通信を試した！<br><br>MacBook のブラウザと iPad のブラウザの表示領域との間を、動く円が行き来するような表示がされるプログラムを書いてみた。 <a href="https://t.co/DgHpIdBljF">pic.twitter.com/DgHpIdBljF</a></p>&mdash; you (@youtoy) <a href="https://twitter.com/youtoy/status/1428759249291677696?ref_src=twsrc%5Etfw">August 20, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">この前試した、 <a href="https://twitter.com/hashtag/p5js?src=hash&amp;ref_src=twsrc%5Etfw">#p5js</a> とリアルタイム通信（MQTT）の組み合わせで MacBook と iPad のそれぞれの画面間をつないだような処理をやった話の発展形！<br><br>今度は、スマホ 2台の画面をつないだのですが、その2つの画面のつながる方向が、動的に縦方向だったり横方向だったり、変化するような実装にしてみた！ <a href="https://t.co/WmCev0oBfx">pic.twitter.com/WmCev0oBfx</a></p>&mdash; you (@youtoy) <a href="https://twitter.com/youtoy/status/1430905469149663240?ref_src=twsrc%5Etfw">August 26, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>