class MaxFFT2D

Two-dimensional Fast Fourier Transform processor

MaxFFT2D provides a native, row/column-decomposed 2-D FFT of real or complex grids, in single or double precision: every row is transformed (length cols), the grid is transposed, every column is transformed (length rows, always as a complex transform regardless of type), and the grid is transposed back. It is backed by the same FFT library as MaxFFT and the fft~ family of MSP objects, and is a natural sibling to MaxFFT for two-dimensional (e.g. image) data -- use MaxFFT directly for 1-D signals.

All data is row-major: a real rows x cols grid is rows * cols elements with row r, column c at index r * cols + c; a complex grid is the same indexing scheme with each element replaced by an interleaved (re, im) pair, i.e. row r, column c at indices (r * cols + c) * 2 / (r * cols + c) * 2 + 1.

Like MaxFFT, MaxFFT2D operates on plain JavaScript Arrays (values are copied in and out) or on TypedArrays (Float32Array / Float64Array matching MaxFFT2D.precision). TypedArray data need not be specially aligned for correctness: MaxFFT2D.forward and MaxFFT2D.inverse always produce correct results regardless of alignment. Alignment only affects performance, and only for one of the two buffers on each call -- MaxFFT2D.forward's input and MaxFFT2D.inverse's output are read/written a row at a time by the underlying row transform, so an unaligned row costs a small internal scratch copy; MaxFFT2D.forward's output and MaxFFT2D.inverse's input are only ever touched by a plain element-copy transpose, so their alignment never matters, not even for performance. Use MaxFFT.alloc to obtain a buffer guaranteed to take the fast (no-copy) path for whichever argument that is.

Like MaxFFT, MaxFFT2D accepts ANY dimensions from 1 up to 67108864 (2^26) -- every (rows, cols) pair in that range constructs successfully, and every transform is exact. Dimensions that factor as 2^a * 3^b * 5^c run natively at full SIMD speed -- cols judged against type (the row transform), rows always as a complex size, because the column pass is always complex regardless of type. Any other dimension transparently falls back to an exact Bluestein (chirp-z) engine for the passes along that axis, at roughly 5-8x the running time of a comparable fast size. Use MaxFFT2D.isFastSize() to check whether a (rows, cols) pair lands entirely on the native path, and MaxFFT2D.nearestFastSize() to find a nearby pair that does, when you want to trade a little padding for the fastest possible transform.

Performance note: constructing a MaxFFT2D allocates native work grids proportional to rows * cols -- tens of megabytes at large sizes, so call MaxFFT2D.dispose() when an instance is no longer needed -- 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 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. 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 rows = 64,
  cols = 64;
const fft2d = new MaxFFT2D(rows, cols, { type: "real", normalize: true });
const image = new Float32Array(rows * cols);
for (let r = 0; r < rows; r++) {
  for (let c = 0; c < cols; c++) {
    image[r * cols + c] = Math.sin((2 * Math.PI * 3 * c) / cols);
  }
}
const spectrum = fft2d.forward(image);
// spectrum has shape (rows, cols / 2 + 1) complex bins, interleaved:
const bin = (r, k) => [
  spectrum[(r * (cols / 2 + 1) + k) * 2],
  spectrum[(r * (cols / 2 + 1) + k) * 2 + 1],
];
const restored = fft2d.inverse(spectrum); // equals image (normalize: true)
fft2d.dispose();

Index

Constructors

Properties

Methods

new MaxFFT2D(rows, cols, options)

new MaxFFT2D(rows: number, cols: number, options?: MaxFFT2DOptions);

Create a 2-D FFT processor for a fixed grid size.

rows and cols may each be any integer from 1 to 67108864 (2^26); a dimension outside that range throws a RangeError. Dimensions that factor as 2^a * 3^b * 5^c (see MaxFFT2D.isFastSize()) run natively at full SIMD speed; every other in-range dimension still works, exactly, via an internal Bluestein (chirp-z) fallback for the passes along that axis (see the class-level remarks). Allocation or internal setup failure throws an Error.

ParameterTypeDescription
rowsnumberNumber of rows (the column-pass transform length).
colsnumberNumber of columns (the row-pass transform length).
optional optionsMaxFFT2DOptionsTransform type, precision, and normalization; see MaxFFT2DOptions.

cols number read-only

The column count, as passed to the constructor. Read-only.

dispose()

Free the native FFT state immediately.

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

MaxFFT2D 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 MaxFFT2D.dispose() has been called. Read-only.

forward(input)

Perform the forward transform.

The input must have exactly MaxFFT2D.length elements and the output (if given, or the value returned otherwise) has exactly MaxFFT2D.spectrumLength elements. For complex transforms these are equal, so output may alias input to transform in place; for real transforms they differ, so in-place operation is not possible and passing the same array as both throws a TypeError (the same restriction MaxFFT documents for spectrum: "unpacked" real transforms).

