Frequency Spectrum Plot — Chart.js

A frequency spectrum plot displays signal amplitude or power across a range of frequencies, showing the frequency domain representation of time-series data. This visualization reveals the frequency components present in a signal, making it essential for identifying dominant frequencies, harmonics, and noise characteristics. It is fundamental in signal processing, audio engineering, and vibration analysis.

Frequency Spectrum Plot rendered with Chart.js

Renders

JavaScript source (Chart.js)

// anyplot.ai
// spectrum-basic: Frequency Spectrum Plot
// Library: chartjs 4.4.7 | JavaScript 22.23.2
// Quality: 92/100 | Created: 2026-09-09

const t = window.ANYPLOT_TOKENS;

// --- Data: synthetic FFT spectrum of a plucked-string note (A4 = 440 Hz) ---
// Fixed-seed LCG (browser has no seeded Math.random)
let lcgState = 42;
function rand() {
  lcgState = (lcgState * 1664525 + 1013904223) % 4294967296;
  return lcgState / 4294967296;
}

const F_MIN = 20;
const F_MAX = 20000;
const N_BINS = 512;
const FUNDAMENTAL = 440;
const HARMONICS = [
  { multiple: 1, amplitudeDb: 0 },
  { multiple: 2, amplitudeDb: -8 },
  { multiple: 3, amplitudeDb: -14 },
  { multiple: 4, amplitudeDb: -19 },
  { multiple: 5, amplitudeDb: -24 },
  { multiple: 6, amplitudeDb: -29 },
  { multiple: 7, amplitudeDb: -34 },
].map((h) => ({ frequency: h.multiple * FUNDAMENTAL, amplitudeDb: h.amplitudeDb }));

function noiseFloorDb(frequency) {
  return -72 - 6 * Math.log10(frequency / 100) + (rand() - 0.5) * 3;
}

function harmonicPeakDb(frequency, peak, widthOctaves) {
  const octaves = Math.log2(frequency / peak.frequency);
  const falloffDb = 55 * (octaves / widthOctaves) ** 2;
  return peak.amplitudeDb - falloffDb;
}

const logStep = Math.log10(F_MAX / F_MIN) / (N_BINS - 1);
const spectrum = Array.from({ length: N_BINS }, (_, i) => {
  const frequency = F_MIN * 10 ** (i * logStep);
  const peakContribution = HARMONICS.reduce(
    (acc, h) => Math.max(acc, harmonicPeakDb(frequency, h, 0.05)),
    -Infinity,
  );
  const amplitude = Math.max(noiseFloorDb(frequency), peakContribution);
  return { x: frequency, y: amplitude };
});

// --- Mount -------------------------------------------------------------
const canvas = document.createElement("canvas");
document.getElementById("container").appendChild(canvas);

// --- Distinctive feature: custom plugin calling out the fundamental ----
const Y_MIN = -90;
const annotateFundamental = {
  id: "annotateFundamental",
  afterDatasetsDraw(chart) {
    const { ctx, scales } = chart;
    const xPixel = scales.x.getPixelForValue(FUNDAMENTAL);
    const yPixel = scales.y.getPixelForValue(HARMONICS[0].amplitudeDb);
    const bottomPixel = scales.y.getPixelForValue(Y_MIN);

    ctx.save();
    ctx.strokeStyle = t.inkSoft;
    ctx.setLineDash([4, 4]);
    ctx.beginPath();
    ctx.moveTo(xPixel, yPixel);
    ctx.lineTo(xPixel, bottomPixel);
    ctx.stroke();

    ctx.setLineDash([]);
    ctx.fillStyle = t.ink;
    ctx.font = "14px sans-serif";
    ctx.textAlign = "center";
    ctx.fillText("440 Hz — A4 fundamental", xPixel, yPixel - 12);
    ctx.restore();
  },
};

// --- Chart ---------------------------------------------------------------
new Chart(canvas, {
  type: "line",
  plugins: [annotateFundamental],
  data: {
    datasets: [
      {
        label: "Amplitude",
        data: spectrum,
        borderColor: t.palette[0],
        backgroundColor: t.palette[0] + "26",
        borderWidth: 2.5,
        pointRadius: 0,
        fill: "start",
        tension: 0,
      },
    ],
  },
  options: {
    responsive: true,
    maintainAspectRatio: false,
    animation: false,
    plugins: {
      title: {
        display: true,
        text: "spectrum-basic · javascript · chartjs · anyplot.ai",
        color: t.ink,
        font: { size: 22 },
        padding: { bottom: 20 },
      },
      legend: { display: false },
    },
    scales: {
      x: {
        type: "logarithmic",
        min: F_MIN,
        max: F_MAX,
        ticks: {
          color: t.inkSoft,
          font: { size: 14 },
          callback: (value) => {
            const digits = Math.log10(value);
            if (!Number.isInteger(digits) && value !== 20 && value !== 50 && value !== 200 && value !== 500 && value !== 2000 && value !== 5000) {
              return null;
            }
            return value >= 1000 ? `${value / 1000}k` : `${value}`;
          },
        },
        grid: { display: false },
        title: { display: true, text: "Frequency (Hz)", color: t.ink, font: { size: 16 } },
      },
      y: {
        min: Y_MIN,
        suggestedMax: 8,
        ticks: { color: t.inkSoft, font: { size: 14 } },
        grid: { color: t.grid },
        title: { display: true, text: "Amplitude (dB)", color: t.ink, font: { size: 16 } },
      },
    },
  },
});

Retrieve this implementation

Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/spectrum-basic/chartjs/code. Any spec id and library id listed in llms-full.txt fit the same URL shape; every URL below is complete and callable.

{
  "spec_id": "spectrum-basic",
  "language": "javascript",
  "library": "chartjs",
  "page": "https://anyplot.ai/spectrum-basic/javascript/chartjs",
  "hub": "https://anyplot.ai/spectrum-basic",
  "code_json": "https://api.anyplot.ai/specs/spectrum-basic/chartjs/code",
  "spec_json": "https://api.anyplot.ai/specs/spectrum-basic",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/spectrum-basic/javascript/chartjs/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/spectrum-basic/javascript/chartjs/plot-dark.png",
  "interactive_light_html": "https://storage.googleapis.com/anyplot-images/plots/spectrum-basic/javascript/chartjs/plot-light.html",
  "interactive_dark_html": "https://storage.googleapis.com/anyplot-images/plots/spectrum-basic/javascript/chartjs/plot-dark.html",
  "quality_score": 92.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of Frequency Spectrum Plot on anyplot.ai.

Other implementations