Skip to content

@sonoscope/core

@sonoscope/core is the zero-dependency WebGL2 and WASM-accelerated rendering engine powering Sonoscope.

  • Coordinated Viewport Sync: Synchronize time and frequency zoom/pan effortlessly between multiple spectrogram, waveform, and ruler renderers.
  • WASM STFT Pipeline: Rust-compiled WebAssembly engine calculating Fast Fourier Transforms with high efficiency.
  • WebGL2 Tile Rendering: GPU shader-driven spectrogram tile computation with built-in colormaps (viridis, plasma, inferno, magma, cividis, turbo, gray).
  • Flexible Frequency Scales: Support for linear, log, and mel perceptual frequency scales.
  • Decoupled Playhead & Rulers: Customizable time and frequency rulers with responsive ticks.
  • Native Navigation: First-class pan and zoom navigation on viewers and the central Sonoscope instance with automatic lifecycle cleanup.

The Sonoscope class is the central coordinator for audio playback, viewport state, and viewer creation.

import { Sonoscope } from "@sonoscope/core";
// From Audio Element
const scope = await Sonoscope.fromAudio(audioElement, {
followPlayback: "page",
});
// From Blob / File
const fileScope = await Sonoscope.fromBlob(audioBlob);
// From URL
const urlScope = await Sonoscope.fromUrl("https://example.com/sound.mp3");

Full reference: Sonoscope, ISonoscope, SonoscopeOptions.

Creates a SpectrogramViewer instance attached to a canvas.

const spectrogram = scope.createSpectrogram(canvas, {
colorMap: "turbo",
frequencyScale: "mel",
minDb: -90,
maxDb: 0,
windowSize: 1024,
hopSize: 256,
});
// Attach 2D navigation (defaults to both time and frequency axes)
scope.attachNavigation(canvas);

Full reference: SpectrogramViewer, ISpectrogramViewer, SpectrogramConfig.

Creates a WaveformViewer instance attached to a canvas.

const waveform = scope.createWaveform(canvas, {
colorMap: "turbo",
amplitudeScale: 1.0,
});
// Attach 1D temporal navigation (defaults to time axis)
scope.attachNavigation(canvas, { axis: "time" });

Full reference: WaveformViewer, IWaveformViewer, WaveformConfig.

Creates a TimeRulerViewer or FrequencyRulerViewer, and attaches playhead overlays via attachPlayheadOverlay.

import { attachPlayheadOverlay } from "@sonoscope/core";
const timeRuler = scope.createTimeRuler(timeCanvas, {
program: "ticks",
tickPosition: "top",
});
scope.attachNavigation(timeCanvas, { axis: "time" });
const freqRuler = scope.createFrequencyRuler(freqCanvas, {
program: "ticks",
frequencyScale: "mel",
tickPosition: "right",
});
scope.attachNavigation(freqCanvas, { axis: "frequency" });
attachPlayheadOverlay(containerElement, scope);

Full reference: TimeRulerViewer, FrequencyRulerViewer, TimeRulerConfig, FrequencyRulerConfig, attachPlayheadOverlay.

Sonoscope provides coordinated navigation via scope.attachNavigation(container, options?) on the root Sonoscope instance or directly via attachNavigation.

You can attach interactive pan and zoom navigation directly to any <canvas> or wrapping container element (such as a <div>):

const scope = await Sonoscope.fromUrl(audioUrl);
const spec = scope.createSpectrogram(specCanvas, { frequencyScale: "mel" });
// 1. Spectrogram navigation (pans and zooms both time & frequency)
const detachSpecNav = scope.attachNavigation(specCanvas);
// 2. Waveform navigation (time-only axis)
const detachWaveNav = scope.attachNavigation(waveCanvas, { axis: "time" });
// 3. Time Ruler (time axis) and Frequency Ruler (frequency axis)
scope.attachNavigation(timeCanvas, { axis: "time" });
scope.attachNavigation(freqCanvas, { axis: "frequency" });

attachNavigation accepts comprehensive configuration for axes, modifier keys, sensitivities, and wheel/drag behavior:

scope.attachNavigation(specCanvas, {
// Constrain navigation to a specific axis: 'both' | 'time' | 'frequency'
axis: "both",
// Configure or toggle mouse wheel navigation
wheel: {
panSensitivity: 1.0,
zoomSensitivity: 0.0015,
zoomModifier: "alt", // 'ctrl' | 'shift' | 'alt' | 'meta' | 'none'
frequencyModifier: "shift",
},
// Configure or toggle drag navigation
drag: {
button: 0, // 0 = left, 1 = middle, 2 = right
modifier: "none",
dragThreshold: 3,
cursor: true,
},
onNavigate: (viewport) => {
console.log("Viewport updated:", viewport);
},
});

Full reference: attachNavigation, NavigationOptions, IViewportController.

  • Calling scope.attachNavigation() returns a manual detach: () => void cleanup function.
  • All attached navigation listeners are also automatically cleaned up whenever scope.destroy() is called.