SlowedLab — private browser audio studio (slowedlab.app)
An advanced real-time audio player supporting all effects (slow, reverb) that elevate your music/audio playing to next level. Paste a SoundCloud link to play it instantly, or upload your own files.
- ☁️ SoundCloud link import — paste a link, play it directly in the studio
- 🎚️ Playback speed from 0.50× to 1.50×
- 🌊 Professional reverb engine
- 🎵 Real-time audio effects (EQ, compression, distortion)
- 📊 Waveform visualization
- 💾 Preset management
- 💝 100% Free with optional support
- Docker with Docker Compose v2 (
docker compose version)
git clone https://github.com/anasfik/SlowedLab
cd SlowedLab
# First run (or after dependency / Dockerfile changes)
docker compose up --build -d
# Follow logs
docker compose logs -f
# Stop (keeps audio cache in backend/data)
docker compose downOpen:
- App: http://localhost:4000
- API health: http://localhost:4001/api/health
Both services have healthchecks; docker compose ps should show
healthy for each after ~1 minute (frontend needs the dev server
compile on first boot).
If 4000/4001 are already taken on your machine, copy .env.example to
.env and set FRONTEND_PORT / BACKEND_PORT to free host ports. .env
is git-ignored.
| Variable | Default | Meaning |
|---|---|---|
FRONTEND_PORT / BACKEND_PORT (.env) |
4000 / 4001 |
Host ports published by compose; container ports are always 4000/4001 |
PORT (backend) |
4001 |
API port inside the container |
MAX_AUDIO_MINUTES |
15 |
Max link-import duration |
LINKIMPORT_CONCURRENCY |
2 |
Parallel yt-dlp downloads |
AUDIO_CACHE_MB |
1024 |
Audio cache cap |
AUDIO_CACHE_FILES |
100 |
Max cached files |
YTDLP_COOKIES_FILE |
auto | Explicit cookie path override; otherwise backend/data/cookies.txt is auto-detected |
YTDLP_PROXY (.env) |
unset | Residential proxy for yt-dlp (resolve + download); health reports only configured:true/false, never the URL |
BACKEND_PROXY_URL (frontend) |
http://backend:4001 |
Where the dev server forwards same-origin /api/* calls (docker service DNS; host npm start falls back to http://localhost:4001) |
Live source mounts (backend/src, backend/tsconfig.json, frontend/src,
frontend/public) mean local edits hot-reload inside the containers;
dependency changes (package.json) need docker compose up --build.
The frontend never bakes in an API host: it always calls same-origin
/api/*. Never set REACT_APP_API_URL — an absolute URL baked into the
bundle breaks every visitor whose machine is not yours.
Downloaded audio persists in backend/data/audio-cache (host-mounted).
cookies.txt is git-ignored — never commit it.
SoundCloud marks a minority of streams DRM-protected for third-party
clients — those tracks can't be imported, and the app says so instead of
hanging. If a public track fails to resolve, a Netscape cookies.txt
exported from a logged-in browser tab and saved as backend/data/cookies.txt
(git-ignored — never commit it) is auto-detected and retried with it.
Confirm pickup with:
curl -s http://localhost:4001/api/health
# "linkImport":{"ytDlp":"yt-dlp","proxy":{"configured":false},"cookies":{"configured":true,...}}-
Clone the repo on the server and start the stack:
docker compose up --build -d docker compose ps # wait for both services to report healthy -
Point a domain at the server and put nginx in front. A ready-to-use config lives at
deploy/nginx/slowedlab.app.conf(it proxies/to the app and/api/to the API, adds caching/security headers and gzip, and leaves port 80 free for the ACME challenge):sudo cp deploy/nginx/security-headers.conf /etc/nginx/snippets/security-headers.conf sudo cp deploy/nginx/slowedlab.app.conf /etc/nginx/sites-available/slowedlab.app sudo ln -sf /etc/nginx/sites-available/slowedlab.app /etc/nginx/sites-enabled/ # edit server_name + the two proxy_pass ports to match your .env sudo nginx -t && sudo systemctl reload nginx
-
Issue the certificate (this also flips nginx to HTTPS and redirects port 80):
sudo certbot --nginx -d your-domain.tld
-
Confirm:
https://your-domain.tldloads the app andhttps://your-domain.tld/api/healthreturns{"status":"ok"}.
Notes:
- The frontend calls the API through same-origin
/api/*, so changing the public URL or the host ports never requires a frontend rebuild. - Keep
backend/dataon a volume; set theMAX_*/ cache caps above to bound disk use. - Certbot renews automatically;
sudo certbot renew --dry-runverifies it.
- Node.js 18+ and npm installed
cd backend
npm install
npm run dev # ts-node, hot reload → http://localhost:4001For a compiled run: npm run build && npm start (serves the same API from
dist/ without ts-node).
cd frontend
npm install
cp .env.local.example .env.local # optional: PORT, OpenAI key
npm startThis starts the React dev server on http://localhost:3000 by default, or
whatever PORT you set in .env.local. The dev server proxies /api/* to
http://localhost:4001, so keep the backend running on that port.
- Open the app at http://localhost:4000
- Upload an audio file using the upload area (supports MP3, WAV, FLAC, OGG, AAC, and more)
- Apply effects:
- ⚡ Speed: Slow the track down or push it faster. Pitch shifts with it, which is what gives the slowed sound its depth.
- ✨ Reverb: Add spatial depth and ambient effects
- EQ: Shape frequency response (bass, treble)
- Compressor: Control dynamic range
- Distortion: Add grit and aggression
- Preview in real-time with the interactive waveform viewer
- Manage presets using built-in effect presets or create custom ones
- Export your processed audio as a WAV file
- Report bugs using the 🐞 button for feedback and improvements
Paste a SoundCloud link in the empty state or queue panel.
The backend resolves metadata, mints a short-lived stream URL the browser
fetches directly (fastest, no server bandwidth), and falls back to a
server-side download with yt-dlp + ffmpeg MP3 conversion, cached under
backend/data/audio-cache and fed into the normal playback pipeline.
Spotify and YouTube links are rejected: Spotify offers no legal full-track
download path, and YouTube blocks server-side downloads from datacenter IPs.
VM deploy notes (also see above):
- The backend image ships
yt-dlp,ffmpeg, anddenoalready. - Cap downloads with
MAX_AUDIO_MINUTES(default 15),LINKIMPORT_CONCURRENCY(default 2),AUDIO_CACHE_MB(default 1024).
- Frontend: React 18 (Create React App), TypeScript, Web Audio API
- Backend: Node.js, Express, TypeScript
- Audio Processing: Web Audio API (100% client-side)
- Containerization: Docker & Docker Compose
- Styling: CSS3 with modern gradients and animations
- File Support: MP3, WAV, FLAC, OGG, AAC, and more
- File Size Limit: 200MB per file
- Processing: 100% client-side (your files never leave your browser)
- Storage: Browser caching via IndexedDB for faster reloads
- Error Handling: Comprehensive validation and user-friendly error messages
- Real-time Performance: Sub-50ms effect processing
- UI Responsiveness: Touch-friendly waveform interactions
This project is 100% free. If you find it useful, consider supporting on Ko-fi ☕
Made with ❤️ for audio enthusiasts | SlowedLab - Elevate Your Audio Experience