Fringe81のオンライン納会を支える技術 配信編

この記事は[Fringe81 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/fringe81) 25日目の記事です。

メリークリスマス！🎄

今年ももう終わり、アドベントカレンダー最終日の今日12月25日、我々Fringe81では四半期に一度の納会（通称Fringe Fes!!!)を開催しています。

毎回、会社のカルチャーを伝え、社員みんなで考えるきっかけとなるコンテンツを有志社員からなる運営チームが考え、実行するというイベントです。

2019年まではオフィスに社員一同が集い開催してきたFringe Fes!!!も今年はコロナの影響で、オンライン開催を余儀なくされました。

2020年、運営チームはオフィスに集えなくても、一体感を感じられる場作りのためにいろいろな工夫をしながら3回のオンラインFringe Fes!!!を開催してきました。

今回はその数ある工夫の中から、自分が主に担当(というか好き勝手やってただけですが)した配信面での工夫を包み隠さず紹介していこうと思います。この記事をみている皆さんの何かの助けになれば幸いです。

## やったこと

Fringe Fes!!!はZoomでコンテンツを配信しつつ、それを他の社員が自宅などから見るというスタイルです。

オンラインでもオフラインで集まっている時のような一体感を感じるには、反応が見えることが大事だろうという安直な考えで、**配信画面にコメントを載っけるシステム**を作ってみました。

回を重ねるごとにアップデートを繰り返してきましたが、この記事で扱うのは今日12月25日に初お披露目の最新版です(?)

実際にZoomで配信される画面はこんな感じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20371/6d33b66d-b901-6305-9c14-66703e665806.png)

↓静止画だとわかりづらいので動画も用意してみました
https://youtu.be/cVgk0fFOprY

画面左側には視聴者のコメントや、行動指針であるDo/Dive/Deepのスタンプが流れ、(少し見えにくいですが)右側には某ライブ配信風のハートや星がフワフワと表示されるようになっています。

このコメントやハートマークなどは、視聴者側に事前に配布したURLからアクセスできる画面から送信することができます。画面はこんな感じ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20371/a4fa7b9c-00af-19b8-d545-cf21460fc617.png)

こんな感じの、ちょっとテンションがアガる(かもしれない)配信システムを作る話です。

## いざ作る

今回目指すざっくりとした姿は

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20371/839a9bda-5384-3b1d-700f-8b55a9eb099b.png)

こんな感じ。

OBSという配信ソフトを利用することで、PCに接続したカメラの映像に重ねてブラウザで表示しているページをオーバーレイ表示させることが可能なので、これらの機能を使ってうまく実現していく方針としました。

## 技術選定

OBSから参照されるということを除くと至って普通のWebアプリの実装でいけそうという目星はついていたので、普段のプロダクト開発では使ってない物や自分の興味のある物を中心に選びました。その結果選択したのは以下の技術たち。

### Firebase

- これは業務でも使っているけど、早さ(キャッチアップ的な意味も開発速度的な意味も)優先で。
- このシステムではユーザーの識別のためにAuthenticationと、視聴者が送ったコメントやリアクションを保存し、配信側からはリアルタイムにその更新を受け取るためにFirestoreを使っています。
- 無料枠が寛大なので弊社規模であれば無料で収まるはず、というのも嬉しいところ。

### Next.js

- 自分がReact好きであるということ、複数画面を作るがルーティングとかで手間取りたくなかったこと、Authenticationの都合でサーバーサイドAPIを生やさないといけないという事情などが重なって、Next.jsがいいだろうという判断をしました。
- 個人的に好きで、いつか仕事でも使えたらいいなという思いもある。

### Vercel

- CD構築のためにごちゃごちゃしなくても、GitHubとの連携を設定すれば一発デプロイ。時間がなかったのでこれで。
- めっちゃ楽。。。

### tailwind.css

[https://tailwindcss.com/](https://tailwindcss.com/)

- これはキャッチアップ目的。そんなに素早く作れるのか？というのを検証してみたかった。
- 結論、いい感じにハマったのでこれも業務で使ってみたいですね。

といった面々。

## つくった

今回は前述の技術等を組み合わせて以下の4つの画面を作りました。

### 1. 登録画面

これは大した工夫もない普通の画面ですが、画面上に表示するための名前を登録してもらう画面を作りました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20371/3984cba2-47d2-4b90-8534-420f9612ffdc.png)

名前を入力してもらって、入力された値はFirebase Authenticationのuidと紐づける形でFirestoreに保存しています。

