@emnudge/wat-fft 0.1.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,17 +6,18 @@ A high-performance FFT implementation in WebAssembly Text format that **signific
6
6
 
7
7
  ### Complex FFT
8
8
 
9
- Benchmarked against [fft.js](https://github.com/indutny/fft.js) (fastest pure-JS FFT):
10
-
11
- | Size | wat-fft (f64) | fft.js | Speedup |
12
- | ------ | ------------------- | --------------- | -------- |
13
- | N=64 | **3,830,000 ops/s** | 2,794,000 ops/s | **1.4x** |
14
- | N=128 | **1,586,000 ops/s** | 1,105,000 ops/s | **1.4x** |
15
- | N=256 | **973,000 ops/s** | 559,000 ops/s | **1.7x** |
16
- | N=512 | **344,000 ops/s** | 223,000 ops/s | **1.5x** |
17
- | N=1024 | **191,000 ops/s** | 113,000 ops/s | **1.7x** |
18
- | N=2048 | **74,500 ops/s** | 47,200 ops/s | **1.6x** |
19
- | N=4096 | **44,400 ops/s** | 23,400 ops/s | **1.9x** |
9
+ Benchmarked against [pffft-wasm](https://www.npmjs.com/package/@echogarden/pffft-wasm) (PFFFT with SIMD):
10
+
11
+ | Size | wat-fft (f32) | pffft-wasm (f32) | Speedup |
12
+ | ------ | -------------------- | ---------------- | -------- |
13
+ | N=16 | **16,700,000 ops/s** | 13,900,000 ops/s | **+20%** |
14
+ | N=64 | **6,040,000 ops/s** | 4,440,000 ops/s | **+36%** |
15
+ | N=128 | **3,040,000 ops/s** | 1,950,000 ops/s | **+56%** |
16
+ | N=256 | **1,640,000 ops/s** | 980,000 ops/s | **+67%** |
17
+ | N=512 | **736,000 ops/s** | 404,000 ops/s | **+82%** |
18
+ | N=1024 | **365,000 ops/s** | 201,000 ops/s | **+81%** |
19
+ | N=2048 | **163,000 ops/s** | 84,000 ops/s | **+94%** |
20
+ | N=4096 | **81,000 ops/s** | 41,000 ops/s | **+95%** |
20
21
 
21
22
  ```mermaid
22
23
  ---
@@ -26,35 +27,36 @@ config:
26
27
  height: 400
27
28
  themeVariables:
28
29
  xyChart:
29
- plotColorPalette: "#4ade80, #60a5fa, #a855f7, #f87171"
30
+ plotColorPalette: "#4ade80, #60a5fa, #f59e0b, #a855f7, #f87171"
30
31
  ---
31
32
  xychart-beta
32
33
  title "Complex FFT Performance (Million ops/s)"
33
- x-axis [N=64, N=128, N=256, N=512, N=1024, N=2048, N=4096]
34
- y-axis "Million ops/s" 0 --> 5
35
- line [3.83, 1.59, 0.97, 0.34, 0.19, 0.074, 0.044]
36
- line [4.60, 2.40, 1.17, 0.54, 0.27, 0.124, 0.062]
37
- line [2.79, 1.11, 0.56, 0.22, 0.11, 0.047, 0.023]
38
- line [1.90, 0.80, 0.45, 0.18, 0.10, 0.041, 0.022]
34
+ x-axis [N=16, N=64, N=128, N=256, N=512, N=1024, N=2048, N=4096]
35
+ y-axis "Million ops/s" 0 --> 18
36
+ line [17.57, 3.83, 1.74, 0.96, 0.37, 0.19, 0.080, 0.044]
37
+ line [16.68, 6.04, 3.04, 1.64, 0.74, 0.36, 0.163, 0.081]
38
+ line [13.88, 4.44, 1.95, 0.98, 0.40, 0.20, 0.084, 0.041]
39
+ line [11.50, 2.80, 1.07, 0.56, 0.22, 0.11, 0.047, 0.023]
40
+ line [6.05, 1.86, 0.80, 0.44, 0.18, 0.10, 0.041, 0.022]
39
41
  ```
40
42
 
41
- > 🟢 **wat-fft f64** · 🔵 **wat-fft f32** · 🟣 **fft.js** · 🔴 **kissfft-js**
43
+ > 🟢 **wat-fft f64** · 🔵 **wat-fft f32** · 🟠 **pffft-wasm** · 🟣 **fft.js** · 🔴 **kissfft-js**
42
44
 
43
- **Choose f64** (`fft_combined.wasm`) for double precision - **1.4-1.9x faster** than fft.js at all sizes. **Choose f32** (`fft_stockham_f32_dual.wasm`) for maximum speed with single precision - up to **2.6x faster** than fft.js.
45
+ **wat-fft f32 beats pffft-wasm by 20-95%** across all sizes. It's also **2-3x faster** than fft.js (the fastest pure JS). **Choose f64** (`fft_combined.wasm`) for double precision. **Choose f32** (`fft_stockham_f32_dual.wasm`) for maximum single-precision speed.
44
46
 
45
47
  ### Real FFT
46
48
 
47
- Benchmarked against [fftw-js](https://www.npmjs.com/package/fftw-js) (Emscripten port of FFTW):
49
+ Benchmarked against [pffft-wasm](https://www.npmjs.com/package/@echogarden/pffft-wasm) and [fftw-js](https://www.npmjs.com/package/fftw-js):
48
50
 
49
- | Size | wat-fft (f32) | fftw-js (f32) | Comparison |
50
- | ------ | ------------------- | --------------- | ---------- |
51
- | N=64 | **6,700,000 ops/s** | 6,620,000 ops/s | **+1%** |
52
- | N=128 | **4,290,000 ops/s** | 4,100,000 ops/s | **+5%** |
53
- | N=256 | **2,170,000 ops/s** | 1,430,000 ops/s | **+51%** |
54
- | N=512 | **1,130,000 ops/s** | 870,000 ops/s | **+31%** |
55
- | N=1024 | **525,000 ops/s** | 444,000 ops/s | **+18%** |
56
- | N=2048 | **264,000 ops/s** | 225,000 ops/s | **+18%** |
57
- | N=4096 | **116,000 ops/s** | 105,000 ops/s | **+11%** |
51
+ | Size | wat-fft (f32) | pffft-wasm (f32) | fftw-js (f32) | vs best |
52
+ | ------ | ------------------- | ------------------- | --------------- | ----------- |
53
+ | N=64 | 6,640,000 ops/s | **6,970,000 ops/s** | 6,660,000 ops/s | -5% (pffft) |
54
+ | N=128 | **4,510,000 ops/s** | 3,490,000 ops/s | 4,290,000 ops/s | **+5%** |
55
+ | N=256 | **2,280,000 ops/s** | 1,920,000 ops/s | 1,440,000 ops/s | **+19%** |
56
+ | N=512 | **1,110,000 ops/s** | 830,000 ops/s | 850,000 ops/s | **+31%** |
57
+ | N=1024 | **531,000 ops/s** | 419,000 ops/s | 458,000 ops/s | **+16%** |
58
+ | N=2048 | **274,000 ops/s** | 179,000 ops/s | 222,000 ops/s | **+23%** |
59
+ | N=4096 | **126,000 ops/s** | 89,000 ops/s | 106,000 ops/s | **+19%** |
58
60
 
59
61
  ```mermaid
60
62
  ---
@@ -64,21 +66,22 @@ config:
64
66
  height: 400
65
67
  themeVariables:
66
68
  xyChart:
67
- plotColorPalette: "#4ade80, #60a5fa, #f87171, #a855f7"
69
+ plotColorPalette: "#4ade80, #60a5fa, #f87171, #f59e0b, #a855f7"
68
70
  ---
69
71
  xychart-beta
70
72
  title "Real FFT Performance (Million ops/s)"
71
73
  x-axis [N=64, N=128, N=256, N=512, N=1024, N=2048, N=4096]
72
74
  y-axis "Million ops/s" 0 --> 8
73
- line [4.80, 2.99, 1.28, 0.76, 0.27, 0.16, 0.062]
74
- line [6.70, 4.29, 2.17, 1.13, 0.525, 0.264, 0.116]
75
- line [6.62, 4.10, 1.43, 0.87, 0.444, 0.225, 0.105]
76
- line [2.93, 1.74, 0.75, 0.42, 0.17, 0.094, 0.039]
75
+ line [4.70, 2.95, 1.28, 0.76, 0.29, 0.16, 0.063]
76
+ line [6.64, 4.51, 2.28, 1.11, 0.53, 0.27, 0.126]
77
+ line [6.66, 4.29, 1.44, 0.85, 0.46, 0.22, 0.106]
78
+ line [6.97, 3.49, 1.92, 0.83, 0.42, 0.18, 0.089]
79
+ line [2.93, 1.79, 0.76, 0.42, 0.17, 0.094, 0.039]
77
80
  ```
78
81
 
79
- > 🟢 **wat-fft f64** · 🔵 **wat-fft f32** · 🔴 **fftw-js** · 🟣 **kissfft-js**
82
+ > 🟢 **wat-fft f64** · 🔵 **wat-fft f32** · 🔴 **fftw-js** · 🟠 **pffft-wasm** · 🟣 **kissfft-js**
80
83
 
81
- **wat-fft f32 beats fftw-js at all sizes** (+1% to +51%). **Choose f64** (`fft_real_combined.wasm`) for double precision. **Choose f32** (`fft_real_f32_dual.wasm`) for maximum single-precision speed.
84
+ **wat-fft f32 beats all competitors at N≥128** (+5% to +31%). At N=64, pffft-wasm has a slight edge. **Choose f64** (`fft_real_combined.wasm`) for double precision. **Choose f32** (`fft_real_f32_dual.wasm`) for maximum single-precision speed.
82
85
 
83
86
  ## Quick Start
84
87
 
@@ -110,9 +113,9 @@ cargo install wasm-tools
110
113
  ```javascript
111
114
  import fs from "fs";
112
115
 
113
- // Load the WASM module (Stockham is recommended for best performance)
116
+ // Load the WASM module
114
117
  // No JavaScript imports needed - trig functions are computed inline
115
- const wasmBuffer = fs.readFileSync("dist/combined_stockham.wasm");
118
+ const wasmBuffer = fs.readFileSync("dist/fft_combined.wasm");
116
119
  const wasmModule = await WebAssembly.compile(wasmBuffer);
117
120
  const instance = await WebAssembly.instantiate(wasmModule);
118
121
  const fft = instance.exports;
@@ -127,22 +130,29 @@ for (let i = 0; i < N; i++) {
127
130
 
128
131
  // Compute FFT
129
132
  fft.precompute_twiddles(N);
130
- fft.fft_stockham(N);
133
+ fft.fft(N);
131
134
 
132
135
  // Results are in-place in data[]
133
136
  console.log("DC component:", data[0], data[1]);
137
+
138
+ // Compute inverse FFT (roundtrip back to original)
139
+ fft.ifft(N);
140
+ console.log("Recovered signal:", data[0], data[1]);
134
141
  ```
135
142
 
136
143
  ## Implementations
137
144
 
138
145
  **Recommended modules:**
139
146
 
140
- | Module | Use Case | Precision |
141
- | ---------------------------- | ---------------------- | --------- |
142
- | `fft_combined.wasm` | Complex FFT (any size) | f64 |
143
- | `fft_real_combined.wasm` | Real FFT (any size) | f64 |
144
- | `fft_stockham_f32_dual.wasm` | Complex FFT (fastest) | f32 |
145
- | `fft_real_f32_dual.wasm` | Real FFT (fastest) | f32 |
147
+ | Module | Use Case | Precision | Inverse |
148
+ | ---------------------------- | -------------------------- | --------- | ------------ |
149
+ | `fft_combined.wasm` | Complex FFT (any size) | f64 | `ifft` |
150
+ | `fft_real_combined.wasm` | Real FFT (any size) | f64 | - |
151
+ | `fft_stockham_f32_dual.wasm` | Complex FFT (interleaved) | f32 | `ifft` |
152
+ | `fft_split_native_f32.wasm` | Complex FFT (split format) | f32 | `ifft_split` |
153
+ | `fft_real_f32_dual.wasm` | Real FFT (fastest) | f32 | `irfft` |
154
+
155
+ **Split-format** (`fft_split_native_f32.wasm`) stores real and imaginary parts in separate arrays, enabling 4 complex numbers per SIMD operation. Performance is similar to interleaved format - use when your data is already in split format to avoid conversion overhead.
146
156
 
147
157
  See [docs/IMPLEMENTATIONS.md](docs/IMPLEMENTATIONS.md) for detailed documentation of all modules, usage examples, and numerical accuracy information.
148
158
 
@@ -166,7 +176,6 @@ npm run bench:rfft # Run real FFT benchmarks
166
176
  npm run bench:rfft32 # Run f32 real FFT benchmarks
167
177
  npm run test:fft # Run comprehensive FFT tests
168
178
  npm run test:rfft # Run real FFT tests
169
- npm run test:permutation # Test permutation algorithms
170
179
  ```
171
180
 
172
181
  ## Development Tools
@@ -207,11 +216,11 @@ The comprehensive FFT test suite (`tests/fft.test.js`) tests all implementations
207
216
  npm run test:fft
208
217
  ```
209
218
 
210
- ### Test a single implementation (useful for debugging)
219
+ ### Test a specific size and pattern
211
220
 
212
221
  ```bash
213
- node tests/fft.test.js --impl stockham 64 random
214
- node tests/fft.test.js --impl fast 256 impulse
222
+ node tests/fft.test.js 64 random
223
+ node tests/fft.test.js 256 impulse
215
224
  ```
216
225
 
217
226
  ### Input patterns
@@ -223,7 +232,7 @@ node tests/fft.test.js --impl fast 256 impulse
223
232
 
224
233
  ### Test sizes
225
234
 
226
- Powers of 2: 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096
235
+ Powers of 2: 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192
227
236
 
228
237
  ## License
229
238
 
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,48 +1,50 @@
1
1
  {
2
2
  "name": "@emnudge/wat-fft",
3
- "version": "0.1.0",
3
+ "version": "0.4.0",
4
4
  "description": "High-performance FFT in WebAssembly",
5
- "license": "ISC",
6
- "author": "EmNudge",
7
- "repository": {
8
- "type": "git",
9
- "url": "https://github.com/emnudge/wat-fft"
10
- },
11
5
  "keywords": [
6
+ "audio",
7
+ "dsp",
12
8
  "fft",
13
- "rfft",
14
9
  "fourier",
10
+ "rfft",
11
+ "signal-processing",
15
12
  "transform",
16
13
  "wasm",
17
- "webassembly",
18
- "dsp",
19
- "signal-processing",
20
- "audio"
14
+ "webassembly"
21
15
  ],
22
- "type": "module",
23
- "exports": {
24
- ".": {
25
- "import": "./index.js"
26
- }
16
+ "license": "ISC",
17
+ "author": "EmNudge",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/EmNudge/wat-fft.git"
27
21
  },
28
22
  "files": [
29
23
  "index.js",
30
24
  "dist/*.wasm"
31
25
  ],
32
- "engines": {
33
- "node": ">=18"
26
+ "type": "module",
27
+ "exports": {
28
+ ".": {
29
+ "import": "./index.js"
30
+ }
34
31
  },
35
32
  "scripts": {
36
- "test": "npm run test:swap && npm run test:reverse_bits && npm run test:fft",
37
- "test:all": "npm run test && npm run test:rfft && npm run test:property && npm run test:boundary && npm run test:correctness && npm run test:output-order",
33
+ "test": "npm run test:fft && npm run test:combined && npm run test:ifft",
34
+ "test:all": "npm run test && npm run test:rfft && npm run test:boundary && npm run test:correctness && npm run test:output-order && npm run test:f32 && npm run test:twiddles && npm run test:golden && npm run test:perbin && npm run test:split && npm run test:thirdparty && npm run test:bench-correctness",
35
+ "test:split": "node --test tests/fft_split_native.test.js",
36
+ "test:thirdparty": "node --test tests/third-party-correctness.test.js",
37
+ "test:bench-correctness": "node --test tests/benchmark-correctness.test.js",
38
+ "test:ifft": "node tests/ifft.test.js",
39
+ "test:twiddles": "node tests/twiddle_validation.test.js",
40
+ "test:property": "node --test tests/property_based.test.js",
41
+ "test:golden": "node --test tests/golden_reference.test.js",
42
+ "test:perbin": "node --test tests/per_bin_validation.test.js",
38
43
  "test:output-order": "node --test tests/output-order.test.js",
39
- "test:swap": "node tests/swap.test.js",
40
- "test:reverse_bits": "node tests/reverse_bits.test.js",
41
- "test:permutation": "node tests/permutation.test.js",
42
44
  "test:fft": "node tests/fft.test.js",
45
+ "test:combined": "node tests/combined.test.js",
43
46
  "test:rfft": "node --test tests/rfft.test.js",
44
47
  "test:f32": "node tests/fft_f32_dual.test.js",
45
- "test:property": "node tests/fft.property.test.js",
46
48
  "test:boundary": "node --test tests/boundary.test.js",
47
49
  "test:butterfly": "node tools/butterfly_tester.js",
48
50
  "test:correctness": "node --test tests/correctness/*.test.js",
@@ -52,20 +54,28 @@
52
54
  "test:correctness:shift": "node --test tests/correctness/fft.shift.test.js",
53
55
  "test:correctness:reference": "node --test tests/correctness/fft.reference.test.js",
54
56
  "test:correctness:known": "node --test tests/correctness/fft.known-values.test.js",
55
- "debug:stockham": "node tools/wasm_compare.js",
57
+ "debug:compare": "node tools/wasm_compare.js",
56
58
  "debug:index": "node tools/index_visualizer.js",
57
59
  "debug:perm": "node tools/permutation_validator.js",
58
60
  "debug:ref": "node tools/stockham_reference.js",
61
+ "debug:split": "node tests/debug_split.js",
59
62
  "build": "node build.js",
60
63
  "bench": "node benchmarks/fft.bench.js",
61
64
  "bench:rfft": "node benchmarks/rfft.bench.js",
62
65
  "bench:f32": "node benchmarks/fft_f32_dual.bench.js",
63
66
  "bench:rfft32": "node benchmarks/rfft_f32_dual.bench.js",
67
+ "bench:browser": "vitest bench --run",
68
+ "bench:browser:ci": "vitest bench --run --outputJson=benchmark-results.json",
69
+ "bench:check": "node scripts/check-benchmarks.js",
64
70
  "lint": "oxlint .",
71
+ "lint:all": "npm run lint && npm run lint:wasm",
72
+ "lint:wasm": "node tools/lint-wasm-dead-code.js",
65
73
  "format": "oxfmt --write .",
66
74
  "format:check": "oxfmt --check ."
67
75
  },
68
76
  "devDependencies": {
77
+ "@echogarden/pffft-wasm": "^0.4.2",
78
+ "@vitest/browser": "^3.0.0",
69
79
  "fast-check": "^4.5.3",
70
80
  "fft-js": "^0.0.12",
71
81
  "fft.js": "^4.0.4",
@@ -73,6 +83,13 @@
73
83
  "kissfft-js": "^0.1.8",
74
84
  "kissfft-wasm": "^2.0.1",
75
85
  "oxfmt": "^0.26.0",
76
- "oxlint": "^1.41.0"
86
+ "oxlint": "^1.41.0",
87
+ "playwright": "^1.50.0",
88
+ "typescript": "^5.7.0",
89
+ "vitest": "^3.0.0",
90
+ "webfft": "^1.0.3"
91
+ },
92
+ "engines": {
93
+ "node": ">=18"
77
94
  }
78
95
  }
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/dist/swap.wasm DELETED
Binary file