スマホだけでVJができるブラウザアプリ「VJam」を作った
ブラウザで動くVJ(ビデオジョッキー)アプリ「VJam」を個人開発しました。

VJamとは
スマホのマイクで音楽を拾い、BPMを自動検出し、180種類以上のビジュアルプリセットがビートに同期して動くアプリです。
- インストール不要(ブラウザでURLを開くだけ)
- タッチジェスチャーだけで操作
- HDMIアダプタでプロジェクターに繋げばそのままVJができる
なぜ作ったか
プロ向けのVJソフト(Resolume Arena €399、VDMX $199)は高額で、ノートPCも必要です。パーティーや小さなイベントで気軽に映像を出したいだけなのに、大掛かりなセットアップは必要ない。スマホ1台で完結するものが欲しいと思いました。
探してみると、そういうものが見つからなかったので自分で作ることにしました。
技術スタック
| 項目 | 技術 |
|---|---|
| フロントエンド | Vanilla JS(フレームワーク・ビルドツールなし) |
| 描画 | p5.js(Canvas 2D + WEBGL mode) |
| 音声分析 | Web Audio API(AnalyserNode + FFT) |
| PWA | Service Worker(オフライン対応) |
| ホスティング | Vercel(静的ファイルのみ) |
| 決済 | Stripe Payment Link + HMACライセンスキー |
サーバーサイドのコードは決済検証用のVercel Function(2ファイル)だけです。データベースは使っていません。
ビート検出の仕組み
VJアプリの肝はビート検出です。Web Audio APIのAnalyserNodeでFFTデータを取得し、3つの方法でビートを判定しています。
1. RMSスパイク
音量(RMS)の急激な変化を検出します。直近のRMS平均に対して1.3倍以上のスパイクがあればビートとします。
2. スペクトラルフラックス
FFTの周波数構成がどれだけ変化したかを計算します。音量が一定でもキックやスネアが入ると周波数構成が変わるため、クラブのような大音量環境でもビートを拾えます。
3. バスオンセット
低音域(bass帯域)のエネルギー変化を見ます。キックドラムに特に効果的です。
この3つのうちどれか1つでも反応すればビートとして扱います。
モバイルでの60fps維持
スマホで安定して60fps出すために、いくつかの最適化を行いました。
pixelDensity(1)の強制
p5.jsはRetinaディスプレイだとピクセル密度が2になり、描画負荷が4倍になります。全180プリセットにpixelDensity(1)を設定しました。
loadPixelsの廃止
フル解像度のピクセル操作はモバイルでは重すぎるため、ellipseやrectなどの描画プリミティブに置き換えました。
GCオブジェクト生成の抑制
スプレッド演算子({...obj})やfilter/mapによる配列再生成を、ホットパス(毎フレーム実行されるコード)から排除し、in-place mutationに書き換えました。
レイヤー合成
各プリセットは独自のcanvasを生成し、CSSのmix-blend-mode: screenで合成しています。プリセットはbackground(0)で黒背景を描画するため、screenブレンドでは黒が透明になり、明るい部分だけが重なります。
この方式により、複数のプリセットを同時に表示して自由に重ね合わせることができます。

決済: DB不要のHMAC方式
個人開発でデータベースの管理を避けたかったため、HMACベースのライセンスキーを採用しました。
- Stripe Checkoutで決済
- リダイレクト先のVercel Functionでsession_idを検証
- タイムスタンプ + HMACでライセンスキーを生成
- クライアント側のlocalStorageに保存
検証時はHMACを再計算するだけで、外部APIへのリクエストは不要です。
プロジェクトの規模
- コード: 約15,000行(テスト含む)
- プリセット: 190種(背景180 + 3Dエフェクト10)
- 開発期間: 約1ヶ月
- テスト: 100件
- 価格: $27 買い切り(Free版は36プリセット)
まとめ

フレームワークなし、サーバーなし、DBなしで、スマホのブラウザだけで動くVJアプリを作りました。Web Audio APIとCanvas APIの組み合わせは、リアルタイムの音声ビジュアライゼーションに十分実用的です。
ぜひ試してみてください。
VJam: https://vjam.vercel.app
フィードバックはX(@VJam_app)までお願いします。