Sound analysis
Let’s start by building basic sound analysis methods for whatever comes next. To make the visuals react to different parts of the audio, we split the track into multiple stems.
The audio used throughout this site is compressed to MP3 and trimmed for demo purposes. All audio is by Yaporigami (Yu Miyashita), licensed under CC BY-NC-SA 4.0. サイトで使用している音声はすべてMP3に圧縮し、デモ用にトリミングしています。音源はすべてYaporigami (Yu Miyashita)によるもので、CC BY-NC-SA 4.0のもとでライセンスされています。
Amplitude and spectrum
open full size ↗Each stem has two live views: a running RMS amplitude trace over the last 15 seconds, and the frequency content of the current frame as 128 Mel-spaced bands, both updating at 60 fps.
We use a Python script to precompute this data before the browser ever sees it, so every frame’s visual state comes from a fixed row of numbers — the same input always renders the same output, with no runtime signal processing and no timing drift between audio and visuals during video export.
How the analysis works. A full-file STFT at 2048-point resolution builds a global energy profile across all frequencies. Spectral roll-off at 5 % and 85 % of cumulative energy marks where the track actually lives — the frequencies that carry its musical content — ignoring sub-bass rumble below and empty headroom above. A Mel-scale filterbank then maps 128 bands logarithmically across that active range, concentrating resolution where the musical content is densest. The script processes left and right channels separately and writes one row of numbers per frame at 60 fps: L and R RMS amplitude followed by 128 Mel band magnitudes for each channel. The output is stored as raw little-endian 32-bit floats — 258 values per frame, 1032 bytes per frame — which the browser loads directly into a typed array without any parsing overhead. ファイル全体に対して2048ポイント解像度でSTFTを行い、全周波数にわたるエネルギープロファイルを作成します。累積エネルギーの5%点と85%点から帯域の境界周波数を求め、トラックの音楽的な内容が実際に存在する周波数帯を特定します。これにより、それより下の低域ノイズや、上側の空白領域は無視されます。次に、Melスケールのフィルタバンクでこの有効範囲全体を対数的に128バンドへマッピングし、音楽的な内容が最も密な領域に解像度を集中させます。スクリプトは左右チャンネルを別々に処理し、60fpsで1フレームにつき1行の数値を書き出します。各行はL/RのRMS振幅に続いて、各チャンネル128個のMelバンド振幅が並びます。出力はリトルエンディアンの32ビット浮動小数点として保存され、1フレームあたり258個の値(1032バイト)になります。ブラウザはこれをパースせずに直接型付き配列へ読み込みます。
The browser fetches that file once and advances through it frame by frame in step with the audio, skipping each stem’s silent lead-in by scanning for the first frame whose amplitude clears a small threshold and cueing both audio and display to that point.
Linear vs. log scaling. The Mel band values are stored as linear amplitude — each number is proportional to energy in that frequency band. Human perception is not linear — loudness is logarithmic: a band at 1 % of peak energy is audible, not negligible. Before display, the browser maps each value to decibels: \( v_\text{dB} = 20 \log_{10}(\max(v, 10^{-5})) \), then rescales \([-80,\, 0]\) dB to \([0, 1]\). The floor at \(-80\) dB is below any content that matters; the ceiling at \(0\) dB is the global peak of each stem (since the data is normalised to its own maximum). The result is a spectrum that fills the display range without clipping. Melバンドの値はリニア振幅として保存され、各値はその周波数帯域のエネルギーに比例します。人間の聴覚は線形ではなく、音の大きさは対数的に知覚されます。そのため、ピークエネルギーの1%程度でも十分に聞こえ、無視できません。表示前にブラウザは各値をデシベルに変換し、[-80, 0] dBを[0, 1]に再スケーリングします。-80 dBは意味のあるコンテンツがほとんど含まれない下限で、0 dBは各ステムの全体ピーク(データはそれぞれの最大値で正規化)に相当します。これにより、クリッピングなしで表示範囲全体を使い切るスペクトルが得られます。
Spectrograms
















Each strip shows a one-minute excerpt — the densest section of each stem, selected by finding the sixty-second window with the highest cumulative amplitude. The horizontal axis is time — ten frames collapsed into one pixel column. The vertical axis runs from the stem’s lowest active frequency at the bottom to its highest at the top. Pixel brightness is the peak Mel-band amplitude within that 10-frame window, with a square-root curve applied so quieter detail stays visible. L and R channels are shown separately.