Skip to main content

spectrumalyzer

Turn any song into animation-ready data. Give it music — get beats, BPM, onsets, frequency bands, song sections and loudness, delivered in the exact format your creative tool speaks: Remotion, HyperFrames, After Effects, Final Cut / DaVinci Resolve, spreadsheets, or plain JSON.

No subscriptions, no uploads, no cloud. Everything runs locally with two open-source workhorses: Python + ffmpeg.


Why spectrumalyzer?

Editors and animators don't think in seconds — they think in frames. Most audio tools stop at "here are some timestamps." spectrumalyzer goes the last mile:

  • Frame-native output — beats pre-converted to your project's frame rate (24 / 25 / 30 / 60 fps), so there's zero runtime math in your compositions.
  • Speaks your tool's dialect — a React hook for Remotion, a marker script for After Effects, an FCPXML for Final Cut and Resolve, a timeline JSON for HyperFrames.
  • Musically grounded — the beat grid is phase-locked to the kick drum, not to generic loudness spikes, so your cuts land on the beat even when hi-hats are louder than the bass.
  • Honest data — every signal is a real measurement (FFT-based), with confidence scores and documented limitations.

How it works — Architecture

spectrumalyzer is a pipeline: decode → analyze → frame-ize → export. Each stage is an independent, testable component.

flowchart LR
    subgraph DECODE["1 · Decode"]
        A["Audio file<br/>mp3 · wav · flac · ogg · m4a · video…"] --> B["ffmpeg → mono PCM<br/>44.1 kHz float"]
    end

    subgraph ANALYZE["2 · Analyze"]
        B --> C["STFT<br/>sliding Fourier transform"]
        C --> D["Spectral flux<br/>onset strength envelope"]
        D --> E["Onset picking<br/>→ raw hits + strengths"]
        D --> F["Tempo<br/>autocorrelation → BPM + confidence"]
        F --> G["Beat grid<br/>kick-anchored phase lock"]
        C --> H["6 frequency bands<br/>sub → treble per frame"]
        B --> I["Loudness contour<br/>+ waveform peaks"]
        I --> J["Sections<br/>quiet · mid · high · peak"]
    end

    subgraph FRAME["3 · Frame-ize"]
        K["time_to_frame<br/>+ band resampling at --fps"]
    end

    subgraph EXPORT["4 · Export"]
        L["6 exporters<br/>+ HTML report"]
    end

    E --> K
    G --> K
    H --> K
    J --> K
    K --> L

The stages in plain language