ちなみにこのおしゃれなサンタさんのイラストは[Loose Drawing](https://loosedrawing.com/)というサイトのものを利用させていただきました。商用利用も可のおしゃれなフリー素材があるお気に入りのサイトです。

この画面をみたデザイナーさんにどのサイトを使ってるか聞かれたくらいなので、おしゃれなんだと思います(?)

### 2. コメント画面

このアプリのメイン画面とも言える画面。

コメントはもちろん、行動指針のスタンプを使ったり、ハートや星のリアクションをつけることもできるようにしてみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20371/905c21b6-c3ee-3072-467a-96eacecf48ea.png)

やっていることはシンプルで、

- コメントやリアクションが送信されたらFirestoreに保存
- コメントが保存されるcollectionをsubscribeしておいて、新しいコメントがきたら画面に反映する

という感じです。

SlackやLINEのような下方向に伸びるスクロール領域を作って、投稿などをしたタイミングでスクロール位置を制御したりというのが地味に難しくコードが汚くなってしまったという個人的な反省を抱えている箇所だったりします。

## 3. 配信コメント画面

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20371/7019ecf6-955d-2da9-c1dd-b33d3c563952.png)

こんな感じで先ほど画面にオーバーレイされていたものを普通の画面を作るときと同様に作ります。

やっていることとしてはシンプルで、視聴者側の画面から投稿されたコメントをsubscribeし、新しい投稿がきたら画面上に表示しています。

## 4. リアクション画面

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20371/e15122cc-5d1c-f63f-954f-6fb421adc549.png)

静止画だとなんのこっちゃさっぱりな感じですが、コメント表示画面と同様に、視聴者側で押されたリアクションをsubscribeしておいて、新しいリアクションがきたら画面したからアニメーションで表示させています。

なんのことはない、普通のCSSアニメーションです。

これらが完成したらOBS上で配信したいコンテンツと重ねてあげるという感じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20371/8477b8a3-6307-0779-48d3-9d2208665092.png)

OBSでは、Photoshopのレイヤーなどと同じような仕組みで複数のソースを重ねることができます。ソースには、カメラからの映像や静止画・動画に加えて、今回使用しているブラウザソースもあり、指定したURLを配信に載っけることができます。

その仕組みを使って配信画面の上にコメントをオーバーレイ表示させています。

全貌はこんな感じです。

基本的にやっていることはただのフロントエンド開発でしかないのですが、配信に載せるという特殊用途!?のシステムだからこその実装上での工夫や注意点を紹介して終わろうと思います。

## 実装の工夫や注意点

### OBSの設定について

OBSのブラウザソースを使って何も考えずに作った画面を重ねたら、文字がやたらと小さくなってしまうという問題が起こりました。

どうやら、html要素のフォントサイズがデフォルトで小さくなってしまっているのが原因のようでした。(今回Tailwind.cssを使ったので全てのフォントサイズ指定がremになっていたためhtml要素のフォントサイズが小さいと全てが小さくなってしまう)

自分で

```css
html {font-size: 16px}
```

と当ててあげることでブラウザ上でみていたものと同じ大きさにすることができました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20371/e30ca3a1-33da-51f8-cfe2-8f62bfea59db.png)

### ブラウザソース内で認証を突破する

ただのコメントかつ、イベントの時だけしか使わないアプリケーションなのでそこまでしなくても、と思いつつ一応Firebase Authenticationの認証を通った相手にだけFirestoreの各種データにアクセスできるような最低限のセキュリティルールを書いていました。

ところが、OBSのブラウザソース内で動いてるWebアプリケーションのinputに何かを入力したり、といったことはできない(多分)ので、ブラウザソース内で動くアプリケーションが認証を突破できず、コメントやリアクションをサブクスライブできないという問題に直面しました。

そこで、この画面に関しては事前にパスワードを共有しておき、クエリパラメータで渡すことで認証をスキップするような仕組みを作りました。

実装としては、クエリパラメータに渡された値をNext.jsのAPIルート機能で立てた専用のAPIに渡し、事前に決めておいたパスワードと一致した場合はFirebaseのAdminSDKの `createCustomToken`メソッドを使い、トークンを発行、フロントエンドへ返却し、ログイン処理をするといった感じで処理しています。

もちろんクエリパラメータに秘匿な情報を持たせるのはNG行為ですが、今回URLしか外部から値を渡す手段がなかったため諦めることにしました。

カスタムトークンを使ったログイン自体はこちらの記事で大体理解できると思います。

[カスタム トークンを作成する | Firebase](https://firebase.google.com/docs/auth/admin/create-custom-tokens?hl=ja)

## おわりに

ちょこちょこと改善を重ねてきたコメントシステム、いかがだったでしょうか。

コロナはまだまだ予断を許さない状況が続きそうな気配を感じるので、もしよろしければ社内イベントやオンライン勉強会などに役立てていただけると幸いです。

それではみなさま、良いお年を！:wave: