A Discord soundboard bot with a web interface. Browse and search sounds on a web page, then play them in Discord voice channels or copy links to paste in chat.
- Web Interface: Browse, search, and preview sounds from a responsive web UI
- Discord Integration: Play sounds in voice channels via slash commands
- Sound Management: Add sounds from URLs (supports any site yt-dlp can download from)
- Python 3.14+
- uv (Python package manager)
- FFmpeg (for audio processing)
- Node.js 24+ (for building web assets)
- A Discord bot token
# Install Python dependencies
uv sync
# On Linux/macOS, include uvloop for better async performance
uv sync --extra unix
# Install Node.js dependencies and build web assets
npm ci
npm run buildCreate a .env file in the project root:
# Required: Discord bot token
token=your_discord_bot_token_here
# Optional: Comma-separated guild IDs for local command registration
test_guild_ids=123456789,987654321
# Optional: Override default paths
# state_file=config/state.json
# sounds_folder=mount/sounds
# static_folder=web/dist
# templates_folder=web/templatemkdir -p mount/soundsuv run python -m soundbotThe web interface will be available at http://localhost:8080.
- Copy
.env.exampleto.envand configure your settings - Run:
docker-compose up --build| Variable | Description |
|---|---|
token |
Discord bot token (required) |
test_guild_ids |
Comma-separated guild IDs for command registration |
SOUNDBOT_PORT_8080 |
Host port to map to container port 8080 |
DOCKERCONFDIR |
Base path for config/data volumes |
├── src/soundbot/ # Python backend
│ ├── core/ # Settings and state management
│ ├── discord/ # Discord bot client and commands
│ ├── models/ # Data models
│ ├── services/ # Business logic
│ └── web/ # FastAPI web server and routes
├── web/ # Frontend assets
│ ├── scripts/ # TypeScript source
│ ├── styles/ # CSS
│ ├── static/ # Static assets
│ └── template/ # Jinja2 templates
├── config/ # Runtime data (sounds, state)
├── Dockerfile
├── docker-compose.yml
└── pyproject.toml
# Install all dependencies
uv sync
npm ci
# Build web assets
npm run build
# Run the bot (serves web UI at http://localhost:8080)
uv run python -m soundbotNote: A file lock prevents multiple instances from running simultaneously. If using Docker, stop it first with
docker compose stop.
The bot ships a small CLI for offline maintenance and ingest. Stop the running bot first (the state lock prevents concurrent instances):
docker compose stop # if running in docker
uv run python -m soundbot.cli <subcommand>
docker compose up -dSubcommands:
# Clip a sound from a local video file. Source is NOT copied — the absolute
# path is stored in state, so future trim/regenerate operations re-read from
# the original. Timestamps accept HH:MM:SS, MM:SS, or seconds.
uv run python -m soundbot.cli clip <video> <name> <start> <end> [--volume N] [--overwrite]
# Re-normalize all (or one) sound's audio from its original. Use after
# changing audio_target_lufs.
uv run python -m soundbot.cli regenerate-audio [--sound NAME] [--dry-run]
# Find sounds whose audio file is missing on disk.
uv run python -m soundbot.cli check-sounds [--remove]clip requires the source to be reachable from wherever the bot runs. For
the Docker container, that means bind-mounting media shares (e.g.
/mnt/tv:/mnt/tv:ro) into the service.
Run these in separate terminals:
# Terminal 1: Watch and rebuild CSS/TypeScript on changes
npm run dev
# Terminal 2: Run the Python backend
uv run python -m soundbot- Windows: Uses standard
asyncioevent loop - Linux/macOS: Uses
uvloopfor improved async performance (install withuv sync --extra unix)
| Command | Description |
|---|---|
/add <name> <url> |
Add a new sound from URL (YouTube, etc.) with optional start/end times |
/delete <name> |
Delete a sound |
/rename <old> <new> |
Rename a sound |
/trim <name> |
Set new start/end timestamps |
/adjust <name> |
Adjust timestamps relatively (add/remove seconds) |
/volume <name> <level> |
Set volume (0.1-5.0, 1.0 = normal) |
/info <name> |
Get sound details |
/list [search] |
List all sounds or search |
| Command | Description |
|---|---|
!soundname |
Play a sound (supports partial matching) |
!stop |
Stop current playback |
!leave |
Leave voice channel |
!sounds [search] |
Quick list of sounds |
The bot automatically joins the best voice channel:
- The channel you're currently in
- A channel with users who recently used the soundbot
- The most populated channel
MIT