plonk:ゲームのサウンドトラックまるごとを 2 kB で
plonk は、コードから曲を作る Web ゲーム向けの音楽エンジンです。曲は数百バイトのテキストで、エンジンは brotli で約 2 kB。ふつうの圧縮済みの音楽ループは、ひとつで 300 kB から 1 MB ほどあります。私たちが Poki 向けに作っているマッチ 3 ゲーム gloop では、サウンドトラック全体を入れてもビルドが 2.2 kB 増えただけでした。プレイグラウンドで実際に聴いてみてください。
| サイズ(brotli) | |
|---|---|
| よくある圧縮済みの音楽ループ | 300 kB〜1 MB |
| plonk のエンジン(esbuild、minify 済み) | 約 2.1 kB |
gloop のサウンドトラック、エンジン込み(debuff build のレポートより) | 2.2 kB |
npm install plonkjsimport { Player } from "plonkjs";
new Player().play({
bpm: 112,
instruments: {
lead: { wave: "square", decay: 0.14, sustain: 0.35, echo: 0.3 },
kick: { wave: "sine", drop: 24, decay: 0.12, sustain: 0 },
},
sections: { a: { lead: "C5 . E5 G5 . E5 C5 .", kick: "x . . . x . . ." } },
order: ["a"],
});なぜ作ったのか
gloop では、ぷるぷるの生き物、スライム、文字まで、すべてをコードで描いています。そうするとゲームは小さく保てますし、細部まで調整できます。そのなかで音楽だけが浮いていました。シンセのピコピコ音が少しあるだけ。ちゃんとした曲を 1 つ入れたら、それだけでゲームの残り全部より大きくなってしまうからです。
Poki ではこのサイズが効いてきます。ダウンロードの大きさで、プレイヤーがクリックしてから遊び始めるまでの速さが決まり、Poki はまさにそこを計測しています。だから音楽も、アートと同じやり方で作る必要がありました。
曲はテキスト
1 ステップは 16 分音符です。C5 は音符、C4+E4+G4 は和音、x はドラムの打音、. は休符、- は音を伸ばします。曲は、こうしたパターンでできたセクションを順番に鳴らしたものです。
sections: {
a: {
lead: "C5 . E5 G5 . E5 C5 . | D5 . F5 A5 . F5 D5 .",
bass: "C3 . . C3 . . G2 . | D3 . . D3 . . A2 .",
kick: "x . . . x . . .",
},
},短いパターンはセクションを埋めるまで繰り返されます。なので、長いメロディの下で1 小節のドラムループを回せます。楽器はオシレーターかフィルターをかけたノイズにエンベロープを付けたものです。さらに、音符ごとにスイープできるレゾナンスフィルター、デチューン、ピッチドロップ、ビブラート、テンポに同期したエコーも使えます。gloop のぶにゅっとしたベースは、たった 1 行です。
bass: { wave: "sawtooth", octave: -1, volume: 0.22, filter: { cutoff: 380, q: 9, sweep: 1400 } },曲はテキストなので、コーディングエージェントも書いたり編集したりできます。notes() は曲をタイミング付きの音符のリストに変えます。だからエージェントやテストでも、聴けない音楽を確かめられます。
ゲームに反応する音楽
gloop のテーマ曲は1 つだけで、それを曲げて使っています。マップ画面ではドラム抜きの 85% の速さで流れます。レベルに入るとドラムが加わります。残り 3 手になると 12% 速くなります。レベルが終わると、ジングルの下でドラムがすっと消えます。
player.layer("kick", 0, 0.8); // fade the kick out
player.tempo(1.12); // speed up, without a jump音符はフレームではなくオーディオクロック上で先にスケジュールされます。そのため、ゲームでフレームが落ちても音楽はずれません。
どのエンジンでも
plonk には依存関係がありません。エンジンのオーディオコンテキストと出力先のノードを渡せば、音楽はゲームの音量やミュートに従います。
new Player({ context: audio.ctx, destination: audio.bus("music") }); // debuff
new Player({ context: sound.context, destination: sound.destination }); // Phaserできないこと
plonk はシンセです。鳴らせるのは矩形波、のこぎり波、三角波、サイン波と、フィルターをかけたノイズです。録音した楽器や音声ファイルは再生できませんし、録音された BGM の代わりにはなりません。でも、ほとんどコストのかからない、弾むようなチップチューン風のサウンドトラックが欲しいなら、ぴったりです。
プレイグラウンドを開いて、パターンを変えて、鳴らしてみてください。作ったものはぜひ Discord で見せてください。