爆音と共振でワイングラスを割るサイトを作りました

## [これ](https://hakai.yasaidev.work/)は何？

音でグラスを壊したいという破壊衝動に駆られたため，Vue.jsの勉強も兼ねて初めて作ったサイト[「爆音グラス破壊」](https://hakai.yasaidev.work/)です．

グラスを弾いた音から固有周波数を分析し，その周波数のサイン波を再生してグラスを激しく共振させ破壊します．

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">爆音でワイングラスを破壊できるサイトを作りました（）<br>スマホの音でもそこそこ震えます．<br>爆音をお持ちの方はワイングラス破壊してみてください<a href="https://t.co/75E7k85l8i">https://t.co/75E7k85l8i</a> <a href="https://t.co/JQLAFQUnHv">pic.twitter.com/JQLAFQUnHv</a></p>&mdash; 電気野菜 (@YasaiDev) <a href="https://twitter.com/YasaiDev/status/1347369935618629636?ref_src=twsrc%5Etfw">January 8, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

現状の手元環境では破壊できるだけの爆音がないので，悶々しています...助けて

## 使い方
<https://hakai.yasaidev.work/usage>
### 準備

- スマホもしくはパソコン
- 割れやすそうなワイングラス
- いい感じの長さのストロー
- できれば爆音スピーカー
(最悪スマホスピーカーでもOK)

### 破壊

- Analysisボタンを押す
  (マイクの確認ダイアログがでたら許可)
- もう一度Analysisボタンを押す
- カウントダウン後グラスをはじいて音を出す
- サイトが音を自動分析
- Playボタンは破壊音を再生！

## 破壊の仕組み

はじめにマイクからグラスを弾いた音をFFTして，周波数のピークをとり固有周波数として求めます．

次に，その固有周波数のサイン波を生成し再生します．

結果，グラスは共振し激しく揺れて限界を超えた際破壊されてしまいます．

## Tech

- JS framework: Vue.js
- Design Framework: Vuetify
- オーディオ/描画周り: p5.js

## 開発周りの工夫点

### Vercelの自動デプロイを導入

mainブランチにpushするたびに自動でvercelにデプロイするようにしました．といってもほとんどvercel君がやってくれたので30分で導入できたのは神です．

ちなみに，Vercel bot君はプルリクエストに対しても勝手に検証用デプロイまで行ってくれて感動しました．（神では？）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204520/4a3b65c0-a627-276a-77c6-589760b7b59d.png)




### Google Analyticsの導入

