class MaxFFT

Fast Fourier Transform processor

MaxFFT provides SIMD-accelerated forward and inverse Fast Fourier Transforms of real or complex signals, in single or double precision. It is backed by the same FFT library used by the fft~ family of MSP objects.

MaxFFT operates on plain JavaScript Arrays (values are copied in and out) or on TypedArrays (Float32Array / Float64Array). TypedArray data is processed directly in place in memory -- without copying -- whenever its underlying buffer is suitably aligned for SIMD access. Use the static MaxFFT.alloc helper to obtain TypedArrays that are guaranteed to take this zero-copy path. TypedArrays from other sources still work; if they happen to be misaligned the data is transparently copied through an internal aligned buffer instead.

MaxFFT accepts ANY transform size from 1 up to 67108864 (2^26) -- every size in that range constructs successfully, and every transform is exact. Sizes that factor as 2^a * 3^b * 5^c (also subject to MaxFFT.minSize() -- 32 for real, 16 for complex transforms on SIMD builds) -- powers of two are always among them, but so are sizes like 480 (2^5 * 3 * 5) -- run natively at full SIMD speed. Every other size in range still works, transparently falling back to a Bluestein (chirp-z) engine that computes the same transform in O(n log n) via an internal FFT at a nearby fast size, rather than a slow O(n^2) direct sum -- the tradeoff is speed, not correctness: expect roughly 5-8x the running time of a native-fast size of similar magnitude. Use MaxFFT.isFastSize() to check whether a given size lands on the native path, and MaxFFT.nearestFastSize() to find a nearby size that does, when you want to trade a little zero-padding for the fastest possible transform.

Real transforms (type: "real") work at any size, odd or even, under spectrum: "unpacked". The default spectrum: "packed" layout shares the DC and Nyquist components in a single pair, which requires a distinct Nyquist bin to exist -- so it's only available at even sizes; combining spectrum: "packed" with an odd, non-native-fast size throws a TypeError (see MaxFFTOptions.spectrum).

Performance note: constructing a MaxFFT allocates native plan and work buffers, and a transform called without an explicit output argument allocates a fresh result array on every call. Neither is real-time safe. For repeated or timing-critical processing, construct instances once, reuse them, and pass a preallocated output (ideally from MaxFFT.alloc) so the steady-state call performs no allocation at all -- this also gives deterministic per-call latency, since allocation cost under V8 varies with garbage-collector state. Very large transforms are synchronous main-thread work; account for their running time when calling from timing-sensitive contexts.

This class is only available in the new v8 javascript engine objects.

Example

const N = 1024;
const fft = new MaxFFT(N, { normalize: true });
const signal = MaxFFT.alloc(N); // aligned Float32Array, zero-copy path
for (let i = 0; i < N; i++) {
  signal[i] = Math.sin((2 * Math.PI * 8 * i) / N);
}
const spectrum = fft.forward(signal);
// bin 8 of the packed half-spectrum:
post("re:", spectrum[16], "im:", spectrum[17], "\n");
const restored = fft.inverse(spectrum); // equals signal (normalize: true)
fft.dispose();

Index

Constructors

Properties

Methods

new MaxFFT(size, options)

new MaxFFT(size: number, options?: MaxFFTOptions);

Create an FFT processor for a fixed transform size.

size may be any integer from 1 to 67108864 (2^26); a size outside that range throws a RangeError. Sizes that factor as 2^a * 3^b * 5^c (and meet MaxFFT.minSize() for the transform type -- see MaxFFT.isFastSize()) run natively at full SIMD speed; every other in-range size still works, exactly, via an internal Bluestein (chirp-z) fallback (see the class-level remarks). type: "real" with spectrum: "packed" (the default) additionally requires an even size when size isn't a native-fast size -- see MaxFFTOptions.spectrum -- and throws a TypeError otherwise. Allocation or internal setup failure throws an Error.

ParameterTypeDescription
sizenumberThe transform size (number of samples for real transforms, number of complex samples for complex transforms).
optional optionsMaxFFTOptionsTransform type, precision, normalization, and (for real transforms) spectrum layout; see MaxFFTOptions.

MaxFFT.alloc(length, precision) static

