万華鏡アプリの BGM を「コード」じゃなく「モチーフ」で作り直した話

| デザイン | 万華鏡

タグ: #音楽 #UX #デザイン

万華鏡アプリのヒーリング BGM を自分で流しっぱなしにしたら、5 分で飽きました。3 つのコードを回すのをやめて、4 つのモチーフをランダムに遷移させる作りに変えたら、眺めていられる時間が延びた話です。

ヒーリング BGM の難しさ

万華鏡アプリは「画面をぼーっと眺めるアプリ」です。だから BGM の良し悪しが体験に直結します。初期版では、3 つのコード(I → IV → V)を繰り返すヒーリング系の BGM を流していました。

自分で長時間流しっぱなしにしてみました。5 分くらいで飽きます。コード進行のループ感が、リラックスどころか軽くストレスになる(作った本人がいちばん先に飽きました)。

モチーフベースという発想

音楽理論的にいうと、コードベースの BGM は「縦の構造」を組み立てる作り方です。モチーフベースは違います。短い旋律フレーズ(モチーフ)を複数用意して、変奏・転調しながら繋いでいく「横の構造」のアプローチです。

万華鏡は常に変化していく視覚です。相性がいいのはモチーフベースだろう。そう判断して BGM を作り直しました。

実装はランダム遷移

4 つのモチーフ(各 8 小節程度)を用意して、再生中はランダムに遷移させます。同じモチーフが連続しないようにメモリを持たせました。転調も挿入して、飽きにくくしています。

コードベースの BGM に比べると、聴いていて「次に何が来るか分からない」感が出ます。ぼーっと眺めている時間が延びました。

遷移ロジック自体はとてもシンプルです。「直前と同じモチーフは選ばない」「キーは小さい確率で転調する」の 2 ルールだけです。

class MotifScheduler {
  MotifScheduler(this.motifs, {Random? rng})
      : _rng = rng ?? Random();

  final List<Motif> motifs;
  final Random _rng;
  Motif? _previous;
  int _key = 0; // semitone offset

  Motif next() {
    final candidates = motifs.where((m) => m != _previous).toList();
    final picked = candidates[_rng.nextInt(candidates.length)];
    if (_rng.nextDouble() < 0.15) {
      const transposes = [-2, -1, 1, 2];
      _key = (_key + transposes[_rng.nextInt(transposes.length)]) % 12;
    }
    _previous = picked;
    return picked.transposed(_key);
  }
}

アプリ名を「万華鏡」へ

BGM のリメイクと同時に、アプリ名を「万華鏡」というシンプルな日本語ひとことに変更しました。サブタイトルも「ヒーリング訴求」に変え、「物理シミュレーション」と「カメラ取り込み」を打ち出すようにしました。

アプリの方向性を、数学的な美しさ・リラックス・触っていて気持ちいい、というキーワードで据え直したのです。

スパークル 5 段階の設定

「派手さ」の好みは人によってぜんぜん違います。なのでスパークル(キラキラ感)を 5 段階で調整できるようにしました。

  • レベル 1: 控えめ(ヒーリング向け)
  • レベル 3: 標準
  • レベル 5: 派手(賑やかし向け)

設定が増えると UI が散らかります。5 段階に絞ったうえで、「変えたい人だけ深掘りできる」階層に置きました。

カラー+写真+ブレンドのデバッグ画面

開発中、テッセレーション(敷き詰め)のデバッグ用画面を作りました。「色」「写真」「ブレンド」モードを切り替えられる隠し機能です。これが意外と楽しくて、一部を製品 UI にも組み込みました。

開発ツールがそのまま製品の機能になるパターン。個人開発ならではです。

まとめ

  • ループする BGM は意外と飽きる。モチーフ遷移にすると延命できる
  • アプリ名・サブタイトル・BGM はセットで方向性を再定義できる
  • 派手さは段階で可変にして、好みに合わせる

「ヒーリング」を本気で作るのは、思ったより難しい仕事でした。次に触るときは、音のほうをもう少し勉強してからにします。