興味半分で[`vue-gtag`](https://www.npmjs.com/package/vue-gtag)で導入しました．クリックなどのイベントを事細かにGA側に送信することが可能といった知見を得れて便利だなと感じた一方，実際のサイトでもこれだけ事細かに情報が収集されているのかと感じ少し恐ろしくなりました．

### かっこいい？OGPの導入

かっこいいね！

クソアプリなのにSNS映えを意識したそうです．今後のアプリ開発等にも役立つ知見を得れたのでヨシです．

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">私のグラスは1054.7Hzで破壊できそう😎<br> <a href="https://twitter.com/hashtag/%E7%88%86%E9%9F%B3%E3%82%B0%E3%83%A9%E3%82%B9%E7%A0%B4%E5%A3%8A?src=hash&amp;ref_src=twsrc%5Etfw">#爆音グラス破壊</a> <a href="https://t.co/75E7k8mVZQ">https://t.co/75E7k8mVZQ</a></p>&mdash; 電気野菜 (@YasaiDev) <a href="https://twitter.com/YasaiDev/status/1347220288237260800?ref_src=twsrc%5Etfw">January 7, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

- 参考: [Twitter Doc](https://developer.twitter.com/ja/docs/tweets/optimize-with-cards/guides/getting-started)

### P5.jsとVue.jsの組み合わせ

ここが一番コーディングで苦労しました．多少知見がたまったのでそのうち記事にします．

[P5.js](https://p5js.org/)が何かというと，クリエイティブ向けコーディング（Ex.オーディオビジュアライザー，ゲームなど）に特化したライブラリです．これを活用したことで，Web Audio周りに関して抽象的に記述できたり，Audioと合わせた描画等をまとめて処理を行うことができました．

- 参考: [Qiita: Vue.jsでp5.jsを使ってみる](https://qiita.com/mitsuya_bauhaus/items/b6f3d1aec07a9e07bb3a)
- 参考: [HIM.CO ヒム・カンパニー – なぜベストを尽くさないのか](https://himco.jp/2020/01/04/3%EF%BC%9A%E3%82%B5%E3%82%A6%E3%83%B3%E3%83%89%E3%81%AE%E5%86%8D%E7%94%9F-p5-sound-js-%E3%82%B5%E3%82%A6%E3%83%B3%E3%83%89/)

### ページの軽量化に取り組んだ

当初はNetlifyにデプロイし，速さを求めVercelにデプロイした．

また，[PageSpeed Insights](https://developers.google.com/speed/pagespeed/insights/?hl=ja)を使って原因が適切なmin.jsを使わずに生のjsを使っていたことに気づき修正しました．
（これが主たる低速度の原因，もしかしたらNetlifyは悪くなかったかも知れないです）

さらに，サイズの大きいcomponentを非同期ロードすることで見かけの表示速度を早めました．

参考: [非同期コンポーネント:ロード状態のハンドリング](https://jp.vuejs.org/v2/guide/components-dynamic-async.html#%E3%83%AD%E3%83%BC%E3%83%89%E7%8A%B6%E6%85%8B%E3%81%AE%E3%83%8F%E3%83%B3%E3%83%89%E3%83%AA%E3%83%B3%E3%82%B0)

```js
const AsyncComponent = () => ({
  // ロードすべきコンポーネント (Promiseであるべき)
  component: import('./MyComponent.vue'),
  // 非同期コンポーネントのロード中に使うコンポーネント
  loading: LoadingComponent,
  // ロード失敗時に使うコンポーネント
  error: ErrorComponent,
  // loading コンポーネントが表示されるまでの遅延時間。 デフォルト: 200ms
  delay: 200,
  // timeout が設定され経過すると、error コンポーネントが表示されます。
  // デフォルト: Infinity
  timeout: 3000
})
```

結果かなり当初よりは早くなりました．しかし，モバイル向けのスコアが低いのでもっと早めたいです.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204520/f5b23183-e660-df24-5bbf-4fc8e47ebb48.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204520/f4e51dac-240a-54c8-d93d-6ff057f3ea48.png)


## やったけど取り消した点とか反省点

### Github pagesにVue RouterのSPAサイトをデプロイした

当初はGithub Pagesに自動デプロイするようなworkflowを書いて動作させようとしましたが,Vue RouterのSPAのデフォルト設定とGithub pagesは少し相性が悪い．初学者でデプロイにハマりたくなかったのでnetlifyへと移行することを決心しました．

おそらくvueやreactなどはnetlifyやvercelのほうが脳死でデプロイできるので楽かもです．

- 参考: [Qiita: vue-routerのgithub-pages用設定](https://qiita.com/uriuriuriu/items/dc5ece0189c518c2e046)

### Netlifyへデプロイした

Github workflowでGithub側でビルドしてNetlifyに[デプロイする物](https://gist.github.com/older4/f9326c0e3fe53ec07158479a5c9d0453)を書きました．

参考: [Qiita: GitHub Actions による GitHub Pages への自動デプロイ](https://qiita.com/peaceiris/items/d401f2e5724fdcb0759d)

こうすることでNetlify側のビルド時間制限を無視して好きなだけデプロイすることができるようにしました．

しかし，アクセスが遅い気がしたので，Vercelに移行しました．
> (Pagespeed insightがモバイルで10点台)

これは後述するがp5.jsのminバージョンを使っていなかったことが大きいと思われます．このことに気づいていなかったです．

### min.js を使わっていなかった

Vercelに移行後もやはりやけにページの読み込みが遅いと思ってGoogleの[PageSpeed Insights](https://developers.google.com/speed/pagespeed/insights/?hl=ja)を使って確認したところP5.jsが大変**ドデカサイズ**でありました(圧縮前5MB，説明不要でかい)．

min.jsに切り替えたら600KiBぐらいになったのでサクサクになりました．

正直`npm run build`したら勝手にmin.jsに切りかわるなどしてファイルサイズの最適化がされると思いこんでいました．反省．

### Githubのブランチ名がmasterからmainになったことを忘れていた

[GitHub Actions による GitHub Pages への自動デプロイ](https://qiita.com/peaceiris/items/d401f2e5724fdcb0759d)を参考にworkflowを書いていたのですが，何故かpushしてもactionが動かないことがありめちゃくちゃ悩みました．

そうです，元記事ではactionのトリガーのブランチ名がmasterだったのです...泣きました．

みなさんもブランチ名が絡むもののコピペには気をつけてください．世界は変わってしまいました．

## もろもろの所感

手元の音響設備ではワイングラスを破壊するほどの音量が確保できなかったのがすごく残念です．爆音をお持ちの方はグラスワインを破壊して僕に自慢してほしいです．

あと，普段はPython書いているのですがJavascript書いていると文法がごっちゃになって最近はSyntax Errorの量産機になりました．悲しいです．