A plain Array input returns a new plain Array (values are copied). A TypedArray input must match this instance's MaxFFT2D.precision (Float32Array for "float32", Float64Array for "float64"); the result is written into output if given (which is also returned), or into a newly allocated TypedArray otherwise. An explicit output is not allowed with a plain Array input.

For real transforms, the output is rows rows of Math.floor(cols / 2) + 1 complex bins in the numpy/CCS convention (matching numpy's rfft2): row r's bins are [DC, 0, re1, im1, ..., re_{w-2}, im_{w-2}, Nyquist, 0] where w = Math.floor(cols / 2) + 1, starting at interleaved index r * w * 2. out[r * w * 2] / out[r * w * 2 + cols] are the (real-valued) DC / Nyquist components of row r's row-transform, and their formally-zero imaginary parts always read back as exactly 0. Note that only the row (cols) axis is halved this way -- the column (rows) axis is always a full complex transform (see MaxFFT2D for why), so the spectrum's row count is rows, not rows / 2 + 1.

For complex transforms the output is simply all rows * cols complex bins in interleaved row-major order.

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

forward(input: number[]): number[];
NameTypeDescription
inputnumber[]The image to transform (row-major; real or interleaved complex per MaxFFT2D.type).
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 MaxFFT2D.forward produces -- input must have MaxFFT2D.spectrumLength elements and the output has MaxFFT2D.length elements -- and returns the time-domain image. Argument conventions (array kinds, the optional output, in-place operation) are otherwise the same as for MaxFFT2D.forward: real transforms have differing input/output lengths and so cannot operate in place.

For real transforms, the formally-zero imaginary slots of each row's DC and Nyquist bins are ignored on input and expected to be 0, matching what MaxFFT2D.forward produces.

If the instance was constructed with normalize: true the output is scaled by 1 / (rows * cols), making inverse(forward(x)) reproduce x. Otherwise the round trip yields (rows * cols) * 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 image, 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

MaxFFT2D.isFastSize(rows, cols, type) static

Check whether a (rows, cols) pair is a natively fast 2-D transform size.

True when BOTH dimensions land on the native (non-Bluestein) path: cols is checked against type (the row transform); rows is always checked as a complex transform size, because the column pass is always complex regardless of type -- see MaxFFT2D for the full rationale. Fast dimensions factor as 2^a * 3^b * 5^c and meet the per-type minimum, same as MaxFFT.isFastSize(). This is not a constructibility check -- every (rows, cols) pair from 1 to 67108864 (2^26) constructs successfully regardless of what this returns -- it only tells you whether construction lands entirely on the fast native path or partly on the Bluestein (chirp-z) fallback, which is exact but roughly 5-8x slower for the passes along the affected axis.

static isFastSize(
  rows: number,
  cols: number,
  type?: "real" | "complex",
): boolean;
NameTypeDescription
rowsnumberThe row count to check.
colsnumberThe column count to check.
optional type"real" | "complex"The transform type to check cols against; defaults to "real".
Return Valueboolean

length number read-only

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

MaxFFT2D.nearestFastSize(rows, cols, type, higher) static

Find the nearest natively-fast 2-D transform size to an arbitrary (rows, cols) pair.

Each dimension snaps independently, under the same rules MaxFFT2D.isFastSize() checks: rows as a complex size (the column pass is always complex), cols against type. Use this for padding guidance when you specifically want the fully-native path rather than the Bluestein fallback. It is not needed just to make a size constructible: every dimension up to 67108864 (2^26) already is (see the MaxFFT2D class remarks).

static nearestFastSize(
  rows: number,
  cols: number,
  type?: "real" | "complex",
  higher?: boolean,
): [number, number];
NameTypeDescription
rowsnumberThe desired row count.
colsnumberThe desired column count.
optional type"real" | "complex"The transform type cols is judged against; defaults to "real".
optional higherbooleanWhen true (the default), snap each dimension to the nearest fast size at or above it; when false, at or below.
Return Value[number, number]The snapped pair as a two-element [rows, cols] array.

normalize boolean read-only

Whether MaxFFT2D.inverse scales its output by 1 / (rows * cols). Set at construction time. Read-only.

precision "float32" | "float64" read-only

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

rows number read-only

The row count, as passed to the constructor. Read-only.

spectrumLength number read-only

The number of elements a spectrum has: the length MaxFFT2D.forward produces and MaxFFT2D.inverse expects as input. Equals MaxFFT2D.length for complex transforms. For real transforms it equals rows * (Math.floor(cols / 2) + 1) * 2 -- rows rows of Math.floor(cols / 2) + 1 complex bins each, interleaved; unlike MaxFFT, there is no packed alternative layout for the 2-D case (see MaxFFT2D.forward). Read-only.

type "real" | "complex" read-only

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