ノイズに色をつける — 白・ピンク・茶
ノイズには「色」がある。光の白色光が全周波数を均等に含むのになぞらえて、全周波数が均等なノイズが白色ノイズ。一様乱数を一粒ずつ並べたものがこれで、各サンプルが互いに無相関、隣が前の値を覚えていない、いちばんザラザラした状態になる。
ノイズの「色」はパワースペクトルの傾きのラベル。周波数 f に対しエネルギーが 1/f⁰ で平らなのが白色ノイズ、1/f² と急に低音へ傾くのが赤(ブラウン)ノイズ、その中間の 1/f がピンクノイズ。一様乱数を独立に並べると相関ゼロで全周波数が均等になり白色、それを積分すると低周波が強まって 1/f² の赤、周波数を倍々にしたいくつかの成分を高い方ほど弱く足すと 1/f のピンクに近づく。
白を一本だけ引く。各サンプルが Math.random() * 2 - 1 で -1〜1 を独立に取るだけ。隣との相関がないので、線は毎サンプルてっぺんと底を行き来して、とげとげに潰れる。
全周波数が同じだけ入っているので、どこを見ても同じ細かさで暴れる。茶はこの逆で、一様乱数を積分して 1/f² の坂にしたもの。前のサンプルに足し込む一行が積分にあたる。* 0.99 は漂いすぎを抑えて値を中心へ少しずつ引き戻している。
// 茶(1/f²): 前のサンプルに足し込む = 積分。* 0.99 で漂いすぎを抑える brown += (Math.random() - 0.5) * 0.5 brown *= 0.99
ピンクは、周波数を倍々にしたいくつかの正弦波を、高い方ほど弱くして足し合わせる。phase[i] をそれぞれ違う速さ freq[i] = 0.04 * 2 ** i で回し、weight を一段ごとに半分にしながら合算する。低いオクターブが大きな波を、高いオクターブが細かい震えを担当して、足すと低音側に傾いた 1/f になる。
// ピンク(1/f): 倍々の周波数を、高い方ほど弱く足す
const pink = () => {
let s = 0
let weight = 1
let wsum = 0
for (let i = 0; i < octaves; i++) {
s += Math.sin(phase[i]) * weight
wsum += weight
weight *= 0.5 // 高いオクターブほど弱く
phase[i] += freq[i]
}
return s / wsum
}オクターブを重ねるこの足し算は、画像の fBm(複数スケールのノイズを重ねる手法)と同じ骨格を持つ。下に白・ピンク・茶を縦に並べる。上から白(とげとげ)、ピンク(ほどよい揺れ)、茶(大きくうねる)。
上の白は隣どうしが無関係なので、線が毎サンプル跳ねてギザギザになる。下の茶は前を引きずるので、ゆったり大きく波打つ。真ん中のピンクはその中間で、細かい揺れと大きなうねりが両方ある。色はスペクトルの傾きのラベルで、傾きを乱数の積分やオクターブ加算で作り分けている。