世界を動かす技術を、日本語で。

数行で任意のサイトをマルチプレイヤー化。サーバーレスWebRTCマッチメイキング

概要

Trystero を使ったリアルタイム通信の仕組みを解説 マウス操作やクリック の同期方法を具体的に紹介 複数プロトコル (BitTorrent, Nostr, MQTT, IPFS, Supabase, Firebase)対応 コード例で ルーム参加・イベント送受信 の流れを説明 拡張機能(音声・映像配信やバイナリデータ送信)の案内

Trysteroによるリアルタイム同期の仕組み

  • Trystero はWebページ上で ユーザー同士をリアルタイム接続 するためのライブラリ
  • マウスの動きクリック操作 を他のユーザーと 同期 可能
  • P2P接続 をBitTorrent、Nostr、MQTT、IPFS、Supabase、Firebaseなど多様な プロトコル で実現
  • ページを開いているユーザー同士が 直接接続 し、 即時反映 を体験
  • 他の端末やタブで同じURLを開くことで、 複数人接続の挙動 を簡単に確認可能

ルームへの参加方法と基本コード

  • ルーム参加には appIdroomId を指定
  • 例:
    • import {joinRoom} from 'trystero'
    • const room = joinRoom({appId: 'trystero-lounge'}, '101')
  • joinRoom関数 でルームを作成・参加し、 通信の基盤 を構築

ピアの参加・退出イベントの監視

  • room.onPeerJoin で新規参加者の検知
  • room.onPeerLeave で退出者の検知
  • 例:
    • room.onPeerJoin(addCursor)
    • room.onPeerLeave(removeCursor)
  • 参加・退出時に UI更新カーソル追加・削除 などの処理が可能

アクションの送受信設定

  • makeAction でカスタムイベント(例:mouseMove, click)を定義
  • 例:
    • const [sendMove, getMove] = room.makeAction('mouseMove')
    • const [sendClick, getClick] = room.makeAction('click')
  • sendMovesendClick でイベントを送信
  • getMovegetClick で受信イベントに応答

イベントのブロードキャストと受信

  • window.addEventListener でマウス移動やクリックを検知し、 sendMove/sendClick で送信
    • 例:
      • window.addEventListener('mousemove', e => sendMove([e.clientX, e.clientY]))
      • window.addEventListener('click', () => sendClick(randomFruit()))
  • getMove で他ユーザーのマウス座標を受信し、 setCursorPosition でUI反映
  • getClick でクリックイベントを受信し、 dropFruitFrom で処理

拡張機能とドキュメント案内

  • Trysteroは 音声・映像ストリームファイル送信 など 多用途対応
  • 詳細や応用例は README で確認可能
  • GitHub: dmotz/trystero でリファレンス・サンプルコードを提供

Hackerたちの意見

自分のライブラリが何をするのかを示す素晴らしい方法だね。それに、そのライブラリ自体もいいコンセプトだし、最近まさにこういうのを探してたんだ。

わあ、これすごい!大好きだけど、ちょっと気になるのは、各接続をピアツーピアでやるのってどれくらいスケーラブルなの?そうなると、ルームに入れたい人全員のためにストリーム接続を開いておかなきゃいけないってこと?

あんまりスケーラブルじゃないよ。WebRTCの通常のルールが適用されるから、ユーザー数が増えるとSFUアプローチを使わなきゃいけなくなる。

どれくらいスケーラブルなのか そのサイトがDOMException: 'RTCPeerConnection'の構築に失敗したってエラーコンソールをスパムしてるのを考えると、あんまりスケーラブルじゃないと思うよ。

各接続をピアツーピアでやるのはどれくらいスケーラブルなの? WebRTCのビデオ通話について大まかに説明できるよ。5人のピアツーピアWebRTCビデオ通話に参加していて、4つのビデオストリームを受信する場合、同時に4つのビデオストリームを送信する必要があるんだ。これはある意味でスケーラブルだよね。アップリンクとダウンリンクの要件が同じだから。ただ、100人のミーティングにいるときに、アプリのロジックが帯域幅を節約するために95人のビデオを隠していると問題が出てくる。そうなると、4つのビデオストリームを受信するだけで、99のストリームを送信しなきゃいけない。実際には、WebRTCのビデオ通話はよく「SFU」または「Selective Forwarding Unit」を使っていて、1つのビデオストリームをベンダーのクラウドサーバーに送信して、他の参加者に転送してもらうんだ。これにより、非対称接続の人や、アップロードがバッテリーを消耗するモバイルユーザー、WebRTCのNATトラバーサルがうまく機能しない厳しいファイアウォールの背後にいるユーザーにもメリットがあるよ。