Allocate a TypedArray guaranteed to use the zero-copy path.

The returned array's data is 64-byte aligned, which satisfies the SIMD alignment requirement of every MaxFFT configuration, so transforms read and write it directly with no copying. The alignment is achieved via the view's byteOffset into its ArrayBuffer -- note that constructing a different view over the same buffer (for example new Float32Array(x.buffer), which drops the byteOffset) loses the guarantee.

static alloc(length: number, precision?: "float32"): Float32Array;
NameTypeDescription
lengthnumberNumber of elements (use an instance's MaxFFT.length to allocate transform-sized buffers).
optional precision"float32""float32" (default) for a Float32Array or "float64" for a Float64Array.
Return ValueFloat32Array

MaxFFT.alloc(length, precision) static

static alloc(length: number, precision: "float64"): Float64Array;
NameTypeDescription
lengthnumber
precision"float64"
Return ValueFloat64Array

dispose()

Free the native FFT state immediately.

Releases the transform setup and internal buffers without waiting for garbage collection. After disposal, MaxFFT.disposed is true and calling MaxFFT.forward or MaxFFT.inverse throws an Error. Calling dispose more than once is harmless.

MaxFFT instances also implement Symbol.dispose, so they can be bound with a using declaration to be disposed automatically at the end of a scope.

dispose(): void;

disposed boolean read-only

True once MaxFFT.dispose() has been called. Read-only.

forward(input)

Perform the forward transform.

The input must have exactly MaxFFT.length elements and the output (if given, or the value returned otherwise) has exactly MaxFFT.spectrumLength elements -- for spectrum: "packed" (the default) and for complex transforms these are equal, so output may alias input to transform in place; for real transforms with spectrum: "unpacked" they differ, so in-place operation is not possible and passing the same array as both throws a TypeError. A plain Array input returns a new plain Array (values are copied). A TypedArray input must match the instance's precision (Float32Array for "float32", Float64Array for "float64"); the result is written into output if given (which is also returned), or into a newly allocated aligned TypedArray otherwise. An explicit output is not allowed with a plain Array input.

For real transforms with the default spectrum: "packed", the output is a packed half-spectrum of size / 2 complex bins in interleaved order. Because the DC (bin 0) and Nyquist (bin size/2) components of a real signal are themselves purely real, they are packed together into the first pair: out[0] is the DC component, out[1] is the Nyquist component, and out[2k] / out[2k + 1] are the real and imaginary parts of bin k for k = 1 to size/2 - 1. Negative-frequency bins are omitted by symmetry.

For real transforms with spectrum: "unpacked", the output instead follows the numpy/CCS convention (matching numpy's rfft): size + 2 elements holding all size / 2 + 1 complex bins DC through Nyquist, fully interleaved and with no packing -- [DC, 0, re1, im1, ..., re_{size/2-1}, im_{size/2-1}, Nyquist, 0]. out[0] / out[size] are the (real-valued) DC / Nyquist components, out[1] / out[size + 1] are always 0 (their formally-zero imaginary parts), and out[2k] / out[2k + 1] for k = 1 to size/2 - 1 are identical to the packed layout above. Note that in this mode the input and output lengths differ (MaxFFT.length vs. MaxFFT.spectrumLength).

For complex transforms the output is simply all size complex bins in interleaved order.

Forward transforms are never scaled, regardless of the MaxFFT.normalize option.

forward(input: number[]): number[];
NameTypeDescription
inputnumber[]The signal to transform.
Return Valuenumber[]The spectrum, in the same array kind as the input.

forward(input, output)

forward(input: Float32Array, output?: Float32Array): Float32Array;
NameTypeDescription
inputFloat32Array
optional outputFloat32Array
Return ValueFloat32Array

forward(input, output)

forward(input: Float64Array, output?: Float64Array): Float64Array;
NameTypeDescription
inputFloat64Array
optional outputFloat64Array
Return ValueFloat64Array

inverse(input)

Perform the inverse transform.

Takes a spectrum in the same layout that MaxFFT.forward produces -- input must have MaxFFT.spectrumLength elements and the output has MaxFFT.length elements -- and returns the time-domain signal. Argument conventions (array kinds, the optional output, in-place operation) are otherwise the same as for MaxFFT.forward; as with forward(), real transforms with spectrum: "unpacked" have differing input/output lengths and so cannot operate in place.

For real transforms with spectrum: "unpacked", input[1] and input[size + 1] (the imaginary parts of DC and Nyquist) are ignored and expected to be 0, matching what MaxFFT.forward produces.

If the instance was constructed with normalize: true the output is scaled by 1/size, making inverse(forward(x)) reproduce x. Otherwise the round trip yields size * x and any desired scaling is up to the caller.

inverse(input: number[]): number[];
NameTypeDescription
inputnumber[]The spectrum to transform.
Return Valuenumber[]The time-domain signal, in the same array kind as the input.

inverse(input, output)

inverse(input: Float32Array, output?: Float32Array): Float32Array;
NameTypeDescription
inputFloat32Array
optional outputFloat32Array
Return ValueFloat32Array

inverse(input, output)

inverse(input: Float64Array, output?: Float64Array): Float64Array;
NameTypeDescription
inputFloat64Array
optional outputFloat64Array
Return ValueFloat64Array

MaxFFT.isFastSize(size, type) static

Check whether a size is a natively fast transform size.

Fast sizes factor as 2^a * 3^b * 5^c and meet the minimum for the given transform type (see MaxFFT.minSize()); these run on pffft's native SIMD path. This is not a constructibility check -- every size from 1 to 67108864 (2^26) constructs successfully (see the MaxFFT class remarks) regardless of what this returns -- it only tells you whether construction lands on the fast native path or the Bluestein (chirp-z) fallback, which is exact but roughly 5-8x slower at a given size.

static isFastSize(size: number, type?: "real" | "complex"): boolean;
NameTypeDescription
sizenumberThe size to check.
optional type"real" | "complex"The transform type to check against; defaults to "real".
Return Valueboolean

length number read-only

The number of elements every time-domain array must have: size for real transforms, 2 * size for complex transforms (interleaved re/im pairs). This is the length MaxFFT.forward expects for its input and MaxFFT.inverse produces as its output, regardless of MaxFFT.spectrum. Read-only.

MaxFFT.minSize(type) static

The minimum valid transform size for a transform type.

static minSize(type?: "real" | "complex"): number;
NameTypeDescription
optional type"real" | "complex"The transform type; defaults to "real".
Return Valuenumber

MaxFFT.nearestFastSize(size, type, higher) static

Find the nearest natively-fast transform size to an arbitrary size.

Use this for padding guidance when you specifically want the native SIMD path (see MaxFFT.isFastSize()) rather than the Bluestein fallback -- for example to trade a little zero-padding for a transform roughly 5-8x faster than running the untouched size through Bluestein. It is not needed just to make a size constructible: every size up to 67108864 (2^26) already is, native-fast or not (see the MaxFFT class remarks).

static nearestFastSize(
  size: number,
  type?: "real" | "complex",
  higher?: boolean,
): number;
NameTypeDescription
sizenumberThe desired size.
optional type"real" | "complex"The transform type; defaults to "real".
optional higherbooleanWhen true (the default), return the nearest fast size at or above size; when false, the nearest at or below.
Return Valuenumber

normalize boolean read-only

Whether MaxFFT.inverse scales its output by 1/size. Set at construction time. Read-only.

precision "float32" | "float64" read-only

The numeric precision: "float32" or "float64". Read-only.

size number read-only

The transform size, as passed to the constructor. Read-only.

spectrum "packed" | "unpacked" read-only

The spectrum layout used by real transforms: "packed" (the default) or "unpacked". Always "packed" for complex transforms, which have only one layout. Read-only.

spectrumLength number read-only

The number of elements a spectrum has: the length MaxFFT.forward produces and MaxFFT.inverse expects as input. Equals 2 * size for complex transforms, size for real transforms with spectrum: "packed" (the default, even size only), and 2 * (floor(size / 2) + 1) for real transforms with spectrum: "unpacked" (size + 2 when size is even, size + 1 when size is odd -- odd sizes have no distinct Nyquist bin). Read-only.

type "real" | "complex" read-only

The transform type: "real" or "complex". Read-only.