推しアイデア
インターネットもペアリングも使わない、ブロードキャストで1文字ずつ流す独自文字コードのガラパゴス的秘密通信! とそれに乗る世界観を伝えるゲーム的表現!
インターネットもペアリングも使わない、ブロードキャストで1文字ずつ流す独自文字コードのガラパゴス的秘密通信! とそれに乗る世界観を伝えるゲーム的表現!
ローカルで特殊仕様な通信が、したい。 ワクワクする技術の魅せ方、したい。
BLE通信の本来のBody領域が封じられてしまったため、ID部分の狭い領域を工夫してやりくりすることで通信を実現しています。 また、新たなるアーキテクチャに対して適切な設計を行なったほか、音と同期する絵作りを頑張りました。





通常のBluetooth通信であれば、Webブラウザ上でも利用可能なAPIが存在するそうなので、例としてJoyconのようなHIDデバイスを接続して制御することは可能なようです。
しかし、今回使ってみたいBLE (Bluetooth Low Energy)はある程度低レベルのAPIでないとアクセスすることが出来ず、ネイティブアプリを作ることがほぼ必然となります。
ネイティブアプリをこの短期間で開発するのはコストが高いので、Web系で開発ができる + Node.jsでnoble/bleno等のBLE関連のライブラリを使用できることが判明したためElectronを採用しています。
ElectronということでメジャーなのはReact + Node.jsだと思います。 しかし、当初の予定では3DS, Wiiのような低解像度感を出すためにThree.js, Phaser等のライブラリを使用しようと考えていましたが、リサーチ途中で見つかったWebGLレンダラーの1つであるPixiJSの公式サイトにて、公式のAgent Skillsの導入案内があったのが非常に好感で採用しました。
後述します。
まず、Bluetoothにはモードが存在します。 コトハコビで使用しているのは「BLE (Bluetooth Low Energy)」と呼ばれるものです。
以下のような特徴を持ちます。
BLEは比較的新しめの規格であり、普及はかなり進んでいるものの、性質故にデータ通信の帯域幅の狭さが目立つため、メインの通信方法として採用される事例は多くはないと考えられます。
そして、今回はこのBLEのブロードキャストモードを使用しています。
ちなみに、BLEはバージョンによりますが数十メートルまで通信することも可能なようで、実際に2階のハッカソン会場から1階エントランスでもパケットの受け取りが可能なことは確認しました。
コネクションは所謂ペアリングのように、相手を認識した上で相互に情報伝達を行いますが、ブロードキャストでは一方的に電波を発信する、ラジオに近い動きをします。
Airtag等のビーコン的な動作をするものもこういった通信を採用していると思います。

上記の画像のような、自身の識別情報をたくさん含んだパケットを発信しているのがブロードキャストです。
本来は、ペアリング前のBluetoothデバイスが、自分を見つけてもらうためのビーコン発信をするために使用されることが多く、基本的には任意のデータ通信を行うものではありません。
ですが、LocalName, Adv.service UUIDsと記載されている項目はある程度自由にデータを変更することが可能です。(今回の開発環境の場合、デバイス依存度合いが高すぎるため要検証)
この自由にデータを格納させられるService UUIDsの部分にコトハコビで発信する文章のうち、1文字を詰め込む構成にしています。
Bluetoothの通信規格上、ば
そう、Topazを見ているそこのあなた、ちょっと思ったでしょう?
懐かしいなぁって。
それを狙ってデザイン設計をしています。
"昔"あった、PlayStation向けゲームソフト「ビブリボン」やNHKのピタゴラスイッチに登場していた「フレーミー」などがモチーフになっています。
色々なポジティブな気持ちが湧き出てくるUIを目指して作りました。
ちょっとしたこだわりが詰まったUIって良いですよね。
アプリ起動中は常に一定のリズムを刻み続けています。リズムに合わせ、機械類はノリノリで動いてくれます。ヒトも機械もハコもハッピー。
どうも、インフラエンジニアです。今回インフラないです。

今回私は通信規格、データ永続化、CIの整備を行いました。
type BleStatus = "IDLE" | "ADVERTISE" | "SCANNING" type BleResult = { ok: boolean, error?: string } // ステータスを排他更新する。遷移ごとに今やってる動作を止めてから移る。 // ADVERTISE にしても、最初の advertise() を呼ぶまで電波は何も撒かない。 setStatus(status: BleStatus): Promise<BleResult> // 撒く生データ(128bit UUID hex の配列)を差し替える。ADVERTISE 中だけ有効。 advertise(serviceUuids: string[]): Promise<BleResult> // HAKO パケットを拾ったら呼ばれる。生の serviceUuids をそのまま渡す。 // 戻り値の関数を呼ぶと購読解除。 onPacket(callback: (serviceUuids: string[]) => void): () => void
超シンプル・ジェネリック。ライブラリのライフサイクル管理と、BLE送受信のLocalNameの管理だけを行い、UUID以降の処理はレンダラに一任する。という内容ですね。

