{"id":50461166,"url":"https://github.com/bradsec/audiospectr","last_synced_at":"2026-06-01T04:32:11.948Z","repository":{"id":356648385,"uuid":"1233408745","full_name":"bradsec/audiospectr","owner":"bradsec","description":" Turn audio files into animated MP4 videos with various audio-reactive styles and patterns","archived":false,"fork":false,"pushed_at":"2026-05-09T02:40:58.000Z","size":6840,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-09T04:36:52.683Z","etag":null,"topics":["audio-processing","audio-spectrum-visualizer","audio-to-video","audiowaveform"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bradsec.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-08T23:41:07.000Z","updated_at":"2026-05-09T02:41:02.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/bradsec/audiospectr","commit_stats":null,"previous_names":["bradsec/audiospectr"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/bradsec/audiospectr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradsec%2Faudiospectr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradsec%2Faudiospectr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradsec%2Faudiospectr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradsec%2Faudiospectr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bradsec","download_url":"https://codeload.github.com/bradsec/audiospectr/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradsec%2Faudiospectr/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33760645,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-01T02:00:06.963Z","response_time":115,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audio-processing","audio-spectrum-visualizer","audio-to-video","audiowaveform"],"created_at":"2026-06-01T04:32:10.137Z","updated_at":"2026-06-01T04:32:11.924Z","avatar_url":"https://github.com/bradsec.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# audiospectr\n\n`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.\n\n## Features\n\n- Input audio analysis for MP3, WAV, FLAC, M4A, and OGG files through `librosa`.\n- MP4 export through system FFmpeg using H.264 video and AAC audio.\n- Quick single-layer rendering from CLI flags.\n- JSON project rendering for multi-layer compositions.\n- Background colour or cover-cropped background image support, with blur, dimming, and optional parallax.\n- Visualiser layers: `bars`, `wavebars`, `waveform`, `circular`, `pulse`, `particles`, `text`, and `timer`.\n- Layer effects: glow, blur, grain, opacity, normal/add/screen blending.\n- Built-in presets with descriptive pattern names, such as `bars-neon-mirror`, `waveform-cyan-fill`, and `circular-synthwave-orbit`.\n- `--dump-frame` mode for rendering one preview PNG without encoding a full video.\n\n## Visualiser Styles\n\n| | |\n|:---:|:---:|\n| ![bars](docs/images/style-bars.png)\u003cbr\u003e**bars** — frequency spectrum as vertical bars | ![wavebars](docs/images/style-wavebars.png)\u003cbr\u003e**wavebars** — symmetric seewav-style bars |\n| ![waveform](docs/images/style-waveform.png)\u003cbr\u003e**waveform** — smoothed amplitude line | ![circular](docs/images/style-circular.png)\u003cbr\u003e**circular** — radial bars around a ring |\n| ![pulse](docs/images/style-pulse.png)\u003cbr\u003e**pulse** — bass-reactive concentric rings | ![particles](docs/images/style-particles.png)\u003cbr\u003e**particles** — audio-spawned floating dots |\n| ![text](docs/images/style-text.png)\u003cbr\u003e**text** — title, artist, or static label overlay | ![timer](docs/images/style-timer.png)\u003cbr\u003e**timer** — playback progress bar or line |\n\n## Preset Gallery\n\n### Bars\n\n| | | |\n|:---:|:---:|:---:|\n| ![bars-neon-mirror](docs/images/presets/bars-neon-mirror.png)\u003cbr\u003e`bars-neon-mirror` | ![bars-cyberpunk-peaks](docs/images/presets/bars-cyberpunk-peaks.png)\u003cbr\u003e`bars-cyberpunk-peaks` | ![bars-vaporwave-dream](docs/images/presets/bars-vaporwave-dream.png)\u003cbr\u003e`bars-vaporwave-dream` |\n| ![bars-ember-wide](docs/images/presets/bars-ember-wide.png)\u003cbr\u003e`bars-ember-wide` | ![bars-bass-wall](docs/images/presets/bars-bass-wall.png)\u003cbr\u003e`bars-bass-wall` | ![bars-ice-peaks](docs/images/presets/bars-ice-peaks.png)\u003cbr\u003e`bars-ice-peaks` |\n| ![bars-lofi-amber](docs/images/presets/bars-lofi-amber.png)\u003cbr\u003e`bars-lofi-amber` | ![bars-minimal-white](docs/images/presets/bars-minimal-white.png)\u003cbr\u003e`bars-minimal-white` | ![bars-noir-steel](docs/images/presets/bars-noir-steel.png)\u003cbr\u003e`bars-noir-steel` |\n\n### Wavebars\n\n| | | |\n|:---:|:---:|:---:|\n| ![wavebars-seewav-classic](docs/images/presets/wavebars-seewav-classic.png)\u003cbr\u003e`wavebars-seewav-classic` | ![wavebars-seewav-green](docs/images/presets/wavebars-seewav-green.png)\u003cbr\u003e`wavebars-seewav-green` | ![wavebars-lofi-warm](docs/images/presets/wavebars-lofi-warm.png)\u003cbr\u003e`wavebars-lofi-warm` |\n\n### Waveform\n\n| | | |\n|:---:|:---:|:---:|\n| ![waveform-magenta-glow](docs/images/presets/waveform-magenta-glow.png)\u003cbr\u003e`waveform-magenta-glow` | ![waveform-cyan-fill](docs/images/presets/waveform-cyan-fill.png)\u003cbr\u003e`waveform-cyan-fill` | ![waveform-dark-ember](docs/images/presets/waveform-dark-ember.png)\u003cbr\u003e`waveform-dark-ember` |\n| ![waveform-forest-line](docs/images/presets/waveform-forest-line.png)\u003cbr\u003e`waveform-forest-line` | ![waveform-silk-line](docs/images/presets/waveform-silk-line.png)\u003cbr\u003e`waveform-silk-line` | |\n\n### Circular\n\n| | | |\n|:---:|:---:|:---:|\n| ![circular-synthwave-orbit](docs/images/presets/circular-synthwave-orbit.png)\u003cbr\u003e`circular-synthwave-orbit` | ![circular-vaporwave-halo](docs/images/presets/circular-vaporwave-halo.png)\u003cbr\u003e`circular-vaporwave-halo` | ![circular-ice-ring](docs/images/presets/circular-ice-ring.png)\u003cbr\u003e`circular-ice-ring` |\n| ![circular-solar-ring](docs/images/presets/circular-solar-ring.png)\u003cbr\u003e`circular-solar-ring` | | |\n\n### Pulse\n\n| | | |\n|:---:|:---:|:---:|\n| ![pulse-bass-ring](docs/images/presets/pulse-bass-ring.png)\u003cbr\u003e`pulse-bass-ring` | ![pulse-hot-pink](docs/images/presets/pulse-hot-pink.png)\u003cbr\u003e`pulse-hot-pink` | ![pulse-deep-blue](docs/images/presets/pulse-deep-blue.png)\u003cbr\u003e`pulse-deep-blue` |\n| ![pulse-spoked-electric](docs/images/presets/pulse-spoked-electric.png)\u003cbr\u003e`pulse-spoked-electric` | | |\n\n### Particles\n\n| | | |\n|:---:|:---:|:---:|\n| ![particles-embers-rise](docs/images/presets/particles-embers-rise.png)\u003cbr\u003e`particles-embers-rise` | ![particles-radial-sparks](docs/images/presets/particles-radial-sparks.png)\u003cbr\u003e`particles-radial-sparks` | ![particles-updraft-stars](docs/images/presets/particles-updraft-stars.png)\u003cbr\u003e`particles-updraft-stars` |\n| ![particles-sidewind-cyan](docs/images/presets/particles-sidewind-cyan.png)\u003cbr\u003e`particles-sidewind-cyan` | | |\n\n### Timer\n\n| | |\n|:---:|:---:|\n| ![timer-neon-bar](docs/images/presets/timer-neon-bar.png)\u003cbr\u003e`timer-neon-bar` | ![timer-minimal-line](docs/images/presets/timer-minimal-line.png)\u003cbr\u003e`timer-minimal-line` |\n\n### Legacy\n\n| | | | |\n|:---:|:---:|:---:|:---:|\n| ![neon](docs/images/presets/neon.png)\u003cbr\u003e`neon` | ![minimal](docs/images/presets/minimal.png)\u003cbr\u003e`minimal` | ![cyberpunk](docs/images/presets/cyberpunk.png)\u003cbr\u003e`cyberpunk` | ![synthwave](docs/images/presets/synthwave.png)\u003cbr\u003e`synthwave` |\n\n## Background Image Modes\n\nThe `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.\n\n### Fit\n\n| | |\n|:---:|:---:|\n| ![cover with 16:9 image](docs/images/backgrounds/bg-cover-16x9.png)\u003cbr\u003e**`cover`** — 16:9 image, bars visualiser | ![cover with 1:1 image](docs/images/backgrounds/bg-cover-1x1.png)\u003cbr\u003e**`cover`** — 1:1 image cropped to fill, circular visualiser |\n| ![contain with 1:1 image](docs/images/backgrounds/bg-contain-1x1.png)\u003cbr\u003e**`contain`** — 1:1 image letterboxed with black padding, waveform visualiser | ![stretch with 1:1 image](docs/images/backgrounds/bg-stretch.png)\u003cbr\u003e**`stretch`** — 1:1 image distorted to fill, wavebars visualiser |\n| ![center with 1:1 image](docs/images/backgrounds/bg-center.png)\u003cbr\u003e**`center`** — 1:1 image at original size, pulse visualiser | |\n\n- **`cover`** — scales to fill the frame, cropping the longer dimension. Best for wide backgrounds and blurred album art.\n- **`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.\n- **`stretch`** — ignores aspect ratio and forces the image to exactly match the frame size.\n- **`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.\n\n### Blur and dim\n\n| | | |\n|:---:|:---:|:---:|\n| ![blur](docs/images/backgrounds/bg-blur.png)\u003cbr\u003e**`blur: 8`** — particles visualiser | ![dim](docs/images/backgrounds/bg-dim.png)\u003cbr\u003e**`dim: 0.6`** — text visualiser | ![blur + dim](docs/images/backgrounds/bg-blur-dim.png)\u003cbr\u003e**`blur: 6, dim: 0.45`** — timer visualiser |\n\n`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.\n\n### Config examples\n\nSquare album cover centred with black letterbox:\n\n```json\n{\n  \"background\": {\n    \"type\": \"image\",\n    \"path\": \"cover.jpg\",\n    \"fit\": \"contain\",\n    \"color\": \"#000000\"\n  }\n}\n```\n\nFull-screen cover image blurred and dimmed (typical album-art treatment):\n\n```json\n{\n  \"background\": {\n    \"type\": \"image\",\n    \"path\": \"cover.jpg\",\n    \"fit\": \"cover\",\n    \"blur\": 6,\n    \"dim\": 0.45\n  }\n}\n```\n\nSoft atmospheric background with parallax drift:\n\n```json\n{\n  \"background\": {\n    \"type\": \"image\",\n    \"path\": \"background.jpg\",\n    \"fit\": \"cover\",\n    \"blur\": 10,\n    \"dim\": 0.3,\n    \"parallax\": true,\n    \"parallax_speed\": 0.03\n  }\n}\n```\n\n## Requirements\n\n- Python 3.11 or newer\n- `uv` (package manager)\n- FFmpeg available on `PATH`\n\nOn Debian or Ubuntu:\n\n```bash\nsudo apt update\nsudo apt install ffmpeg python3 python3-venv\n```\n\nOn macOS (Homebrew):\n\n```bash\nbrew install ffmpeg\n```\n\nOn 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.\n\n## Setup\n\nInstall [uv](https://docs.astral.sh/uv/) if you don't have it:\n\n```bash\ncurl -LsSf https://astral.sh/uv/install.sh | sh\n```\n\nOn Windows:\n\n```powershell\npowershell -ExecutionPolicy ByPass -c \"irm https://astral.sh/uv/install.ps1 | iex\"\n```\n\nThen install project dependencies:\n\n```bash\nuv sync --group dev\n```\n\n`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.\n\nRun the test suite:\n\n```bash\nuv run pytest\n```\n\n## Quick Render\n\nThe simplest render — one command, no background needed:\n\n```bash\nspectr quick --audio track.mp3 --preset bars-neon-mirror --output out.mp4\n```\n\nAdd a cover image:\n\n```bash\nspectr quick \\\n  --audio track.mp3 \\\n  --background cover.jpg \\\n  --preset bars-neon-mirror \\\n  --output out.mp4\n```\n\nPreview a frame before committing to a full render — no FFmpeg, instant:\n\n```bash\nspectr quick \\\n  --audio track.mp3 \\\n  --background cover.jpg \\\n  --preset bars-neon-mirror \\\n  --output out.mp4 \\\n  --dump-frame 100\n```\n\nThis writes `out.png` next to the output path and exits without encoding.\n\nFor full control over a single visualiser layer:\n\nUseful options:\n\n```text\n--style bars|wavebars|waveform|circular|pulse|particles|text|timer\n--preset bars-neon-mirror\n--width 1920\n--height 1080\n--fps 30\n--video-preset veryfast\n--crf 23\n--bar-count 96\n--amplitude-scale 3.5\n--glow-scale 0.5\n--background-fit cover|contain|stretch|center\n--background-color \"#000000\"\n--dump-frame 150\n```\n\n`--dump-frame N` writes a PNG next to the configured output file and exits before FFmpeg encoding.\n\nFor 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`.\n\nCRF 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.\n\nIf `--style` is omitted, quick mode uses the preset's `type`. If both are provided, `--style` wins.\n\nIf no preset is selected, quick mode renders the selected style in white over the default black background.\n\nBackground image fit modes:\n\n- `cover`: fill the whole video while preserving aspect ratio, cropping edges if needed. Best for full-screen backgrounds.\n- `contain`: fit the whole image inside the video while preserving aspect ratio, with padding around it. Best for square album art.\n- `stretch`: force the image to exactly match the video size.\n- `center`: place the image at original size in the centre, with padding or cropping if needed.\n\nSquare album cover centered with black padding:\n\n```bash\nspectr quick \\\n  --audio track.mp3 \\\n  --background cover.jpg \\\n  --background-fit contain \\\n  --background-color \"#000000\" \\\n  --style circular \\\n  --output album-cover.mp4\n```\n\nFull-screen image background:\n\n```bash\nspectr quick \\\n  --audio track.mp3 \\\n  --background wide-background.jpg \\\n  --background-fit cover \\\n  --preset bars-neon-mirror \\\n  --output fullscreen.mp4\n```\n\nList installed presets:\n\n```bash\nspectr presets\n```\n\nPreset names are pattern-first:\n\n```text\nbars-neon-mirror\nbars-cyberpunk-peaks\nbars-minimal-white\nbars-bass-wall\nbars-ember-wide\nbars-ice-peaks\nbars-lofi-amber\nbars-vaporwave-dream\nbars-noir-steel\nwaveform-silk-line\nwaveform-cyan-fill\nwaveform-magenta-glow\nwaveform-dark-ember\nwaveform-forest-line\nwavebars-seewav-classic\nwavebars-seewav-green\nwavebars-lofi-warm\ncircular-synthwave-orbit\ncircular-ice-ring\ncircular-solar-ring\ncircular-vaporwave-halo\npulse-bass-ring\npulse-hot-pink\npulse-deep-blue\npulse-spoked-electric\nparticles-radial-sparks\nparticles-updraft-stars\nparticles-sidewind-cyan\nparticles-embers-rise\ntimer-neon-bar\ntimer-minimal-line\n```\n\nLegacy presets `neon`, `minimal`, `cyberpunk`, and `synthwave` are still available.\n\n## JSON Project Render\n\nUse `render` mode for multi-layer videos:\n\n```bash\nspectr render sample_project.json\n```\n\nPreview a single frame:\n\n```bash\nspectr render sample_project.json --dump-frame 150\n```\n\nExample project:\n\n```json\n{\n  \"resolution\": [1920, 1080],\n  \"fps\": 30,\n  \"audio\": \"track.mp3\",\n  \"output\": \"out.mp4\",\n  \"background\": {\n    \"type\": \"image\",\n    \"path\": \"cover.jpg\",\n    \"fit\": \"cover\",\n    \"color\": \"#000000\",\n    \"blur\": 4,\n    \"dim\": 0.35\n  },\n  \"metadata\": {\n    \"title\": \"Song Title\",\n    \"artist\": \"Artist Name\"\n  },\n  \"layers\": [\n    {\n      \"type\": \"bars\",\n      \"x\": 0,\n      \"y\": 760,\n      \"width\": 1920,\n      \"height\": 300,\n      \"bar_count\": 96,\n      \"color\": \"#00f5ff\",\n      \"secondary_color\": \"#ff00cc\",\n      \"mirror\": true,\n      \"effects\": {\n        \"glow\": true,\n        \"glow_intensity\": 1.5,\n        \"glow_radius\": 15\n      }\n    },\n    {\n      \"type\": \"text\",\n      \"x\": 80,\n      \"y\": 60,\n      \"width\": 1760,\n      \"height\": 90,\n      \"content\": \"{artist} - {title}\",\n      \"font_size\": 52,\n      \"color\": \"#ffffff\"\n    },\n    {\n      \"type\": \"timer\",\n      \"x\": 0,\n      \"y\": 1065,\n      \"width\": 1920,\n      \"height\": 15,\n      \"color\": \"#00f5ff\",\n      \"background_color\": \"#111111\"\n    }\n  ]\n}\n```\n\n## Examples\n\nThe `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`.\n\n### Podcast episode — `examples/podcast.json`\n\nClean waveform with show name, episode title, and a progress bar — white on dark, no image needed.\n\n```bash\nspectr render examples/podcast.json\n```\n\n```json\n{\n  \"resolution\": [1920, 1080],\n  \"fps\": 30,\n  \"audio\": \"episode.mp3\",\n  \"output\": \"podcast.mp4\",\n  \"background\": {\"type\": \"color\", \"color\": \"#0a0a0a\"},\n  \"metadata\": {\"title\": \"Episode 42\", \"artist\": \"My Podcast\"},\n  \"layers\": [\n    {\n      \"type\": \"waveform\",\n      \"x\": 0, \"y\": 380,\n      \"width\": 1920, \"height\": 320,\n      \"color\": \"#ffffff\",\n      \"line_width\": 3\n    },\n    {\n      \"type\": \"text\",\n      \"x\": 80, \"y\": 80,\n      \"width\": 1760, \"height\": 80,\n      \"content\": \"{artist}  ·  {title}\",\n      \"font_size\": 44,\n      \"color\": \"#cccccc\"\n    },\n    {\n      \"type\": \"timer\",\n      \"x\": 0, \"y\": 1062,\n      \"width\": 1920, \"height\": 18,\n      \"style\": \"bar\",\n      \"color\": \"#ffffff\",\n      \"background_color\": \"#222222\"\n    }\n  ]\n}\n```\n\n### Album / track share — `examples/album-single.json`\n\nBars over a blurred cover image, with artist name, track title, and a neon progress bar.\n\n```bash\nspectr render examples/album-single.json\n```\n\n```json\n{\n  \"resolution\": [1920, 1080],\n  \"fps\": 30,\n  \"audio\": \"track.mp3\",\n  \"output\": \"album-single.mp4\",\n  \"background\": {\n    \"type\": \"image\",\n    \"path\": \"cover.jpg\",\n    \"fit\": \"cover\",\n    \"blur\": 6,\n    \"dim\": 0.45\n  },\n  \"metadata\": {\"title\": \"Track Name\", \"artist\": \"Artist Name\"},\n  \"layers\": [\n    {\n      \"type\": \"bars\",\n      \"x\": 0, \"y\": 700,\n      \"width\": 1920, \"height\": 340,\n      \"bar_count\": 96,\n      \"color\": \"#00f5ff\",\n      \"secondary_color\": \"#ff00cc\",\n      \"mirror\": true,\n      \"effects\": {\n        \"glow\": true,\n        \"glow_intensity\": 1.5,\n        \"glow_radius\": 15\n      }\n    },\n    {\n      \"type\": \"text\",\n      \"x\": 80, \"y\": 60,\n      \"width\": 1760, \"height\": 80,\n      \"content\": \"{artist} — {title}\",\n      \"font_size\": 48,\n      \"color\": \"#ffffff\"\n    },\n    {\n      \"type\": \"timer\",\n      \"x\": 0, \"y\": 1062,\n      \"width\": 1920, \"height\": 18,\n      \"color\": \"#00f5ff\",\n      \"background_color\": \"#111111\"\n    }\n  ]\n}\n```\n\n### Lofi / ambient session — `examples/lofi-ambient.json`\n\nWarm circular ring with rising ember particles over a dimmed background — no text, pure motion.\n\n```bash\nspectr render examples/lofi-ambient.json\n```\n\n```json\n{\n  \"resolution\": [1920, 1080],\n  \"fps\": 30,\n  \"audio\": \"session.mp3\",\n  \"output\": \"lofi-ambient.mp4\",\n  \"background\": {\n    \"type\": \"image\",\n    \"path\": \"background.jpg\",\n    \"fit\": \"cover\",\n    \"blur\": 8,\n    \"dim\": 0.6\n  },\n  \"layers\": [\n    {\n      \"type\": \"circular\",\n      \"x\": 610, \"y\": 190,\n      \"width\": 700, \"height\": 700,\n      \"bar_count\": 72,\n      \"color\": \"#e8a040\",\n      \"bar_width\": 5,\n      \"rotation_speed\": 0.2,\n      \"smoothing_factor\": 0.88,\n      \"amplitude_scale\": 4.5,\n      \"effects\": {\n        \"glow\": true,\n        \"glow_intensity\": 1.3,\n        \"glow_radius\": 14\n      }\n    },\n    {\n      \"type\": \"particles\",\n      \"x\": 0, \"y\": 0,\n      \"width\": 1920, \"height\": 1080,\n      \"flow_mode\": \"directional\",\n      \"direction\": 270,\n      \"spawn_rate\": 18,\n      \"velocity_multiplier\": 4.0,\n      \"particle_size\": 3,\n      \"color\": \"#ff6600\",\n      \"lifetime\": 110,\n      \"max_particles\": 2800,\n      \"effects\": {\n        \"glow\": true,\n        \"glow_intensity\": 1.25,\n        \"glow_radius\": 10\n      }\n    }\n  ]\n}\n```\n\n### Square post (Instagram / YouTube Short)\n\nCentre the album cover with black padding, circular visualiser spinning around it:\n\n```bash\nspectr quick \\\n  --audio track.mp3 \\\n  --background cover.jpg \\\n  --background-fit contain \\\n  --background-color \"#000000\" \\\n  --preset circular-synthwave-orbit \\\n  --width 1080 --height 1080 \\\n  --output square.mp4\n```\n\n## Layer Configuration\n\nAll layers support common positioning and compositing fields:\n\n```json\n{\n  \"type\": \"bars\",\n  \"x\": 0,\n  \"y\": 0,\n  \"width\": 1920,\n  \"height\": 1080,\n    \"opacity\": 1.0,\n    \"blend_mode\": \"normal\",\n    \"effects\": {\n      \"glow\": false,\n      \"glow_intensity\": 1.0,\n      \"glow_radius\": 15,\n      \"glow_scale\": 0.5,\n      \"grain\": 0.0,\n      \"blur\": 0.0\n    }\n}\n```\n\nSupported `blend_mode` values are `normal`, `add`, and `screen`.\n\nVisualiser-specific fields include:\n\n- `bars`: `bar_count`, `bar_width`, `spacing`, `corner_radius`, `mirror`, `color`, `secondary_color`, `peak_indicators`, `smoothing_type`, `smoothing_factor`, `analyzer_range`, `amplitude_scale`, `response_curve`\n- `wavebars`: `bar_count`, `bar_width`, `spacing_ratio`, `corner_radius`, `color`, `amplitude_scale`, `response_curve`, `reflection_alpha`, `height_ratio`, `smoothing_factor`\n- `waveform`: `line_width`, `color`, `fill_color`, `smoothing_factor` (0.0–1.0, default 0.65 — higher values produce a slower, more fluid line)\n- `circular`: `bar_count`, `radius`, `inner_radius`, `bar_width`, `rotation_speed`, `center_image`, `color`, `amplitude_scale`, `response_curve`, `min_bar_length`\n- `pulse`: `base_radius`, `max_scale`, `ring_width`, `ring_count`, `spoke_count`, `spoke_length`, `spoke_width`, `core`, `core_scale`, `rotation_speed`, `color`, `glow_intensity`\n- `particles`: `flow_mode`, `spawn_rate`, `velocity_multiplier`, `particle_size`, `color`, `lifetime`, `direction`, `spawn_spread`, `max_particles`\n- `text`: `content`, `font_path`, `font_size`, `color`, `alignment`, `padding`\n- `timer`: `style`, `color`, `background_color`, `corner_radius`\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n\nThis is an independent, original implementation. The following projects provided design inspiration:\n\n- **[wav2bar-reborn](https://github.com/Picorims/wav2bar-reborn)** by Picorims (MPL-2.0) — the multi-layer compositing model and layer-per-surface rendering approach.\n- **[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.\n\nNo source code from either project was copied or modified. The `wavebars-seewav-*` preset names are an explicit attribution to seewav's aesthetic.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbradsec%2Faudiospectr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbradsec%2Faudiospectr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbradsec%2Faudiospectr/lists"}