Created
August 16, 2020 18:50
-
-
Save pschichtel/2a3f0dcc14678d75ba345595c276b607 to your computer and use it in GitHub Desktop.
A simple FFT (fast fourier transform) implementation in browser JS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Fourier</title> | |
<meta charset="UTF-8"> | |
</head> | |
<body> | |
<textarea></textarea> | |
<table> | |
<thead> | |
<tr> | |
<th>Frequency [Hz]</th> | |
<th>Weight</th> | |
</tr> | |
</thead> | |
<tbody id="sink"> | |
</tbody> | |
</table> | |
<script> | |
function cadd(a, b) { | |
let [ar, ai] = a; | |
let [br, bi] = b; | |
return [ar + br, ai + bi]; | |
} | |
function csub(a, b) { | |
let [ar, ai] = a; | |
let [br, bi] = b; | |
return [ar - br, ai - bi]; | |
} | |
function cmul(a, b) { | |
let [ar, ai] = a; | |
let [br, bi] = b; | |
return [ar * br - ai * bi, ar * bi + ai * br]; | |
} | |
function cexp(a) { | |
let [real, imaginary] = a; | |
let realExp = Math.exp(real); | |
return [realExp * Math.cos(imaginary), realExp * Math.sin(imaginary)]; | |
} | |
function cmag(a) { | |
let [real, imaginary] = a; | |
return Math.sqrt(real * real + imaginary * imaginary); | |
} | |
function separate(data, m, n) { | |
var b = []; | |
for (var i = 0; i < (n - m) / 2; i++) // copy all odd elements to b | |
b[i] = data[m + i * 2 + 1]; | |
for (var i = 0; i < (n - m) / 2; i++) // copy all even elements to lower-half of a | |
data[m + i] = data[m + i * 2]; | |
for (var i = 0; i < (n - m) / 2; i++) // copy all odd (from b) to upper-half of a[] | |
data[m + i + (n - m) / 2] = b[i]; | |
} | |
function fft(data, m, n) { | |
if (n - m < 2) return; | |
separate(data, m, n); // all evens to lower half, all odds to upper half | |
fft(data, m, m + (n - m) / 2); // recurse even items | |
fft(data, m + (n - m) / 2, n); // recurse odd items | |
for (var k = 0; k < (n - m) / 2; k++)// combine results of two half recursions | |
{ | |
var e = data[m + k]; // even | |
var o = data[m + k + (n - m) / 2]; // odd | |
var w = cexp([0, -2 * Math.PI * k / (n - m)]) // w is the "twiddle-factor" | |
data[m + k] = cadd(e, cmul(w, o)); | |
data[m + k + (n - m) / 2] = csub(e, cmul(w, o)); | |
} | |
return data; | |
} | |
let textArea = document.querySelector("textarea"); | |
let sink = document.getElementById("sink"); | |
textArea.addEventListener("change", e => { | |
let numbers = e.target.value.trim().split('\n').map(num => [parseInt(num.trim().replace(",", ".")), 0]); | |
let sampleRate = 10; | |
fft(numbers, 0, numbers.length); | |
for (let i = 0; i <= (numbers.length / 2); ++i) { | |
let magnitude = cmag(numbers[i]); | |
let frequency = i * (sampleRate/numbers.length); | |
let weight = magnitude; | |
let row = document.createElement("tr"); | |
let freqContainer = document.createElement("td"); | |
freqContainer.innerText = frequency; | |
let weightContainer = document.createElement("td"); | |
weightContainer.innerText = weight; | |
row.appendChild(freqContainer); | |
row.appendChild(weightContainer); | |
sink.appendChild(row); | |
} | |
alert(JSON.stringify(numbers.map(cmag))); | |
}) | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment