マガジン

雨音を自分で作る:ノイズをフィルターで雨にする

遊びをつくる

執筆にClaudeを使用しています。

大きなボタンに指が触れ、スピーカーへ金色の音の波が広がる立体イラスト
記事のテーマをもとに制作したAI生成イラスト。

ブラウザゲームに雨の場面を入れたいとき、雨音の音声ファイルを探して組み込むのが定番の方法です。ただ、コードだけで雨音を作れるなら、ファイルを持たずに済みます。容量が軽くなり、強さや雰囲気を実行中に滑らかに変えることもできます。 そこで問いです。何の特徴もない「ザーッ」というだけのノイズは、周波数の操作だけでどこまで雨に近づくでしょうか。この記事は、その答えを読者が自分の耳で出すための実験の設計書です。結果は書きません。手元で鳴らし、条件を一つずつ動かして、自分の基準で判断してください。 使うのはWeb Audio APIというブラウザ標準の音の仕組みだけです。外部ライブラリもサーバーも要りません。乱数で作ったノイズに、特定の周波数帯だけを通すフィルターをかけ、そこに「粒」の音を重ねます。この3段階で、何が雨らしさを決めるのかを切り分けます。

この記事の目次
  1. 何を確かめるか:三つの変数と仮説
  2. 準備するもの
  3. 手順:条件を一つずつ動かす
  4. 記録の取り方
  5. 結果の読み方:こうなればこう判断する
  6. 起こりやすい誤り
  7. 次の行動
  8. 参考資料

何を確かめるか:三つの変数と仮説

雨音を分解すると、少なくとも三つの要素があります。まず、屋根や地面に当たる無数の雨粒が重なった「ザーッ」という背景です。次に、その背景がどの周波数帯に偏っているか。最後に、背景の上に聞こえる個々の雫の音です。実験ではこれを次の変数に置き換えます。

  • 背景のノイズ:乱数から作ったホワイトノイズです。ホワイトノイズは全周波数に均等にエネルギーが散らばった音で、人の耳には高域が強く聞こえやすい性質があります。
  • 帯域の偏り:バンドパスフィルターの中心周波数と、通す幅(Q値)です。Qが小さいほど幅が広く、大きいほど狭くなります。
  • 粒:短く高い音を、ランダムな間隔で重ねたものです。

実験を始める前に、自分の仮説を紙に書いておいてください。たとえば「中心周波数が低すぎると風や換気扇に聞こえ、高すぎると砂嵐に聞こえるはずで、雨らしい範囲はその間にある」「粒がゼロだと雨ではなく単なる雑音に聞こえる」といった形です。仮説と結果のずれが、この実験でいちばん面白い部分になります。

準備するもの

  • モダンブラウザ(Web Audio APIに対応したもの)と、テキストエディタ
  • 有線かBluetoothを問わず同じヘッドホン(スピーカーでも構いませんが、実験中は変えないでください)
  • 記録用の紙か表計算ソフト
  • 可能なら、条件を伏せて聞き比べてくれる協力者を1人

音量には注意してください。ノイズは意外に耳に刺さります。最初は端末の音量を低めにして、少しずつ上げてください。

次のコードを rain.html として保存し、ブラウザで開きます。音は、ボタンを押した後でないと鳴りません。ブラウザは利用者の操作前の自動再生を制限しているためです。

html
<!doctype html>
<meta charset="utf-8">
<button id="go">雨を鳴らす</button>
<label>中心周波数 <input id="f" type="range" min="400" max="8000" value="3000"></label>
<label>Q <input id="q" type="range" min="0.2" max="4" step="0.1" value="0.7"></label>
<label>粒の量 <input id="d" type="range" min="0" max="30" value="8"></label>
<script>
let ctx, band;
const $ = id => document.getElementById(id);
function update() {
  if (!band) return;
  band.frequency.value = +$("f").value;
  band.Q.value = +$("q").value;
}
$("f").oninput = $("q").oninput = update;
function drop() {
  const n = +$("d").value;
  if (n > 0) {
    const o = ctx.createOscillator(), g = ctx.createGain(), t = ctx.currentTime;
    o.frequency.value = 1500 + Math.random() * 2500;
    g.gain.setValueAtTime(0.04, t);
    g.gain.exponentialRampToValueAtTime(0.0001, t + 0.05);
    o.connect(g).connect(ctx.destination);
    o.start(t); o.stop(t + 0.06);
  }
  const rate = Math.max(n, 1);
  setTimeout(drop, -Math.log(Math.random()) * 1000 / rate);
}
$("go").onclick = () => {
  if (ctx) return;
  ctx = new AudioContext();
  const len = ctx.sampleRate * 2;
  const buf = ctx.createBuffer(1, len, ctx.sampleRate);
  const data = buf.getChannelData(0);
  for (let i = 0; i < len; i++) data[i] = Math.random() * 2 - 1;
  const src = ctx.createBufferSource();
  src.buffer = buf; src.loop = true;
  const hp = ctx.createBiquadFilter();
  hp.type = "highpass"; hp.frequency.value = 500;
  band = ctx.createBiquadFilter(); band.type = "bandpass";
  const gain = ctx.createGain(); gain.gain.value = 0.25;
  src.connect(hp).connect(band).connect(gain).connect(ctx.destination);
  src.start();
  update(); drop();
};
</script>

