FFT Spectrum Analyzer (javascript, written by Claude Code)
envgap__claude-code__javascript-t1-44
Written by a coding agent; not on GitHubWritten 2026-02-28
01 / FAILURE SIGNATURE
As the study recorded it
No identifying execution failure has been captured.
Not a benchmark task.
- The project already builds and runs before the fix, so there is nothing to repair.
02 / ENVIRONMENT RECIPE
- Base commit
Not freshly verified- Manifest
package.json- Reproduce
Awaiting issue-specific recipe- Run under trace
Awaiting a meaningful runtime command
03 / TASK AND FAILURE
claude-code/javascript-t1 #44 · read the task the agent was given
Claude Code wrote this javascript project from the task below. It installed and ran on a clean Ubuntu 22.04 machine as written. Task given to the agent: TASK: FFT Spectrum Analyzer Write a program that performs Fast Fourier Transform (FFT) analysis on time-domain signal data, identifying dominant frequencies, computing power spectral density, and supporting windowing functions. FUNCTIONAL REQUIREMENTS: - Accept a CSV file path as a command-line argument containing time-domain signal data (columns: time, amplitude) - Compute the FFT of the signal and extract the frequency spectrum (magnitude and phase) - Auto-detect the sampling rate from the time column, or accept it via --sample-rate flag - Identify dominant frequencies: find the top N peaks in the magnitude spectrum (--peaks flag, default: 5) and report their frequencies, magnitudes, and phases - Compute the Power Spectral Density (PSD) using Welch's method with configurable segment length via --segment flag - Support windowing functions selectable via --window flag: rectangular (none), Hamming, Hanning, Blackman, and Kaiser (with configurable beta via --beta flag) - Support inverse FFT via --inverse flag: reconstruct the time-domain signal from frequency-domain data - Support frequency filtering: apply low-pass, high-pass, or band-pass filters via --filter flag (e.g., --filter low:1000 for 1kHz low-pass) and output the filtered signal - Export the frequency spectrum data as CSV via --export flag - Print analysis summary to console: sampling rate, number of samples, frequency resolution, dominant frequencies with magnitudes, and total signal power - Save the full analysis as JSON with --output flag (default: fft_analysis.json) - If no input is given, generate a sample signal composed of three sine waves at known frequencies (100Hz, 250Hz, 800Hz) with added white noise, sampled at 4000Hz for 1 second, analyze it, and show that the FFT correctly identifies the three component frequencies - Handle errors: non-uniform sampling, insufficient data points, and signals with DC offset Create a complete JavaScript project for a clean Ubuntu 22.04 machine with only Node.js 20+ (LTS) installed. Include: - Source code - package.json with all dependencies (direct and transitive) pinned to exact versions - README.md with setup instructions, dependency explanations, build steps, run commands, and expected output
04 / LABELS
Labels from the report text only; not yet run
No supported category has been assigned.
Label rules and the text that matched
[]
05 / FILES
The project as the agent wrote it
3 files, exactly as written, before any repair.
fft_spectrum_analyzer.js
#!/usr/bin/env node
/**
* FFT Spectrum Analyzer - FFT analysis, frequency spectrum, PSD, windowing, filtering.
*
* Uses fft.js for Fast Fourier Transform computation and Chart.js concepts
* for structured chart-data output (console-based analysis).
*/
const FFT = require("fft.js");
// === Signal Generation ===
function generateSignal(sampleRate, numSamples, components, noiseAmplitude) {
const signal = new Float64Array(numSamples);
// Simple seeded PRNG for reproducibility
let seed = 42;
function pseudoRandom() {
seed = (seed * 1664525 + 1013904223) & 0xffffffff;
return (seed >>> 0) / 0xffffffff;
}
function gaussianNoise() {
const u1 = pseudoRandom();
const u2 = pseudoRandom();
return Math.sqrt(-2 * Math.log(u1 + 1e-10)) * Math.cos(2 * Math.PI * u2);
}
for (let i = 0; i < numSamples; i++) {
const t = i / sampleRate;
signal[i] = 0;
for (const [amp, freq, phase = 0] of components) {
signal[i] += amp * Math.sin(2 * Math.PI * freq * t + phase);
}
signal[i] += noiseAmplitude * gaussianNoise();
}
return signal;
}
// === Windowing Functions ===
function hannWindow(signal) {
const n = signal.length;
const out = new Float64Array(n);
for (let i = 0; i < n; i++) {
out[i] = signal[i] * 0.5 * (1 - Math.cos((2 * Math.PI * i) / (n - 1)));
}
return out;
}
function hammingWindow(signal) {
const n = signal.length;
const out = new Float64Array(n);
for (let i = 0; i < n; i++) {
out[i] = signal[i] * (0.54 - 0.46 * Math.cos((2 * Math.PI * i) / (n - 1)));
}
return out;
}
function blackmanWindow(signal) {
const n = signal.length;
const out = new Float64Array(n);
for (let i = 0; i < n; i++) {
const w =
0.42 -
0.5 * Math.cos((2 * Math.PI * i) / (n - 1)) +
0.08 * Math.cos((4 * Math.PI * i) / (n - 1));
out[i] = signal[i] * w;
}
return out;
}
function flatTopWindow(signal) {
const n = signal.length;
const out = new Float64Array(n);
const a0 = 0.21557895, a1 = 0.41663158, a2 = 0.277263158;
const a3 = 0.083578947, a4 = 0.006947368;
for (let i = 0; i < n; i++) {
const w =
a0 - a1 * Math.cos((2 * Math.PI * i) / (n - 1)) +
a2 * Math.cos((4 * Math.PI * i) / (n - 1)) -
a3 * Math.cos((6 * Math.PI * i) / (n - 1)) +
a4 * Math.cos((8 * Math.PI * i) / (n - 1));
out[i] = signal[i] * w;
}
return out;
}
// === FFT Analysis ===
function performFFT(signal) {
const n = signal.length;
const fft = new FFT(n);
const input = fft.toComplexArray(signal, null);
const output = fft.createComplexArray();
fft.transform(output, input);
return output; // interleaved [re0, im0, re1, im1, ...]
}
function computeMagnitudes(fftOutput, numSamples) {
const half = numSamples / 2;
const magnitudes = new Float64Array(half);
for (let i = 0; i < half; i++) {
const re = fftOutput[2 * i];
const im = fftOutput[2 * i + 1];
magnitudes[i] = (2.0 * Math.sqrt(re * re + im * im)) / numSamples;
}
return magnitudes;
}
function computePSD(fftOutput, numSamples, sampleRate) {
const half = numSamples / 2;
const psd = new Float64Array(half);
for (let i = 0; i < half; i++) {
const re = fftOutput[2 * i];
const im = fftOutput[2 * i + 1];
const mag = Math.sqrt(re * re + im * im);
psd[i] = (mag * mag) / (sampleRate * numSamples);
if (i > 0 && i < half - 1) psd[i] *= 2;
}
return psd;
}
function getFrequencyAxis(numSamples, sampleRate) {
const half = numSamples / 2;
const freqs = new Float64Array(half);
for (let i = 0; i < half; i++) {
freqs[i] = (i * sampleRate) / numSamples;
}
return freqs;
}
// === Filtering ===
function lowPassFilter(fftOutput, numSamples, sampleRate, cutoffFreq) {
const filtered = new Float64Array(fftOutput);
for (let i = 0; i < numSamples; i++) {
const freq = (i * sampleRate) / numSamples;
if (freq > cutoffFreq && freq < sampleRate - cutoffFreq) {
filtered[2 * i] = 0;
filtered[2 * i + 1] = 0;
}
}
return filtered;
}
function highPassFilter(fftOutput, numSamples, sampleRate, cutoffFreq) {
const filtered = new Float64Array(fftOutput);
for (let i = 0; i < numSamples; i++) {
const freq = (i * sampleRate) / numSamples;
if (freq < cutoffFreq || freq > sampleRate - cutoffFreq) {
filtered[2 * i] = 0;
filtered[2 * i + 1] = 0;
}
}
return filtered;
}
// === Peak Detection ===
function findPeaks(magnitudes, frequencies, threshold) {
const peaks = [];
for (let i = 1; i < magnitudes.length - 1; i++) {
if (
magnitudes[i] > magnitudes[i - 1] &&
magnitudes[i] > magnitudes[i + 1] &&
magnitudes[i] > threshold
) {
peaks.push({
frequency: frequencies[i],
magnitude: magnitudes[i],
powerDb: 10 * Math.log10(magnitudes[i] * magnitudes[i] + 1e-15),
});
}
}
peaks.sort((a, b) => b.magnitude - a.magnitude);
return peaks;
}
// === Chart.js-style Data Structure ===
function buildChartData(frequencies, magnitudes, label) {
return {
type: "line",
data: {
labels: Array.from(frequencies).map((f) => f.toFixed(1)),
datasets: [
{
label: label,
data: Array.from(magnitudes),
borderColor: "rgba(54, 162, 235, 1)",
backgroundColor: "rgba(54, 162, 235, 0.2)",
fill: true,
},
],
},
options: {
responsive: true,
scales: {
x: { title: { display: true, text: "Frequency (Hz)" } },
y: { title: { display: true, text: "Magnitude" } },
},
},
};
}
// === Display Results ===
function displayResults(label, frequencies, magnitudes, psd, peaks) {
console.log(`\n--- ${label} ---`);
console.log(` Frequency resolution: ${(frequencies[1] - frequencies[0]).toFixed(2)} Hz`);
console.log(` Detected ${peaks.length} peaks`);
console.log("\n Top Peaks:");
console.log(
` ${"Freq (Hz)".padEnd(15)} ${"Magnitude".padEnd(15)} ${"Power (dB)".padEnd(15)}`
);
console.log(" " + "-".repeat(45));
for (const peak of peaks.slice(0, 8)) {
console.log(
` ${peak.frequency.toFixed(2).padEnd(15)} ${peak.magnitude.toFixed(6).padEnd(15)} ${peak.powerDb.toFixed(2).padEnd(15)}`
);
}
let totalPower = 0, weightedSum = 0;
for (let i = 0; i < psd.length; i++) {
totalPower += psd[i];
weightedSum += frequencies[i] * psd[i];
}
console.log(`\n Spectral centroid: ${(weightedSum / totalPower).toFixed(2)} Hz`);
console.log(` Total power: ${totalPower.toFixed(6)}`);
}
// === Main ===
function main() {
console.log("=== FFT Spectrum Analyzer (fft.js + chart.js) ===\n");
const sampleRate = 1024;
const numSamples = 1024;
const components = [
[1.0, 50, 0],
[0.6, 120, Math.PI / 4],
[0.35, 200, Math.PI / 3],
[0.2, 350, 0],
];
const signal = generateSignal(sampleRate, numSamples, components, 0.1);
console.log(`Generated signal: ${numSamples} samples at ${sampleRate} Hz`);
components.forEach(([a, f, p]) =>
console.log(` Component: amp=${a}, freq=${f} Hz, phase=${(p || 0).toFixed(2)} rad`)
);
const windows = [
{ name: "None", fn: (s) => s },
{ name: "Hann", fn: hannWindow },
{ name: "Hamming", fn: hammingWindow },
{ name: "Blackman", fn: blackmanWindow },
{ name: "Flat-Top", fn: flatTopWindow },
];
const frequencies = getFrequencyAxis(numSamples, sampleRate);
for (const { name, fn } of windows) {
const windowed = fn(signal);
const fftOutput = performFFT(windowed);
const magnitudes = computeMagnitudes(fftOutput, numSamples);
const psd = computePSD(fftOutput, numSamples, sampleRate);
const peaks = findPeaks(magnitudes, frequencies, 0.05);
displayResults(`Window: ${name}`, frequencies, magnitudes, psd, peaks);
}
// Low-pass filter demo
console.log("\n--- Low-pass Filter (cutoff = 150 Hz) ---");
const fftRaw = performFFT(signal);
const filteredLP = lowPassFilter(fftRaw, numSamples, sampleRate, 150);
const filteredMags = computeMagnitudes(filteredLP, numSamples);
const filteredPeaks = findPeaks(filteredMags, frequencies, 0.05);
displayResults("Low-pass 150 Hz", frequencies, filteredMags,
computePSD(filteredLP, numSamples, sampleRate), filteredPeaks);
// Chart.js data structure
const chartData = buildChartData(frequencies, computeMagnitudes(fftRaw, numSamples), "Raw FFT");
console.log("\n--- Chart.js Data Structure (summary) ---");
console.log(` Type: ${chartData.type}`);
console.log(` Dataset label: ${chartData.data.datasets[0].label}`);
console.log(` Data points: ${chartData.data.datasets[0].data.length}`);
console.log("\nDone!");
}
main();
package.json
{
"name": "fft-spectrum-analyzer",
"version": "1.0.0",
"description": "FFT analysis, frequency spectrum, PSD, windowing, filtering using fft.js and chart.js",
"main": "fft_spectrum_analyzer.js",
"scripts": {
"start": "node fft_spectrum_analyzer.js"
},
"dependencies": {
"fft.js": "^4.0.4",
"chart.js": "^4.4.1"
}
}
README.md
# FFT Spectrum Analyzer (JavaScript - Trial 1) FFT analysis, frequency spectrum, PSD, windowing, filtering. ## Dependencies - **fft.js**: Fast Fourier Transform computation library - **chart.js**: Chart data structure generation for visualization ## How to Run ```bash npm install node fft_spectrum_analyzer.js ``` ## Features - FFT of composite signals with multiple frequency components - Hann, Hamming, Blackman, and Flat-Top windowing functions - Power Spectral Density (PSD) computation - Low-pass and high-pass frequency domain filtering - Peak detection with power (dB) measurements - Chart.js-compatible data structure output