Skip to content

Instantly share code, notes, and snippets.

@pschichtel
Created August 16, 2020 18:50
Show Gist options
  • Save pschichtel/2a3f0dcc14678d75ba345595c276b607 to your computer and use it in GitHub Desktop.
Save pschichtel/2a3f0dcc14678d75ba345595c276b607 to your computer and use it in GitHub Desktop.
A simple FFT (fast fourier transform) implementation in browser JS
<!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