Godot 4 > Fast Fourier Transform (FFT)
Fast Fourier Transform in GDScript.
Table of Contents
When to use
Pure GDScript, no dependencies, works everywhere. If you need performance and already use NumDot, use nd.fft() instead.
Installation
- Install addon
- Enable plugin from project settings
The singleton FFT is autoloaded at project start.
Usage
Input can be a plain Array of floats or a PackedFloat64Array (interleaved [re0, im0, re1, im1, ...]).
Output is always PackedFloat64Array.
var result = FFT.fft([1, 1, 1, 1, 0, 0, 0, 0])
print(FFT.pretty(result))
var recovered = FFT.ifft(FFT.fft([1, 1, 1, 1, 0, 0, 0, 0]))
print(FFT.to_reals(recovered))
Notes
fft and ifft modify the input PackedFloat64Array in-place for speed.
Pass a duplicate if you need the original preserved:
var result = FFT.fft(my_packed.duplicate())
Thread safe. Twiddle factor cache uses a mutex on first population per size, after which reads are lock-free. Callers must ensure input arrays are not shared across threads.
var _thread := Thread.new()
var _terminated := false
var _mutex := Mutex.new()
var _data: PackedFloat64Array
func _ready() -> void:
_thread.start(_thread_runner)
func _exit_tree() -> void:
_terminated = true
_thread.wait_to_finish()
func _thread_runner() -> void:
while not _terminated:
OS.delay_msec(100)
_mutex.lock()
FFT.fft(_data)
_mutex.unlock()
func _process(_delta: float) -> void:
if not _mutex.try_lock():
return
print(FFT.pretty(_data))
_mutex.unlock()
Performance
fft(1024) x100: 127399 us total, 1274.0 us/call
For per-frame use at 60fps, N<=256 is recommended.
Demo
A real-time audio spectrum visualizer is included in /demo.
It demonstrates FFT-based frequency analysis of a playing audio stream, rendered as a bar spectrum with log-spaced frequency bins and dB scaling.
How it works
AudioPlayerdecodes the WAV stream to a mono PCM buffer and slices a sample window aligned to the current playback position, compensating for hardware output latencydemo.gdruns the FFT each frame, maps bins to log-spaced frequency bands, converts magnitude to dB, and smooths the result over timeSpectrumrenders the smoothed values as colored bars scaled to the control's size
Running the demo
- Open
/demo/demo.tscn - Assign a WAV file (PCM, any sample rate) to the
AudioStreamPlayernode - Run the scene
Tuning
| Constant | Location | Effect |
|---|---|---|
DB_FLOOR |
demo.gd |
Sensitivity: lower values (-60) show more, higher (-20) gate quiet signals |
SMOOTHING |
demo.gd |
Temporal smoothing: lower is snappier, higher is silkier |
FFT_SIZE |
demo.gd |
Frequency resolution vs. CPU cost: must be power of 2, ≤256 recommended at 60fps |
BAR_COUNT |
demo.gd |
Number of frequency bands displayed |
Reference
Public methods
FFT.fft(data: Array | PackedFloat64Array) -> PackedFloat64Array
Forward transformation from data-space into frequency-space.
FFT.ifft(data: Array | PackedFloat64Array) -> PackedFloat64Array
Reverse transformation from frequency-space into data-space.
FFT.to_packed(reals: Array) -> PackedFloat64Array
Converts a real-valued Array to interleaved complex PackedFloat64Array.
FFT.to_reals(data: PackedFloat64Array) -> PackedFloat64Array
Extracts the real components from an interleaved complex array.
FFT.pretty(data: PackedFloat64Array) -> String
Returns a human-readable string of complex values for debugging.