Skip to content

About

🎶 A simple and intuitive TypeScript library for generating spectrogram waterfall plots.

Resources

Stars

8 stars

Watchers

0 watching

Forks

Repository files navigation

spectrogram-js

🎶 A lightweight TypeScript library for real-time spectrogram (waterfall) rendering.

Live Demo: https://anyshake.github.io/spectrogram-js/

spectrogram-js provides a simple, dependency-light way to generate FFT-based spectrograms and render them onto a <canvas>, suitable for signal processing, audio, sensor data, and scientific visualization use cases.

Features

  • Real-time spectrogram / waterfall rendering
  • Configurable FFT window, overlap, and window functions
  • Built-in colormaps (jet, viridis, etc.)
  • Optional shared FFT executor for performance
  • Designed for streaming / incremental data
  • Zero framework dependency (plain Canvas API)

Installation

$ npm install spectrogram-js

or

$ pnpm add spectrogram-js

Basic Usage

import { Spectrogram } from 'spectrogram-js';

const canvas = document.querySelector('canvas')!;
const windowSize = 768;

const spectrogram = new Spectrogram({
    windowType: 'hann', // hann, hamming, blackman, rectangular
    overlap: Math.floor(windowSize * 0.86),
    sampleRate: 250,
    minDb: 110,
    maxDb: 160,
    windowSize
});

const init = async () => {
    spectrogram.setColormap('jet');
    await spectrogram.init();
};

init() loads the necessary fonts, which is required for rendering the spectrogram.

Rendering

spectrogram.render({
    canvas,
    width: 768,
    height: 256,
    timeRange: [0, 120], // seconds
    freqRange: [0, 25] // Hz
});

Call render() in a loop (requestAnimationFrame) for real-time updates.

Appearance

You can customize appearance and visibility by setting xAxis and yAxis independently in render():

spectrogram.render({
    canvas,
    width: 768,
    height: 256,
    timeRange: [0, 120],
    freqRange: [0, 25],
    xAxis: {
        visible: true,
        color: '#2563eb',
        lineWidth: 2,
        labelStyle: {
            color: '#1d4ed8',
            fontSize: 14,
            fontWeight: 600,
            fontFamily: 'Arial, sans-serif'
        }
    },
    yAxis: {
        visible: false,
    }
});

All axis settings are optional. Defaults preserve the original appearance: visible axes, #464646 lines at 1 px, and #252525 labels using normal-weight 11 px AxisFont. labelStyle applies to tick labels and the frequency axis title. Sizes use CSS pixels; fontWeight accepts a number or a CSS weight such as 'normal' or 'bold'. Custom fonts must be available in the browser before rendering.

Setting visible: false hides that axis together with its ticks, labels, and title. Margins adjust to label sizes and axis visibility. Omitted options use defaults on each render() call. See example/index.ts for interactive controls for both axes.

Time Labels

Set xAxis.labelFormatter to control how time ticks are displayed:

spectrogram.render({
    canvas,
    width: 768,
    height: 256,
    timeRange: [0, 120],
    xAxis: {
        labelFormatter: (time, { offset, end, timestamped }) =>
            timestamped
                ? new Date(time).toISOString().slice(11, 19)
                : `${(end - offset).toFixed(0)}s`
    }
});

time is the tick timestamp in milliseconds for timestamped data, otherwise milliseconds from the first sample. offset and end are seconds from the first sample to the tick and to the end of the visible range. Margins and tick spacing follow the formatted label width. Without a formatter, timestamped data shows local HH:mm:ss and other data shows seconds before the end of the range.

Feeding Data

Input data format:

Array<[timestamp_in_ms, value]>;

For example:

spectrogram.setData([
    [1765945224240, 80001],
    [1765945224244, 27639],
    [1765945224248, 57144],
    [1765945224252, 29916],
    [1765945224256, 66871]
    // ...
]);

You’re expected to manage buffering yourself (e.g. ring buffer).

Gaps

By default, timestamped samples are placed back to back at sampleRate and only the first timestamp is used. Set gapThreshold (milliseconds) to keep missing data visible:

const spectrogram = new Spectrogram({
    windowType: 'hann',
    overlap: Math.floor(windowSize * 0.86),
    sampleRate: 250,
    minDb: 110,
    maxDb: 160,
    windowSize,
    gapThreshold: 500
});

When two consecutive timestamps are further apart than gapThreshold, the missing time is inserted and rendered blank (transparent), so the time axis stays aligned with the data. Raw Float32Array / Float64Array data can mark gaps with NaN samples. Analysis windows must contain windowSize valid samples; a column is left blank when its window contains a missing sample or runs past the end of the data. gapThreshold applies to the next setData() call.

Colormaps

Built-in colormaps include:

  • viridis
  • inferno
  • grayscale
  • jet
  • hot
  • cool
  • spring
  • summer
  • autumn
  • winter
  • bone

See src/render/ColorMap.ts for the color definitions.

License

MIT License © 2025 AnyShake Project

About

🎶 A simple and intuitive TypeScript library for generating spectrogram waterfall plots.

Resources

Stars

8 stars

Watchers

0 watching

Forks

Contributors

Languages