Task 4 of 5

Decibels, Then Colour

The numbers in a spectrogram span orders of magnitude. In this one the loudest cell is about 0.126 and the median cell about 0.00014 — a factor of nearly 900. Hand that straight to a colour ramp and the median cell gets a tenth of one percent of the range: you get a black rectangle with a few bright streaks, and the obvious conclusion — "my kernel is broken" — is wrong. Your kernel is fine. Your scaling is wrong.

The fix is what ears already do: work in decibels. db = 20 · log₁₀(mag / peak) puts the loudest cell at 0 and every halving 6 dB below it, so a 60 dB display range covers a factor of 1,000 in one legible ramp and everything quieter clamps to black. That single transform is the difference between a picture and a black rectangle.

Then colour. Loudness rides on value — the part that has to be monotone, or the picture lies about which cell is louder — and the hue sweep is there for legibility, which is the case Colour Spaces argues at length. The ramp below is written for you: black → indigo → magenta → orange → cream. What is yours is the two lines above it, and the canvas is 256×256, so each spectrogram cell paints a 4×2 block.

Goal: finish the paint kernel — magnitude to decibels, decibels to a 0…1 value — and render the result.

Requirements

Hint 1 — log base 10

Math.log10 is on gpu.js's whitelist, so write it directly — no need for Math.log(x) / Math.LN10. Math.log is the natural logarithm and gives answers 2.3 times too far down the scale.

Hint 2 — the two lines
const db = 20 * Math.log10(mag / peak + 1e-9);
const v = Math.max(0, Math.min(1, 1 + db / this.constants.range));
Hint 3 — why the 1e-9

log₁₀(0) is -Infinity, and -Infinity / 60 stays infinite: one silent cell would paint NaN, which lands on screen as whatever the driver felt like. The tiny floor costs nothing (it is 180 dB down) and makes the silent case land on plain black instead.

Same idea elsewhere

Amplitude-to-decibels is the universal display transform for audio — librosa.amplitude_to_db, every DAW meter, every analyser you have ever looked at — and the wider lesson generalises well past sound: a GPU can compute a perfectly correct answer and a bad transfer function will still show you nothing. HDR tone mapping, log-scale depth buffers and gamma correction are the same move made for the same reason. On any platform this is a fragment shader reading a storage texture; the arithmetic does not change.

All tasks in Spectrograms

  1. One Spectrum, No Clock
  2. Slice, Window, Transform
  3. Fine in Time, or Fine in Frequency
  4. Decibels, Then Colour
  5. Payoff: Read the Sweep Off the Picture

This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.