数年前にこれを試したとき、真のNxNルームはPCで約8人、モバイルで4人に制限されてた。ボトルネックはビデオのエンコード/デコードなんだ。大きなルームでは、すべての受信者にビデオをルーティングするためにサーバーが必要で、これをSFUって呼ぶ。SFUを使えば数百人の参加者がいることも可能だけど、全員が同時に話したり見えたりするわけじゃない。音声だけなら無限大だよ。以前、音声ベースのソーシャルメディアに関わってたけど、ここでもSFUが必要で、いくつかのミキシング機能を追加して、複数の音声ストリームを1つの出力にミックスしてた。めっちゃ楽しかったし、スケーラブルだったよ。

これはサーバーレスじゃないよ。SDPシグナリングのために他の誰かのサーバーを使ってるだけ。実際のアプリでは、TURNサーバーやSFUサーバーも必要になる可能性が高いし。シグナリングのための本当のサーバーレスアプローチもあるけど、例えばお互いのQRコードをスキャンするやつとか、そういうのは明らかに使い道が限られてる。

本当のP2P WebRTCとQRコードにはずっと夢中だったけど、少なくとも昔は、Firefoxがすごく短いタイムアウト(確か5秒くらい)でオファーに失敗しちゃって、バンド外シグナリングが完全に不可能だったんだよね。

その通りだね!サーバーレスって面白い言葉だよね。クラウド企業は、サーバーを自分でプロビジョニングしたり管理したりしなくていいって意味で使ってるけど、技術的にはまだまだサーバーがある状態なんだ。P2P接続を有効にするために、クラウドプロバイダーと何も設定しなくてもいいのは便利だね。

今のサーバーレスは「あなたのインフラにはサーバーがない」って意味だよ。

QRコードの解決策については知らなかったけど、どうやって機能するの?

サーバーレススタックはサーバー料金が一番高いって言われてるよね。

サーバーレスは他の誰かのネットワークをシグナリングに使うんだよね。

最近PeerJS(https://peerjs.com/)を探ってるんだけど、似たようなコンセプトだよね。PeerJSにはない何かを提供してくれるのかな?

私がTrysteroの作者なんだけど、両方のライブラリは同じ目標を達成しようとしてるけど、APIは全然違うよ。一つの大きな違いは、PeerJSはデフォルトでマッチメイキングに単一の中央サーバーを使うのに対して、Trysteroはさまざまなシグナリング接続を並行して使うことで、柔軟性と冗長性を提供してるんだ。

サイトには「今はこのページを開いているのはあなた一人だけだけど、他の人と一緒にどうなるかを見るために、別のタブでこのURLを開くことができるよ」って書いてある。UbuntuでFirefoxとChromeを使ってるんだけど、2つ目のタブを試したり、FirefoxとChromeで開くと、コンピュータのネットワーク接続がロックアップしちゃう(ピングが止まる)。タブを閉じないといけなくて、その後google.comへのピングが30秒で戻ってきて、徐々に通常に戻る。どうやらhttp://playground.nostrcheck.me/relayが問題みたい。

https://github.com/dmotz/trystero これは元のソースコードへの直接リンクだよ。

俺がカーソルを追いかけ回してた人へ、なんでそんなことしたのか分からないけど、ごめんね。

おお、すごい!俺も昔、firebaseを仲介に使ってサーバーレスのゲームを作ったことがあるよ。https://github.com/amdson/rtcpvp / https://amdson.github.io/rtcpvp。これがあればいいなって思ってたし、ICE p2pの設定は本当に面倒だったからね。

これやばい、めっちゃ好き!一般的に言われてる「サーバーレス」よりもずっと「サーバーレス」だね!