An audio recognition and music discovery system using audio fingerprinting to identify songs from a Spotify-backed catalog — similar to Shazam, but self-hosted.
sonicLens lets you upload an audio clip and identify the matching song from a catalog you build from Spotify. It uses FFT-based combinatorial hashing for fingerprinting, similar to the Shazam algorithm.
Key features:
- Audio recognition with confidence scoring
- Song catalog built from Spotify tracks, albums, or playlists
- Full-track audio streaming via a Go/librespot microservice
- Spotify OAuth integration for user authentication and playback
- Recognition history per user
- JWT-authenticated REST API
┌─────────────────────────────────┐
│ Client (Browser / API consumer)│
└──────────────┬──────────────────┘
│
┌───────────▼────────────┐ ┌─────────────────────┐
│ Spring Boot REST API │────▶│ Go Microservice │
│ Java · Port 8082 │ │ Port 8888 │
│ │◀────│ │
│ - JWT auth │ │ - Spotify full- │
│ - Audio fingerprinting│ │ track streaming │
│ - Recognition logic │ │ - librespot OAuth │
│ - Spotify API bridge │ │ - WAV encoding │
│ - Song/history mgmt │ └─────────────────────┘
└───────┬────────────────┘
│
┌─────┴──────┐ ┌───────────────────┐
│ PostgreSQL │ │ GCP Cloud Storage│
└────────────┘ └───────────────────┘
- Java 17+
- Go 1.21+
- PostgreSQL
pkg-configand librespot native dependencies (see Go service setup)- A Spotify Developer app (Client ID + Secret)
- (Optional) GCP project with Cloud Storage
The Go service handles full-track audio streaming from Spotify. It must be running before the Spring Boot app starts.
cd librespot-service
PKG_CONFIG_PATH="/opt/homebrew/lib/pkgconfig" go run .On first run, a browser window opens for Spotify OAuth login. Credentials are saved to ~/.sonicLens/go-librespot/credentials.json and reused on subsequent starts.
Run with pm2 (recommended for persistent deployments):
# Start
pm2 start "go run ." --name librespot-service
# For faster restarts, build a binary first
go build -o librespot-service .
pm2 start ./librespot-service --name librespot-serviceUseful pm2 commands:
pm2 list # see all running services
pm2 logs librespot-service # tail logs
pm2 restart librespot-service # restart
pm2 stop librespot-service # stopConfigure src/main/resources/application.properties:
# Database
spring.datasource.url=jdbc:postgresql://<host>:5432/<db>
spring.datasource.username=<user>
spring.datasource.password=<password>
# Spotify
spotify.client-id=<your-client-id>
spotify.client-secret=<your-client-secret>
spotify.redirect-uri=http://<host>:8082/callback
# Go service (must match where librespot-service is running)
librespot.service.url=http://localhost:8888
# JWT
jwt.secret=<your-secret>
# GCP (optional)
gcp.storage.bucket-name=<bucket>
gcp.storage.project-id=<project>Then build and run:
./gradlew bootRunThe API will be available on port 8082.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /auth/register |
— | Create a user account |
| POST | /auth/login |
— | Get a JWT token |
| GET | /auth/spotify |
JWT | Get Spotify OAuth authorization URL |
| GET | /callback |
— | Spotify OAuth redirect handler |
| GET | /auth/spotify/token |
JWT | Get/refresh Spotify access token |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /songs/add |
JWT | Add song by Spotify track ID |
| POST | /songs/add-url |
JWT | Add from Spotify track/album/playlist URL |
| GET | /songs/search?q= |
— | Search Spotify |
| POST | /songs/upload |
JWT | Upload a local audio file |
| POST | /songs/seed |
JWT | Bulk-seed catalog from Spotify featured/new releases |
| GET | /songs |
— | List all songs in catalog |
| GET | /songs/get/{id} |
— | Get song by ID |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /recognize |
JWT | Upload an audio clip for identification |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /history |
JWT | Get current user's recognition history |
| DELETE | /history/{id} |
JWT | Delete a history entry |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /users/me |
JWT | Get current user info |
| GET | /users/{id} |
— | Get user by ID |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /admin/librespot/status |
— | Check Go service readiness |
| POST | /admin/librespot/reconnect |
— | Trigger Spotify re-authentication |
sonicLens uses a Shazam-style combinatorial hash fingerprinting approach:
- Decode any audio format to mono 16-bit PCM at 11,025 Hz
- Spectrogram — 4,096-sample FFT with 50% overlap and Hann window
- Peak detection — find peak magnitude across 6 log-scale frequency bands (40 Hz – 4 kHz)
- Combinatorial hashing — pair each peak (anchor) with nearby peaks (target) within a time zone to create a hash encoding anchor frequency, target frequency, and time delta
- Recognition — query the database for matching hashes, vote by
(songId, timeDelta), and declare the winner above a 5% confidence threshold
The librespot service runs on port 8888:
| Method | Endpoint | Description |
|---|---|---|
| GET | /status |
Returns {"ready": bool} |
| POST | /reconnect |
Clears credentials and triggers new OAuth login |
| GET | /stream/{trackId} |
Streams full track as WAV (44.1 kHz, stereo, 16-bit PCM) |
| Layer | Technology |
|---|---|
| Backend API | Java 17, Spring Boot, Spring Security (JWT) |
| Audio streaming | Go, go-librespot |
| Fingerprinting | JTransforms (FFT) |
| Database | PostgreSQL (JPA/Hibernate) |
| File storage | GCP Cloud Storage |
| Auth | JWT (JJWT), Spotify OAuth2 |
| Build | Gradle |
| Process mgmt | pm2 |