Stage What happens Why it matters to you
Decode ffmpeg converts any audio (or video's audio track) to clean mono PCM You never think about codecs — if ffmpeg plays it, spectrumalyzer reads it
STFT The signal is windowed and Fourier-transformed thousands of times This is where "sound" becomes measurable frequency data
Spectral flux Measures how fast the spectrum changes between frames Percussive hits (kicks, snares, clicks) jump out; sustained notes don't
Onset picking Adaptive thresholds find the exact moments of impact Your "hit" effects trigger on real transients, not random loud parts
Tempo Autocorrelation finds the repeating pulse; octave confusion is resolved by preferring the DAW-authors' tempo, refined below one frame BPM you can quantize an edit to
Beat grid A perfectly uniform grid is phase-aligned to the low-frequency (kick) pulse Cuts land on the beat, not on the off-beat hi-hat
Frequency bands Energy in 6 musical ranges, normalized per band, per frame Bass drives glow, treble drives sparkle — each band shows its own dynamics
Sections The smoothed loudness contour is scanned for structural shifts "Quiet intro → peak drop" becomes data your storyboard can follow
Frame-ize Every timestamp and band row is converted to your project fps Copy-paste numbers straight into keyframes
Export Pure, tested renderers produce each tool's format Same analysis, every dialect, no drift between tools

Component map

Component Responsibility
audio ffmpeg/ffprobe wrapper — duration, sample rate, PCM decoding
analysis.spectrum STFT, 6-band energies, spectral centroid, spectral flux
analysis.beats onset picking, tempo estimation, kick-anchored beat grid
analysis.structure song-section segmentation with energy labels
analysis.features loudness envelope, waveform peaks (visualization)
model the data contract: AnalysisResult, Beat, Section, BandData + frame math
exporters one pure renderer per output format + companion files
report self-contained HTML visualization
cli analyze, report, formats commands

Quick start

# 1. Install (requires ffmpeg on your system)
pip install spectrumalyzer

# 2. Analyze a track for a 30 fps project, in three formats at once
spectrumalyzer analyze song.mp3 --fps 30 --format remotion,csv,ae

# 3. Open a visual report in your browser
spectrumalyzer report song.mp3 -o song.html

That's it. You now have beat numbers in frames, a marker script for After Effects, a CSV for Resolve, and a page that shows you the song's anatomy.


Installation

Requirements: Python 3.9+ and ffmpeg on your PATH.

# macOS
brew install ffmpeg && pip install spectrumalyzer

# Debian / Ubuntu / Mint
sudo apt install ffmpeg && pip install spectrumalyzer

# Windows (winget or choco)
winget install ffmpeg && pip install spectrumalyzer

Check your setup:

spectrumalyzer --version    # CLI is on PATH
ffmpeg -version             # decoder is reachable

The three commands

1. analyze — the workhorse

spectrumalyzer analyze AUDIO [--fps N] [--format LIST] [options]
Option Default What it does
--fps 24/25/30/60 none Adds true frame numbers to all exports (required for remotion, hyperframes, ae, csv, fcpxml)
--format LIST json Comma-separated: json,remotion,hyperframes,ae,csv,fcpxml
-o, --output BASE audio name Base path; each format appends its own suffix
--min-bpm / --max-bpm 60 / 200 Tempo search window — use for unusually slow/fast music
--no-bands off Skip frequency-band analysis (faster)
--no-sections off Skip section detection (faster)
--stdout off Print the JSON to the terminal (pipe-friendly)
-q, --quiet off Suppress progress output (for scripts/CI)

Example — everything, for a 24 fps film project:

spectrumalyzer analyze soundtrack.flac --fps 24 \
  --format json,remotion,hyperframes,ae,csv,fcpxml \
  --min-bpm 70 --max-bpm 180 -o deliverables/soundtrack

2. report — see the music

spectrumalyzer report AUDIO [-o report.html] [--fps N]

Generates one self-contained HTML file (no server, no external assets): waveform with the beat grid drawn over it, a band-by-time heatmap, the section strip, and all key stats. Perfect for client reviews or deciding where the edit's energy should go before you animate anything.

3. formats — the cheat sheet

spectrumalyzer formats

Lists every export format, its file suffix and what it's for.


Output formats — which one do I use?

Format File(s) produced Use it in You get
json *.json Anything — custom scripts, AI agents, spreadsheets The complete analysis: beats, onsets, sections, bands, centroid, loudness
remotion *.remotion.json + *.useBeats.ts Remotion (React videos) Frame-native data plus a ready React hook
hyperframes *.hyperframes.json + *.hyperframes.snippet.html HyperFrames compositions Timeline events mapped to data-hf-* attributes and hf-seek listeners
ae *.markers.jsx After Effects A script that stamps every beat/hit/section as a comp marker — snap layers to it
csv *.markers.csv Premiere Pro, DaVinci Resolve (marker import), Excel/Numbers Universal marker table: index, type, time, frame, label, strength
fcpxml *.fcpxml Final Cut Pro, DaVinci Resolve, Compressor Standards-based timeline with rational-time markers

Remotion in 30 seconds

spectrumalyzer analyze song.mp3 --fps 30 --format remotion
# → song.remotion.json + song.useBeats.ts — copy both next to your composition
import { useBeatPop } from "./song.useBeats";

export const MyScene = () => {
  const scale = useBeatPop(0.15);   // pops 15% on every beat, springs back
  return <h1 style={{ transform: `scale(${scale})` }}>ON BEAT</h1>;
};

The generated hook also includes useBeatIndex(), useBeatProgress(), useIsOnBeat(window), and the raw BEATS / BPM constants.

After Effects in 30 seconds

spectrumalyzer analyze song.mp3 --fps 30 --format ae
# → song.markers.jsx — then: File → Scripts → Run Script File…

Every beat, hit and section becomes a named comp marker (beat 1, hit, section: peak) — enable Layer → Time → marker snapping and keyframe straight to the music.

Raw JSON anatomy (abridged)

{
  "bpm": 128.32,
  "bpmConfidence": 0.91,          // 0..1 — how strong the tempo pulse is
  "fps": 30,
  "durationInFrames": 7690,
  "beats": [
    { "time": 0.5012, "frame": 15, "strength": 0.87 }
  ],
  "onsets": [                      // raw transients — hits, not grid beats
    { "time": 0.1203, "frame": 4, "strength": 1.0 }
  ],
  "bands": {
    "names": ["sub", "bass", "lowMid", "mid", "highMid", "treble"],
    "frameDuration": 0.0232,
    "matrix": [[0.0, 0.42, 0.11, 0.05, 0.02, 0.01], "…per analysis frame"]
  },
  "bandsByFrame": "…same 6 values, resampled to one row per VIDEO frame",
  "sections": [
    { "start": 0.0, "end": 24.8, "label": "quiet", "energy": 0.22,
      "startFrame": 0, "endFrame": 744 }
  ]
}

The signals — and what to animate with them

Signal Field Creative recipes
Beat grid beats[] Cut on beats; scale pops (useBeatPop); scroll/carousel steps; logo bounces
Onsets (hits) onsets[] Glitch flashes, particle bursts, impact frames, whip-pans on accents
Beat strength beats[].strength Big cuts on strong beats, subtle motion on weak ones
sub (20–60 Hz) bands.matrix[:,0] Camera shake, room rumble, bass drops
bass (60–250 Hz) bands.matrix[:,1] Glow pulses, light intensity, liquid wobble
lowMid (250–500) bands.matrix[:,2] Warmth — slow color drift, ambient fills
mid (500–2k) bands.matrix[:,3] Vocals/lead-reactive shapes, text emphasis
highMid (2–6k) bands.matrix[:,4] Snare cracks — sharp 1-frame hits
treble (6–20k) bands.matrix[:,5] Sparkles, glints, particle twinkles, confetti
Centroid centroid[] Brightness→blur, cool→warm color grading
Loudness loudness[] Zoom pressure, opacity rides, vignette breathing
Sections sections[] Scene grouping, narrative pacing, auto-storyboard order

Python API

Prefer scripting? One import does everything:

from spectrumalyzer import analyze

result = analyze("song.mp3", fps=30)

result.bpm                    # 128.32
result.bpm_confidence         # 0.91
result.beats_as_frames()      # [15, 53, 91, ...]  at 30 fps
result.band_rows_at_fps()     # one [sub..treble] row per video frame
result.sections               # [Section(start, end, energy, label), ...]
result.to_dict()              # JSON-ready dict

Every option from the CLI is a keyword argument (bands=False, sections=False, min_bpm=70, max_bpm=180, …).


Troubleshooting & FAQ

ffmpeg not found — install it (see Installation) and make sure ffmpeg -version works in your terminal.

BPM is double or half what I expect. Heavily syncopated music can report the 8th-note pulse or the half-time feel. Constrain the search: --min-bpm 100 --max-bpm 140 for a house track you know is ~120.

bpmConfidence is low (< 0.5). The pulse is unclear — ambient, live drums, or rubato material. The beat grid may drift; prefer onsets[] (raw hits) for such tracks, or widen the BPM window.

Beats feel slightly off on hi-hat-heavy music. That was the v0.1 problem — v0.2 anchors the grid to the kick. If a track has no low-frequency pulse (acoustic, choir), the grid falls back to onset alignment (±12 ms).

Remotion export refuses to run. It needs --fps — frame numbers can't be computed without a frame rate.

Can it read video files? Yes — any file ffmpeg reads, including MP4/MOV; the audio track is extracted automatically.

Where is the old spectrum key? v0.1's "spectrum" was not a real FFT (it was loudness slices wearing frequency labels). v0.2 replaced it with the real bands matrix. bpm, beats, beatTimes are unchanged; see the CHANGELOG.


Development

git clone https://github.com/Dream-Pixels-Forge/spectrumalyzer
cd spectrumalyzer
pip install -e ".[dev]"      # pytest + ruff
pytest                       # 44 tests, synthetic fixtures, fully offline
ruff check src tests

Roadmap (downbeats, musical key detection, cut-list generation, DAW project stubs, Resolve scripting): see docs/ROADMAP.md.


License

MIT — see LICENSE. Built by Dream-Pixels-Forge.


Semantic hearing (v0.3) — describe, tag, and find music by meaning

Beyond beats, spectrumalyzer can run a small local embedding model (EmbeddingGemma-300m, Google's current small Gemma — with a non-gated automatic fallback) to give its measurements language:

# one-time: download the model (~90 MB fallback / ~600 MB Gemma)
spectrumalyzer model pull

# describe a track in words + mood/energy/genre tags
spectrumalyzer describe song.mp3

# semantic search across a library — plain language in, ranked tracks out
spectrumalyzer find "calm warm bed for a documentary intro" ./my-library
spectrumalyzer find "aggressive intense banger for a workout" ./my-library

# attach description + tags to any analysis
spectrumalyzer analyze song.mp3 --embed -o enriched.json

How it stays honest: tags are not magic. Every track is summarized in words derived from measurements (tempo class, dBFS loudness, brightness, dominant frequency band, transient density, section contrast), then tagged by blending embedding similarity with a numeric feature-profile match per descriptor. Pulseless ambient material is reported as "no clear pulse" — never an invented BPM.

The model lives in ~/.config/spectrumalyzer/embedding/ and everything runs locally — no audio or text ever leaves your machine. The core DSP analysis never depends on it.

Install with the extra: pip install "spectrumalyzer[embedding]".

Metadata

Release files for spectrumalyzer 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for spectrumalyzer 0.3.0
File Size Uploaded Provenance
spectrumalyzer-0.3.0.tar.gz 58.0 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for spectrumalyzer 0.3.0
File Interpreter ABI Platform
spectrumalyzer-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 109.1 kB

Release files / spectrumalyzer-0.3.0.tar.gz

Download URL spectrumalyzer-0.3.0.tar.gz
Size 58.0 kB
Tags Source
SHA-256 checksum
How to use checksums
be376fa9e6bd7977b43be018597b4a761f1d9cfbd73ff49426ec6141e4cddccd
BLAKE2b-256 checksum
How to use checksums
754052e820a73c2a2b26cfb5b239d0fbce06d338cc2381bec69aff7ded2a3ada
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release files / spectrumalyzer-0.3.0-py3-none-any.whl

Download URL spectrumalyzer-0.3.0-py3-none-any.whl
Size 51.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a744e0a5a1fbcdccde809338af4faa6960ef3ef47cc1ec6dc47d3ce5b086bd71
BLAKE2b-256 checksum
How to use checksums
846546835e7645bbb034ec495e88132108deab7268e6ea01f3159eb4e92c1c08
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Security for spectrumalyzer 0.3.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for spectrumalyzer-0.3.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page