https://github.com/bradsec/audiospectr
Turn audio files into animated MP4 videos with various audio-reactive styles and patterns
https://github.com/bradsec/audiospectr
audio-processing audio-spectrum-visualizer audio-to-video audiowaveform
Last synced: 3 days ago
JSON representation
Turn audio files into animated MP4 videos with various audio-reactive styles and patterns
- Host: GitHub
- URL: https://github.com/bradsec/audiospectr
- Owner: bradsec
- License: mit
- Created: 2026-05-08T23:41:07.000Z (26 days ago)
- Default Branch: main
- Last Pushed: 2026-05-09T02:40:58.000Z (26 days ago)
- Last Synced: 2026-05-09T04:36:52.683Z (26 days ago)
- Topics: audio-processing, audio-spectrum-visualizer, audio-to-video, audiowaveform
- Language: Python
- Homepage:
- Size: 6.52 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# audiospectr
`audiospectr` is a Python CLI application that turns audio files into animated MP4 videos with audio-reactive visualiser layers. It pre-analyses the audio into per-frame FFT, waveform, RMS, and frequency-band data, composites one or more RGBA layers over a colour or image background, then streams raw frames directly to FFmpeg for encoding.
## Features
- Input audio analysis for MP3, WAV, FLAC, M4A, and OGG files through `librosa`.
- MP4 export through system FFmpeg using H.264 video and AAC audio.
- Quick single-layer rendering from CLI flags.
- JSON project rendering for multi-layer compositions.
- Background colour or cover-cropped background image support, with blur, dimming, and optional parallax.
- Visualiser layers: `bars`, `wavebars`, `waveform`, `circular`, `pulse`, `particles`, `text`, and `timer`.
- Layer effects: glow, blur, grain, opacity, normal/add/screen blending.
- Built-in presets with descriptive pattern names, such as `bars-neon-mirror`, `waveform-cyan-fill`, and `circular-synthwave-orbit`.
- `--dump-frame` mode for rendering one preview PNG without encoding a full video.
## Visualiser Styles
| | |
|:---:|:---:|
| 
**bars** — frequency spectrum as vertical bars | 
**wavebars** — symmetric seewav-style bars |
| 
**waveform** — smoothed amplitude line | 
**circular** — radial bars around a ring |
| 
**pulse** — bass-reactive concentric rings | 
**particles** — audio-spawned floating dots |
| 
**text** — title, artist, or static label overlay | 
**timer** — playback progress bar or line |
## Preset Gallery
### Bars
| | | |
|:---:|:---:|:---:|
| 
`bars-neon-mirror` | 
`bars-cyberpunk-peaks` | 
`bars-vaporwave-dream` |
| 
`bars-ember-wide` | 
`bars-bass-wall` | 
`bars-ice-peaks` |
| 
`bars-lofi-amber` | 
`bars-minimal-white` | 
`bars-noir-steel` |
### Wavebars
| | | |
|:---:|:---:|:---:|
| 
`wavebars-seewav-classic` | 
`wavebars-seewav-green` | 
`wavebars-lofi-warm` |
### Waveform
| | | |
|:---:|:---:|:---:|
| 
`waveform-magenta-glow` | 
`waveform-cyan-fill` | 
`waveform-dark-ember` |
| 
`waveform-forest-line` | 
`waveform-silk-line` | |
### Circular
| | | |
|:---:|:---:|:---:|
| 
`circular-synthwave-orbit` | 
`circular-vaporwave-halo` | 
`circular-ice-ring` |
| 
`circular-solar-ring` | | |
### Pulse
| | | |
|:---:|:---:|:---:|
| 
`pulse-bass-ring` | 
`pulse-hot-pink` | 
`pulse-deep-blue` |
| 
`pulse-spoked-electric` | | |
### Particles
| | | |
|:---:|:---:|:---:|
| 
`particles-embers-rise` | 
`particles-radial-sparks` | 
`particles-updraft-stars` |
| 
`particles-sidewind-cyan` | | |
### Timer
| | |
|:---:|:---:|
| 
`timer-neon-bar` | 
`timer-minimal-line` |
### Legacy
| | | | |
|:---:|:---:|:---:|:---:|
| 
`neon` | 
`minimal` | 
`cyberpunk` | 
`synthwave` |
## Background Image Modes
The `background` block in a project file (or `--background-fit` / `--background-color` flags in quick mode) controls how images are positioned and processed before layers are composited on top.
### Fit
| | |
|:---:|:---:|
| 
**`cover`** — 16:9 image, bars visualiser | 
**`cover`** — 1:1 image cropped to fill, circular visualiser |
| 
**`contain`** — 1:1 image letterboxed with black padding, waveform visualiser | 
**`stretch`** — 1:1 image distorted to fill, wavebars visualiser |
| 
**`center`** — 1:1 image at original size, pulse visualiser | |
- **`cover`** — scales to fill the frame, cropping the longer dimension. Best for wide backgrounds and blurred album art.
- **`contain`** — fits the whole image inside the frame, padding the remaining area with `color`. Best for square album covers you want to display in full.
- **`stretch`** — ignores aspect ratio and forces the image to exactly match the frame size.
- **`center`** — places the image at its native pixel size centred on the frame. If the image is smaller than the frame, `color` fills the edges; if larger, it is cropped to centre.
### Blur and dim
| | | |
|:---:|:---:|:---:|
| 
**`blur: 8`** — particles visualiser | 
**`dim: 0.6`** — text visualiser | 
**`blur: 6, dim: 0.45`** — timer visualiser |
`blur` applies a Gaussian blur (value is the radius in pixels). `dim` blends the image toward black — `0.0` leaves the image unchanged, `1.0` produces solid black. Combine both to soften and darken a background so visualiser layers and text read clearly on top.
### Config examples
Square album cover centred with black letterbox:
```json
{
"background": {
"type": "image",
"path": "cover.jpg",
"fit": "contain",
"color": "#000000"
}
}
```
Full-screen cover image blurred and dimmed (typical album-art treatment):
```json
{
"background": {
"type": "image",
"path": "cover.jpg",
"fit": "cover",
"blur": 6,
"dim": 0.45
}
}
```
Soft atmospheric background with parallax drift:
```json
{
"background": {
"type": "image",
"path": "background.jpg",
"fit": "cover",
"blur": 10,
"dim": 0.3,
"parallax": true,
"parallax_speed": 0.03
}
}
```
## Requirements
- Python 3.11 or newer
- `uv` (package manager)
- FFmpeg available on `PATH`
On Debian or Ubuntu:
```bash
sudo apt update
sudo apt install ffmpeg python3 python3-venv
```
On macOS (Homebrew):
```bash
brew install ffmpeg
```
On Windows, download a build from [ffmpeg.org/download.html](https://ffmpeg.org/download.html), extract it, and add the `bin` folder to your `PATH` environment variable. Verify with `ffmpeg -version` in a new terminal.
## Setup
Install [uv](https://docs.astral.sh/uv/) if you don't have it:
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```
On Windows:
```powershell
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
```
Then install project dependencies:
```bash
uv sync --group dev
```
`uv` creates and manages the virtual environment automatically — no need to activate it. Prefix any command with `uv run` to use the project environment, or install globally with `uv tool install .` to use `spectr` directly.
Run the test suite:
```bash
uv run pytest
```
## Quick Render
The simplest render — one command, no background needed:
```bash
spectr quick --audio track.mp3 --preset bars-neon-mirror --output out.mp4
```
Add a cover image:
```bash
spectr quick \
--audio track.mp3 \
--background cover.jpg \
--preset bars-neon-mirror \
--output out.mp4
```
Preview a frame before committing to a full render — no FFmpeg, instant:
```bash
spectr quick \
--audio track.mp3 \
--background cover.jpg \
--preset bars-neon-mirror \
--output out.mp4 \
--dump-frame 100
```
This writes `out.png` next to the output path and exits without encoding.
For full control over a single visualiser layer:
Useful options:
```text
--style bars|wavebars|waveform|circular|pulse|particles|text|timer
--preset bars-neon-mirror
--width 1920
--height 1080
--fps 30
--video-preset veryfast
--crf 23
--bar-count 96
--amplitude-scale 3.5
--glow-scale 0.5
--background-fit cover|contain|stretch|center
--background-color "#000000"
--dump-frame 150
```
`--dump-frame N` writes a PNG next to the configured output file and exits before FFmpeg encoding.
For faster renders, lower the output size or FPS, lower `--glow-scale`, or use a faster FFmpeg preset such as `--video-preset ultrafast`. For more reactive bars, increase `--amplitude-scale`.
CRF controls H.264 quality and size: lower values mean higher quality and larger files. Useful values are `18` for high quality, `20-23` for normal output, and `28` for smaller files.
If `--style` is omitted, quick mode uses the preset's `type`. If both are provided, `--style` wins.
If no preset is selected, quick mode renders the selected style in white over the default black background.
Background image fit modes:
- `cover`: fill the whole video while preserving aspect ratio, cropping edges if needed. Best for full-screen backgrounds.
- `contain`: fit the whole image inside the video while preserving aspect ratio, with padding around it. Best for square album art.
- `stretch`: force the image to exactly match the video size.
- `center`: place the image at original size in the centre, with padding or cropping if needed.
Square album cover centered with black padding:
```bash
spectr quick \
--audio track.mp3 \
--background cover.jpg \
--background-fit contain \
--background-color "#000000" \
--style circular \
--output album-cover.mp4
```
Full-screen image background:
```bash
spectr quick \
--audio track.mp3 \
--background wide-background.jpg \
--background-fit cover \
--preset bars-neon-mirror \
--output fullscreen.mp4
```
List installed presets:
```bash
spectr presets
```
Preset names are pattern-first:
```text
bars-neon-mirror
bars-cyberpunk-peaks
bars-minimal-white
bars-bass-wall
bars-ember-wide
bars-ice-peaks
bars-lofi-amber
bars-vaporwave-dream
bars-noir-steel
waveform-silk-line
waveform-cyan-fill
waveform-magenta-glow
waveform-dark-ember
waveform-forest-line
wavebars-seewav-classic
wavebars-seewav-green
wavebars-lofi-warm
circular-synthwave-orbit
circular-ice-ring
circular-solar-ring
circular-vaporwave-halo
pulse-bass-ring
pulse-hot-pink
pulse-deep-blue
pulse-spoked-electric
particles-radial-sparks
particles-updraft-stars
particles-sidewind-cyan
particles-embers-rise
timer-neon-bar
timer-minimal-line
```
Legacy presets `neon`, `minimal`, `cyberpunk`, and `synthwave` are still available.
## JSON Project Render
Use `render` mode for multi-layer videos:
```bash
spectr render sample_project.json
```
Preview a single frame:
```bash
spectr render sample_project.json --dump-frame 150
```
Example project:
```json
{
"resolution": [1920, 1080],
"fps": 30,
"audio": "track.mp3",
"output": "out.mp4",
"background": {
"type": "image",
"path": "cover.jpg",
"fit": "cover",
"color": "#000000",
"blur": 4,
"dim": 0.35
},
"metadata": {
"title": "Song Title",
"artist": "Artist Name"
},
"layers": [
{
"type": "bars",
"x": 0,
"y": 760,
"width": 1920,
"height": 300,
"bar_count": 96,
"color": "#00f5ff",
"secondary_color": "#ff00cc",
"mirror": true,
"effects": {
"glow": true,
"glow_intensity": 1.5,
"glow_radius": 15
}
},
{
"type": "text",
"x": 80,
"y": 60,
"width": 1760,
"height": 90,
"content": "{artist} - {title}",
"font_size": 52,
"color": "#ffffff"
},
{
"type": "timer",
"x": 0,
"y": 1065,
"width": 1920,
"height": 15,
"color": "#00f5ff",
"background_color": "#111111"
}
]
}
```
## Examples
The `examples/` directory contains ready-to-run project files. Edit the `audio` and `output` paths in each file to point to your files, then run with `render`.
### Podcast episode — `examples/podcast.json`
Clean waveform with show name, episode title, and a progress bar — white on dark, no image needed.
```bash
spectr render examples/podcast.json
```
```json
{
"resolution": [1920, 1080],
"fps": 30,
"audio": "episode.mp3",
"output": "podcast.mp4",
"background": {"type": "color", "color": "#0a0a0a"},
"metadata": {"title": "Episode 42", "artist": "My Podcast"},
"layers": [
{
"type": "waveform",
"x": 0, "y": 380,
"width": 1920, "height": 320,
"color": "#ffffff",
"line_width": 3
},
{
"type": "text",
"x": 80, "y": 80,
"width": 1760, "height": 80,
"content": "{artist} · {title}",
"font_size": 44,
"color": "#cccccc"
},
{
"type": "timer",
"x": 0, "y": 1062,
"width": 1920, "height": 18,
"style": "bar",
"color": "#ffffff",
"background_color": "#222222"
}
]
}
```
### Album / track share — `examples/album-single.json`
Bars over a blurred cover image, with artist name, track title, and a neon progress bar.
```bash
spectr render examples/album-single.json
```
```json
{
"resolution": [1920, 1080],
"fps": 30,
"audio": "track.mp3",
"output": "album-single.mp4",
"background": {
"type": "image",
"path": "cover.jpg",
"fit": "cover",
"blur": 6,
"dim": 0.45
},
"metadata": {"title": "Track Name", "artist": "Artist Name"},
"layers": [
{
"type": "bars",
"x": 0, "y": 700,
"width": 1920, "height": 340,
"bar_count": 96,
"color": "#00f5ff",
"secondary_color": "#ff00cc",
"mirror": true,
"effects": {
"glow": true,
"glow_intensity": 1.5,
"glow_radius": 15
}
},
{
"type": "text",
"x": 80, "y": 60,
"width": 1760, "height": 80,
"content": "{artist} — {title}",
"font_size": 48,
"color": "#ffffff"
},
{
"type": "timer",
"x": 0, "y": 1062,
"width": 1920, "height": 18,
"color": "#00f5ff",
"background_color": "#111111"
}
]
}
```
### Lofi / ambient session — `examples/lofi-ambient.json`
Warm circular ring with rising ember particles over a dimmed background — no text, pure motion.
```bash
spectr render examples/lofi-ambient.json
```
```json
{
"resolution": [1920, 1080],
"fps": 30,
"audio": "session.mp3",
"output": "lofi-ambient.mp4",
"background": {
"type": "image",
"path": "background.jpg",
"fit": "cover",
"blur": 8,
"dim": 0.6
},
"layers": [
{
"type": "circular",
"x": 610, "y": 190,
"width": 700, "height": 700,
"bar_count": 72,
"color": "#e8a040",
"bar_width": 5,
"rotation_speed": 0.2,
"smoothing_factor": 0.88,
"amplitude_scale": 4.5,
"effects": {
"glow": true,
"glow_intensity": 1.3,
"glow_radius": 14
}
},
{
"type": "particles",
"x": 0, "y": 0,
"width": 1920, "height": 1080,
"flow_mode": "directional",
"direction": 270,
"spawn_rate": 18,
"velocity_multiplier": 4.0,
"particle_size": 3,
"color": "#ff6600",
"lifetime": 110,
"max_particles": 2800,
"effects": {
"glow": true,
"glow_intensity": 1.25,
"glow_radius": 10
}
}
]
}
```
### Square post (Instagram / YouTube Short)
Centre the album cover with black padding, circular visualiser spinning around it:
```bash
spectr quick \
--audio track.mp3 \
--background cover.jpg \
--background-fit contain \
--background-color "#000000" \
--preset circular-synthwave-orbit \
--width 1080 --height 1080 \
--output square.mp4
```
## Layer Configuration
All layers support common positioning and compositing fields:
```json
{
"type": "bars",
"x": 0,
"y": 0,
"width": 1920,
"height": 1080,
"opacity": 1.0,
"blend_mode": "normal",
"effects": {
"glow": false,
"glow_intensity": 1.0,
"glow_radius": 15,
"glow_scale": 0.5,
"grain": 0.0,
"blur": 0.0
}
}
```
Supported `blend_mode` values are `normal`, `add`, and `screen`.
Visualiser-specific fields include:
- `bars`: `bar_count`, `bar_width`, `spacing`, `corner_radius`, `mirror`, `color`, `secondary_color`, `peak_indicators`, `smoothing_type`, `smoothing_factor`, `analyzer_range`, `amplitude_scale`, `response_curve`
- `wavebars`: `bar_count`, `bar_width`, `spacing_ratio`, `corner_radius`, `color`, `amplitude_scale`, `response_curve`, `reflection_alpha`, `height_ratio`, `smoothing_factor`
- `waveform`: `line_width`, `color`, `fill_color`, `smoothing_factor` (0.0–1.0, default 0.65 — higher values produce a slower, more fluid line)
- `circular`: `bar_count`, `radius`, `inner_radius`, `bar_width`, `rotation_speed`, `center_image`, `color`, `amplitude_scale`, `response_curve`, `min_bar_length`
- `pulse`: `base_radius`, `max_scale`, `ring_width`, `ring_count`, `spoke_count`, `spoke_length`, `spoke_width`, `core`, `core_scale`, `rotation_speed`, `color`, `glow_intensity`
- `particles`: `flow_mode`, `spawn_rate`, `velocity_multiplier`, `particle_size`, `color`, `lifetime`, `direction`, `spawn_spread`, `max_particles`
- `text`: `content`, `font_path`, `font_size`, `color`, `alignment`, `padding`
- `timer`: `style`, `color`, `background_color`, `corner_radius`
## License
MIT — see [LICENSE](LICENSE).
This is an independent, original implementation. The following projects provided design inspiration:
- **[wav2bar-reborn](https://github.com/Picorims/wav2bar-reborn)** by Picorims (MPL-2.0) — the multi-layer compositing model and layer-per-surface rendering approach.
- **[seewav](https://github.com/adefossez/seewav)** by Alexandre Défossez (Unlicense) — the symmetric waveform-bar visual style reproduced in the `wavebars` layer and the `wavebars-seewav-*` presets.
No source code from either project was copied or modified. The `wavebars-seewav-*` preset names are an explicit attribution to seewav's aesthetic.