このようなシンプルなMock画面とターミナルのログの形式で提供しました。シンプルで扱いやすく、なかなか良い形でデバック環境を提供できた気がします。
今回はIndexedDBに真剣に向き合い、最大限のパフォーマンスを持ってデータを扱いました。アプリ側が使用しているAPIは次です。
// created_at の新しい順に最大 limit 件。 getRecent(limit = 50): Promise<SessionRecord[]> // 文字が来るたび呼ぶ。初回は draft を作って created_at を打ち、以降は text を追記して育てる。 push(session_id: string, text: string): void // 全 draft を即確定フラッシュ(画面遷移用)。書き込み完了まで await できる。 flushAll(): Promise<void>
デバック用にClearや50件追加などの関数とMockUIを整備していたり実際はもう少し複雑ですが、複雑なQueryはないため、素のIndexedDBに関数を被せるだ気で十分なパフォーマンスと必要十分なAPI提供ができてるかと思います。
ただしこのアプリ、1文字ずつ文字が送られてくるため最悪100のセッションが2秒ごとに書き込みを要求してくるかもしれません。
ということでIndexDBのTransactionを活用し、値が最後に更新されてからコミット(flush)されるまでの時間を8秒、最後にコミットされてからのコミットされるまでの時間を30秒と定義し、負荷を軽減しました。
折角のElectronということで配布を見据え、ビルドを試してみました。
手でビルドして共有もだるいし、Releaseで配る形のBuild CIをAIくんにお願いしました。まるでインフラエンジニアだ〜
なんか気づいたらWindowsビルドも組んであって、いくつかバグフィックスしたら普通にWindowsビルドに成功していました。ウケますね。

Windowsビルドも試してみたところ、送信はダメでしたが受信だけは動くことが発覚しました。 ライブラリ自体を信用できてなかったので、見る側だけでも結構行幸だな。という感じです。
ということでひとまず目的は達していたのですが、せっかくだし真面目にやってみたら?と相方(本業)から言われます。しょうがないにゃ〜
自分はActionsやCIは2回ぐらい真面目に触ったことがあるのですが、それ以外はコピペ・AI・GUIだけでやってきました。

久しぶりにやるかーということで色々AIくんに相談しつつ、pnpm/electronのキャッシュから引っ張ってくる、pythonのインストールを破棄するという偉業に成功しました。やったね! その後、ビルドだけではなくチェックのCIも実装!俺の勝ち!
動かんのだな〜そもそもビルドが思うように通らなかったり、Vite+とpnpm(とElectron)というなかなか新しい構成で、pnpmのバージョンをどこから引っ張ってくるのか論争などで四苦八苦してました。

ここずっとCIのコミットです。
それでも自分なりに最適な形に仕上げれたかと思います。docsとdiscordで仕様が分岐するということで、docsは全て破棄するという選択を取ったのですが、prのコメントなどに書き込むという脱法を行なっているので、興味がある方はそちらをご覧ください。
https://github.com/henohenon/hackz-allo-cup/pull/21 https://github.com/henohenon/hackz-allo-cup/pull/25 https://github.com/henohenon/hackz-allo-cup/pull/26
技術の逆転というのも裏テーマとして開発を行いました。普段Unityなどフロントエンドをする自分、へのへのんはユーティリティ/CIなどバックエンド的なことを行っていました。
へのへのんさんの感想としては、手取り足取りだったけど成し遂げられてよかった。そこそこ楽しかったですが、進んでやりたくはない、全てのインフラエンジニアに感謝とのことです。
また今回初対面ということもあってか、共通認識が1日目の段階ではうまくできておらず、半日はそこのすり合わせと再構築をしていました。最初普通に気まずかったです。ありがとう。ごめん。
普段はインフラ・バックエンドを触っていますが、今回は珍しくレンダラー側(フロントエンド)の見た目の部分をメインに触りました。
ユーティリティ側のバックエンド的なロジック部分はある程度設計を担当しましたが、いつもと違う技術スタックで工程の組み立てミス、リサーチ不足等まだまだ力不足だなと感じる場面が多かったです。
最終的には動く形かつアプリとしてまとまりがある状態になったので、色々な学びがありつつ、アプリがちゃんと完成してよかったなと感じています。
今後Electronを採用して開発する機会があれば、完全なマルチプラットフォーム開発をしてみたいなと感じました。
環境依存度合いが大きすぎることが原因でした。
BLEのブロードキャストは通常のBluetooth接続とは違い、本来はアプリケーションではなくハードウェアそのものが制御する領域であり、イヤホン・スマートウォッチ等のペアリングを行うビーコンとして使われることが多いはずです。
よって、BLEのアドバタイズ等の要求を出したとしても、それをどういう順番で動かすのかを決めるのはOS、ドライバの役割であり、アプリケーションソフトウェア側ではこれ以上抵抗することはできそうになかったです。
また、ライブラリの対応状況も良くはなく、今回使用したライブラリもオリジナルからフォークされたものでした。ライブラリ自体も整備は進んでいないと考えられ、どのような環境・デバイスであれば想定している機能を正常に利用できるかどうかの検証までは時間を割くことができませんでした。
macOSでは上手く動くけど、Windowsでは動かないみたいな、やはりマルチプラットフォーム開発は難しいんだなと感じました。