Task 6 of 6

Run It Backwards for Free

The inverse transform differs from the forward one in two small ways: the exponent is +2πi·kt/n instead of -, and the result is divided by n. You could write a second set of kernels. You do not have to — there is an identity that gets the inverse out of the forward transform you already have:

ifft(X) = conj( fft( conj(X) ) ) / n

Conjugating flips the sign of every imaginary part, which is exactly the sign flip the exponent needs; doing it on the way in and again on the way out leaves everything else where it was. Nine lines of kernel and a division, and the transform runs both ways.

And then the warning, which is the real content of this task. A round trip is a consistency check, not a correctness check — it proves your inverse undoes your forward transform, and nothing else. Flip the twiddle sign in the butterfly and the forward transform computes the conjugate of the right spectrum; run it through this same inverse and the signal comes back perfectly, to the last bit. The magnitudes were already identical, the round trip is clean, and every plot looks right. The only thing that catches it is the comparison you did in the last task: against the definition.

Goal: write the conjugate-and-scale kernel, use it at both ends of a forward transform to invert one, and confirm the 256 samples come back.

Requirements

Hint 1 — conjugation, both planes at once

The kernel owns one output cell, and which plane it is decides the sign:

if (this.thread.y === 0) return spectrum[0][this.thread.x] * scale;
return -spectrum[1][this.thread.x] * scale;

The real part keeps its sign. Only the imaginary part turns over.

Hint 2 — the whole inverse, one line
const recovered = conjugate(fft(conjugate(spectrum, 1)), 1 / n);

Inside out: conjugate, transform, conjugate again and scale. The 1 on the way in is not decoration — the same kernel has to take a scale both times or gpu.js sees two different call shapes.

Hint 3 — where the n goes

All of it, on the inverse. A forward transform followed by an unscaled inverse gives you the signal multiplied by n — so if every recovered sample is 256 times too big, the division is missing rather than misplaced.

Same idea elsewhere

The conjugation trick is standard practice, not a curiosity: it is why cuFFT, rocFFT and FFTW all describe their inverse as "unnormalized" and leave the 1/n to you — the same kernels run both directions, and a library that scaled automatically would make the common case (transform, filter, transform back, and the scales cancel) pay for something nobody asked for. Where the 1/n lives, or whether it is split as 1/√n between the two directions, is a convention you have to read off each library's documentation rather than assume.

All tasks in The FFT Butterfly

  1. Where the Saving Comes From
  2. The Butterfly: One Thread, One Output
  3. Put the Input in the Right Order
  4. Drive log₂n Passes
  5. Same Answer, Two Centuries Apart
  6. Run It Backwards for Free

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