MuViz is a Django music visualizer app with two playback/visual modes and karaoke-style lyrics.
Demo -> MuViz
(might experience some delays in loading times because of free tier hosting)
- Upload local audio files (single or multi-file queue upload, up to 10 files per batch).
- Ingest remote audio links (SoundCloud, Internet Archive, direct audio URL).
- Play audio in a full-screen visualizer player.
- Navigate queued tracks with on-screen previous/next controls.
- Switch between:
- Milkdrop mode (
butterchurn) - Spectrum mode (
audiomotion-analyzer)
- Milkdrop mode (
- Control playback (seek, volume, mute, fullscreen, hide controls, keyboard shortcuts).
- Show synced lyrics with progressive per-word highlight and availability-aware UI state.
- Cache resolved LRC lyrics on the track record.
- Quality-scored Milkdrop catalog with profile filters:
ultra,high,balanced,all. - Favorites-only filtering and weighted random preset selection.
- Beat-reactive Milkdrop switching with sensitivity control.
- Expanded preset sources via bundled extra packs (
Extra,Extra2,MD1) plus curated weekly internet presets. - Startup gate overlay in player so interactions unlock only after queue and lyric preparation completes.
- Backend: Django 5
- Database: SQLite by default (PostgreSQL via
DATABASE_URL) - Audio metadata:
mutagen - Remote ingestion/lyrics HTTP calls:
requests - SoundCloud resolution/download:
yt-dlp - Static serving in deployment:
whitenoise - Frontend: Django templates + vanilla JS + CSS
Track.source values:
uploadsoundcloudinternet_archiveremote_urlyoutube(legacy value retained for existing records)
Notes:
- SoundCloud is loaded as cached file download in backend flow.
- If SoundCloud formats are preview-only,
preview_only=trueis stored and a warning is shown in player UI. - Internet Archive and direct URLs may stream directly if CORS + byte-range headers are available; otherwise they are cached locally.
forge-website/
manage.py
requirements.txt
build.sh
Dockerfile
render.yaml
muviz/
settings.py
urls.py
visualizer/
models.py
views.py
urls.py
forms.py
services/
lyrics.py
remote_audio.py
migrations/
tests.py
templates/visualizer/
base.html
index.html
player.html
static/
css/
design-system.css
layout.css
visualizer.css
js/
app.js
presets.js
visualizer-engine.js
open-karaoke-lyrics.js
audio-engine.js
media/
tracks/
Static file note:
- Runtime static files are taken from top-level
static/(STATICFILES_DIRSinmuviz/settings.py). muviz/static/contains duplicates and is not the primary runtime source.
- Create and activate virtual environment.
python3 -m venv .venv
source .venv/bin/activate- Install dependencies.
pip install -r requirements.txt- Run migrations.
python manage.py migrate- Start server.
python manage.py runserver- Open:
http://127.0.0.1:8000/
Defined in muviz/settings.py.
SECRET_KEY(required in production)DEBUG(default:True)ALLOWED_HOSTS(CSV, default:*)DATABASE_URL(optional, enables non-SQLite DB)CSRF_TRUSTED_ORIGINS(CSV)MEDIA_ROOT(default:<BASE_DIR>/media)
Upload constraints:
- Max size: 50 MB
- Allowed extensions:
.mp3,.wav,.ogg,.flac,.aac,.m4a,.webm
GET /-> landing pageGET /play/<track_id>/-> player pagePOST /api/upload/-> upload local filePOST /api/link/-> ingest remote link (JSON or form)GET /api/presets/-> list presetsPOST /api/presets/save/-> create custom presetDELETE /api/presets/<preset_id>/delete/-> delete non-built-in presetGET /api/lyrics/<track_id>/-> lyrics availability + provider metadataGET /api/lyrics/<track_id>/lrc/-> normalized LRC plain text
- Metadata:
title,artist,album,duration - Storage and playback:
file,playback_url,mime_type,file_size - Source fields:
source,source_url,source_identifier - Lyrics cache:
lyrics_lrc - Remote status:
preview_only - Helper properties:
audio_url,has_audio_source
nameconfig(JSON)is_builtin
Player template: templates/visualizer/player.html
Frontend orchestration: static/js/visualizer-engine.js
- Creates Web Audio context and media element source.
- Initializes selected visualizer mode on demand.
- Default mode is
milkdrop. - Supports Milkdrop preset browsing/search/auto-cycle plus quality profiles and beat-reactive transitions.
- Supports Spectrum style + gradient + reflection/radial/mirror controls.
- Handles play/pause/seek/volume/mute/fullscreen/hide-controls.
- Supports queue navigation controls and queue status display when multiple tracks are queued.
- Locks interactive controls during startup preparation and unlocks after lyrics precheck/preload stage.
- Fetches and renders LRC lyrics through
OpenKaraokeLyricsDisplay.
Keyboard shortcuts:
Space: play/pauseL: lyrics on/offF: fullscreenH: hide/show bottom barN: next Milkdrop presetP: previous Milkdrop presetR: random Milkdrop presetB: toggle beat-reactive Milkdrop switching[: previous track in queue]: next track in queueLeft/Right: seek -/+ 5 secondsUp/Down: volume -/+ 5%
Backend service: visualizer/services/lyrics.py
Resolution order:
- Use cached
Track.lyrics_lrcif available. - Query LRCLIB (
https://lrclib.net/api/search). - Fallback to
syncedlyricspackage lookup. - If only plain lyrics are found, estimate timestamps into LRC.
Frontend renderer: static/js/open-karaoke-lyrics.js
- Parses synced and plain lyrics.
- Rebuilds compact/collapsed text into display tokens.
- Computes per-word progress and highlights active word segment.
python manage.py check
python manage.py makemigrations
python manage.py migrate
python manage.py testCurrent test status in this workspace:
python manage.py test visualizerruns 12 tests.- All 12 tests are passing.
- Build:
bash build.sh - Start:
gunicorn muviz.wsgi:application --bind 0.0.0.0:$PORT --workers 2 --timeout 180
docker build -t muviz .
docker run -p 8000:8000 muvizContainer startup command runs collectstatic, migrate, then gunicorn.
- Upload rejected: check extension/size limits in
muviz/settings.py. - Visualizer not rendering: check CDN access for
butterchurn,butterchurn-presets,audiomotion-analyzer. - Lyrics not available: provider may not have track data; app returns
available=false. - Frontend changes not appearing: hard refresh the browser to clear cached static assets.