Professional Audio Normalization for Everyone
A free, web-based audio normalization tool. Upload your audio, pick a preset, and download broadcast-ready files. No login required. No data retained.
# Clone the repository
git clone https://github.com/miikkis-gh/audiolevel.git
cd audiolevel
# Start all services
docker compose up -d
# Open in browser
open http://localhost:80
# Prerequisites: Bun, Redis, FFmpeg, ffmpeg-normalize
# Backend
cd backend
bun install
bun run dev
# Frontend (new terminal)
cd frontend
bun install
bun run dev
Visit http://localhost:5173 for the frontend.
| Preset | Target LUFS | True Peak | Best For |
|---|---|---|---|
| Podcast | -16 | -1.5 dB | Spoken word, interviews |
| Broadcast | -23 | -2.0 dB | TV/Radio (EBU R128) |
| YouTube | -14 | -1.0 dB | YouTube uploads |
| Streaming | -14 | -1.0 dB | Spotify, Apple Music |
| Mastering | -9 | -0.3 dB | Loud, punchy masters |
| Audiobook | -18 | -3.0 dB | ACX compliance |
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Browser │────▶│ Frontend │────▶│ Backend │
│ │◀────│ (Svelte) │◀────│ (Hono) │
└─────────────┘ └─────────────┘ └──────┬──────┘
│ │
│ WebSocket │ Jobs
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Nginx │ │ BullMQ │
│ (optional) │ │ + Redis │
└─────────────┘ └──────┬──────┘
│
▼
┌─────────────┐
│ FFmpeg │
│ Normalize │
└─────────────┘
Backend
Frontend
POST /api/upload Upload audio file
GET /api/upload/job/:id Get job status
GET /api/upload/job/:id/download Download processed file
GET /api/presets List available presets
GET /api/health Health check
WS /ws Real-time progress updates
// Subscribe to job updates
ws.send(JSON.stringify({ type: 'subscribe', jobId: 'abc123' }))
// Receive progress
{ type: 'progress', jobId: 'abc123', percent: 45, stage: 'normalizing' }
// Receive completion
{ type: 'complete', jobId: 'abc123', downloadUrl: '/api/upload/job/abc123/download' }
Full API documentation: docs/API.md
# Backend
PORT=3000
REDIS_URL=redis://localhost:6379
UPLOAD_DIR=./uploads
OUTPUT_DIR=./outputs
MAX_FILE_SIZE=104857600 # 100MB
FILE_RETENTION_MINUTES=15
MAX_CONCURRENT_JOBS=4
PROCESSING_TIMEOUT_MS=300000 # 5 minutes
# Frontend
VITE_API_URL=http://localhost:3000
VITE_WS_URL=ws://localhost:3000/ws
This application runs behind an nginx reverse proxy that handles SSL termination.
Host nginx configuration:
http://localhost:8081Docker nginx configuration:
# Using production compose file
docker compose -f docker-compose.prod.yml up -d
Deployment guide: docs/DEPLOYMENT.md
# Run tests
cd backend && bun test
cd frontend && bun test
# Type checking
cd backend && bun run typecheck
cd frontend && bun run check
# Build for production
cd frontend && bun run build
audiolevel/
├── backend/
│ ├── src/
│ │ ├── index.ts # Server entry
│ │ ├── routes/ # API handlers
│ │ ├── services/ # Business logic
│ │ ├── workers/ # Job processors
│ │ └── websocket/ # Real-time handlers
│ └── package.json
├── frontend/
│ ├── src/
│ │ ├── App.svelte # Main component
│ │ ├── app.css # Tailwind CSS 4 theme
│ │ ├── lib/components/
│ │ │ ├── audiolevel/ # Audio processing UI
│ │ │ │ ├── AudioLevelUI.svelte
│ │ │ │ ├── ParticleSphere.svelte
│ │ │ │ ├── BatchReport.svelte
│ │ │ │ ├── SingleReport.svelte
│ │ │ │ └── OverrideSelector.svelte
│ │ │ └── ... # Other components
│ │ └── stores/ # State management
│ └── package.json
├── docs/ # Documentation
├── nginx/ # Nginx config
└── docker-compose.yml
| Resource | Limit |
|---|---|
| File size | 100 MB |
| Uploads per hour | 10 per IP |
| File retention | 15 minutes |
| Processing timeout | 5 minutes |
| Concurrent jobs | 4 |
git checkout -b feature/amazing)git commit -m 'Add amazing feature')git push origin feature/amazing)MIT License — see LICENSE for details.
Built with FFmpeg, Bun, and a love for good audio.