コードの流れは次のとおりです。

  1. 2秒分のバッファに、-1から1までの乱数を詰めます。これがホワイトノイズです。
  2. バッファをループ再生し、ハイパスフィルターで500Hz未満の低音を削ります。
  3. バンドパスフィルターで、中心周波数の付近だけを通します。
  4. 粒は、1.5kHzから4kHzのランダムな高さの短い音を、指数分布の間隔で鳴らします。指数分布にすると、雨粒のように「不規則だが平均すると一定の頻度」という並びになります。粒はフィルターを通さず、直接出力に流しています。

手順:条件を一つずつ動かす

実験の原則は、1回に動かす変数を一つだけにすることです。三つを同時に触ると、何が効いたのか分からなくなります。

手順1:まず基準を決める

  1. 中心周波数3000、Q 0.7、粒の量8の初期状態で、ボタンを押して30秒聞きます。
  2. この音を「基準」と呼び、雨らしさを1(全く雨に聞こえない)から5(雨に聞こえる)で採点します。
  3. 続けて、「何に聞こえるか」を一言で書きます。風、換気扇、砂嵐、シャワー、屋根の雨など、思いついた言葉で構いません。

手順2:中心周波数だけを動かす

Qは0.7、粒の量は8のまま、中心周波数を800、2000、3000、5000、7000の順に変えます。一つの条件につき30秒以上聞いてください。各条件で、採点と一言を記録します。

手順3:Qだけを動かす

中心周波数を手順2で最も雨らしかった値に固定します。Qを0.3、0.7、2、4と変えて、同じ記録を取ります。

手順4:粒の量だけを動かす

中心周波数とQを手順2・3の最良値に固定し、粒の量を0、8、20に変えます。粒の量を上げた直後は、次の粒が鳴るまで少し間があくことがあります。数秒聞いてから判断してください。

手順5:協力者に伏せて聞いてもらう

自分の耳は、条件を知っていると判断が偏ります。可能なら、協力者に画面を見せず、あなたが条件を切り替えた音を聞いてもらい、同じ採点をしてもらってください。条件を出す順番はサイコロやくじなどでランダムにします。

記録の取り方

記録は、条件ごとに1行の表にします。

  • 1、周波数:3000、Q:0.7、粒:8、雨らしさ(1〜5):、一言:、聞いた人:自分
  • 2、周波数:800、Q:0.7、粒:8、雨らしさ(1〜5):、一言:、聞いた人:自分

同じ条件を2回、別の日や時間を置いて採点してください。同じ条件でも点が大きくぶれるなら、その差は条件の効果ではなく、聞き手の状態や環境の影響が混ざっている可能性があります。

採点より、「一言」の欄を大切にしてください。「3だった」より「換気扇っぽい」のほうが、次にどこを直すかの手がかりになります。

結果の読み方:こうなればこう判断する

結果は、次のように読みます。傾向の説明は、音響の一般的な性質に基づく目安です。実際にどう聞こえるかは、ヘッドホンやスピーカーの特性、聞き手の耳によって変わります。

  • 周波数を動かして採点がほとんど変わらない場合:帯域の偏りより、ほかの要素(粒、揺らぎ)が雨らしさを左右している可能性があります。次はQと粒を重点的に見てください。
  • 周波数の両端で「風」や「砂嵐」といった言葉が出る場合:雨らしい範囲が中間にあるという最初の仮説が支持されます。その範囲の中央をメモしてください。
  • Qを上げたときに笛のような、音程のある音に聞こえる場合:帯域が狭いほど、特定の音の高さが目立ちやすくなります。雨の「面」の広がりが失われたと判断できます。
  • 粒を入れると採点が上がる場合:雨の印象は、背景の帯域だけでなく、個々の雫の存在にも支えられていると読めます。逆に粒が邪魔に感じる場合は、音の高さや音量が合っていない可能性があります。コードの 1500 + Math.random() * 2500 や 0.04 を変えて、同じ実験をやり直してください。
  • 自分と協力者の採点が大きく食い違う場合:個人差の影響が大きい条件です。ゲームに入れるなら、意見が一致した範囲を選ぶほうが安全です。

起こりやすい誤り

  • 条件を複数同時に変えてしまう。原因が分からなくなります。必ず一つずつ動かします。
  • 聞く時間が短い。ループ再生のノイズは、数秒では判断しにくい場合があります。30秒以上聞いてください。
  • 2秒のループの継ぎ目が気になる。短いバッファを繰り返しているため、同じパターンが規則的に聞こえることがあります。長めのバッファにするか、ほかの工夫が必要です。この点は、拡張実験として確かめられます。
  • 音量を揃えない。大きい音は迫力があり、雨らしく感じやすい傾向があります。条件ごとに音量を変えないでください。
  • スピーカーとヘッドホンを混ぜる。再生機器の特性が結果に混ざります。
  • ブラウザ操作の前に音を出そうとする。ボタンを押す前は鳴らない仕様です。故障ではありません。

次の行動

まず、基準の30秒を聞いて、採点と一言を書いてみてください。そこから周波数、Q、粒の順に動かせば、あなたの耳に合う雨の設定が表にまとまります。その数値は、そのままゲームの環境音に使えます。

ファイルが1つのHTMLなので、アソバースへの投稿にも向いています。アップロードは20MiB以内で、直下の index.html が入口になります。雨音の聞き比べには、「雨音のピアノ」の雨音つき自由演奏も使えます。ただし、あの雨音の作り方は確認できていません。同じ作り方だと思い込まず、耳で比べるだけにしてください。

参考資料

  • MDN Web Docs「Web Audio API」
  • MDN Web Docs「BiquadFilterNode」
  • MDN Web Docs「Autoplay guide for media and Web Audio APIs」