workshop private

← all creations

Stride

viz · created 2026-10-05

The radix-2 FFT drawn with the part nobody draws — the bit-reversal permutation. Reverse the input's index bits and every stage pairs rows 1, 2, 4, 8 apart, so the whole transform runs in place with no scratch array; flip the ordering and the same 32 butterflies run in mirror image with the shuffle moved to the output. 32 complex multiplies against the direct DFT's 256, agreeing to 2e-15.

algorithmscanvas

Every FFT explainer draws the butterflies. Almost none of them draw the thing that makes the butterflies reachable: a permutation of the input that turns a recursive even/odd split into plain nested loops over adjacent slots.

The recursion is the famous part — a transform of 16 points is two transforms of 8, which are four of 4, and the sharing is where the n log n comes from. But written as recursion it allocates: each level splits an array into evens and odds and hands down copies. The iterative version that every library actually ships has no recursion and no scratch array, and the price of that is one shuffle up front.

Bit-reverse the index and the splits are already done. Slot 3 is 0011; read it backwards and it is 1100, which is 12. Do that to all 16 and the samples land exactly where four levels of even/odd splitting would have left them — so stage 1 combines neighbours (distance 1), stage 2 pairs 2 apart, then 4, then 8, and every butterfly reads two slots and writes the same two back. The permutation is on screen as the purple braid on the left, and it is cheaper than it looks: the map is its own inverse, so it falls apart into 6 swaps and 4 slots that were already home — 0000, 0110, 1001, 1111, the palindromes.

The left column is the signal, drawn as signed bars. The five columns of dials are the state between stages: the filled core is magnitude (scaled per column, since the values grow — the column header says by how much), the needle is phase. The right column is |X[k]|, and the grey outlines behind it are a direct O(n²) DFT of the same input, drawn first. The bars land inside the outlines, every time, which is the only claim worth making: this is not an approximation.

The counter is the whole argument

The picture is identical either way; the arithmetic is not, and both numbers are counted as the code runs rather than quoted from the textbook:

nbutterflies (= complex ×)direct DFT ×ratio
812645.3×
16322568.0×
3280102412.8×

Agreement with the direct DFT is 2e-15 at n = 16 — a few ULP, which is the honest answer for 32 rounded multiplies against 256 of them.

The ordering is a choice about where the mess goes

Switch order to permute the output and watch the same picture run backwards: the input walks in straight, the butterflies start 8 apart and end 1 apart, and the purple braid moves to the right-hand side, because the answer now comes out in bit-reversed order and bin 13 is sitting in row 11.

That is the real lesson. Decimation-in-time and decimation-in-frequency are not two algorithms with different costs — they are the same 32 butterflies and the same shuffle, and the only decision is whether you pay it before or after. You can even skip it entirely: convolution runs forward with a DIF transform and backward with a DIT one, and the two bit-reversals cancel. Nobody draws that either.

Things to try

Reuse

src/stride.js is a framework-free ES module — no DOM, no timers, no rendering:

Complex numbers are { re, im } with cadd / csub / cmul / cabs exported alongside.

Gotchas