NextResponse's BodyInit type doesn't accept Node.js Buffer directly. Wrapping in Uint8Array satisfies the type and works at runtime. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ClickTrack
A self-hosted, open-source click track generator for cover bands.
Search any song, browse community-contributed tempo maps, and download a metronomic WAV file — accented downbeats, time signature changes, and tempo ramps included.
Features
- Song search via MusicBrainz, cached locally in PostgreSQL
- Community tempo maps in the open CTP (Click Track Protocol) format
- WAV generation — 44.1 kHz, 16-bit PCM, mono; 880 Hz accented / 440 Hz unaccented clicks
- Count-in — configurable 1–8 bar count-in prepended before bar 1
- Tempo ramps — linear BPM interpolation beat-by-beat for ritardando/accelerando sections
- Federated registry — pull CTP files from any public Git repo on a cron schedule
- Self-hosted — single
docker compose upgets you running
Quick Start (Docker)
1. Clone and configure
git clone https://github.com/your-org/clicktrack.git
cd clicktrack
cp .env.example .env
# Edit .env — at minimum set a strong POSTGRES_PASSWORD
2. Start the stack
docker compose up -d --build
This starts:
| Container | Role |
|---|---|
app |
Next.js 14 (port 3000, proxied via nginx) |
postgres |
PostgreSQL 16 with persistent volume |
redis |
Redis 7 for caching |
nginx |
Reverse proxy on ports 80/443 |
3. Open the app
Navigate to http://localhost (or your server's IP/domain).
4. Enable registry sync (optional)
Set REGISTRY_REPO in .env to a public GitHub repo URL containing CTP files, then:
docker compose --profile registry up -d
The registry-sync container will pull and import CTP files every REGISTRY_SYNC_INTERVAL seconds (default: 1 hour).
Development Setup
# Prerequisites: Node 20+, a local PostgreSQL 16, Redis 7
cp .env.example .env
# Update DATABASE_URL and REDIS_URL to point at your local services
npm install
# Apply schema
psql $DATABASE_URL -f lib/db/schema.sql
# Start dev server with hot reload
npm run dev
Or with Docker (hot-reload via volume mount):
docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build
CTP — Click Track Protocol
CTP is an open JSON format for describing a song's complete tempo map. A CTP file is a plain .ctp.json file that can be version-controlled and shared.
Full example
{
"version": "1.0",
"metadata": {
"title": "Bohemian Rhapsody",
"artist": "Queen",
"mbid": "b1a9c0e0-9c4a-4a6b-8b5a-1234567890ab",
"duration_seconds": 354,
"contributed_by": "freddie_fan",
"verified": false,
"created_at": "2026-04-01T00:00:00Z"
},
"count_in": {
"enabled": true,
"bars": 1,
"use_first_section_tempo": true
},
"sections": [
{
"label": "Intro (Ballad)",
"start_bar": 1,
"bpm": 72.0,
"time_signature": { "numerator": 4, "denominator": 4 },
"transition": "step"
},
{
"label": "Rock Section",
"start_bar": 45,
"bpm": 152.0,
"time_signature": { "numerator": 4, "denominator": 4 },
"transition": "step"
},
{
"label": "Outro (slowing)",
"start_bar": 120,
"bpm_start": 152.0,
"bpm_end": 72.0,
"time_signature": { "numerator": 4, "denominator": 4 },
"transition": "ramp"
}
]
}
Schema rules
| Field | Type | Notes |
|---|---|---|
version |
"1.0" |
Must be exactly "1.0" |
metadata.mbid |
UUID or null |
MusicBrainz Recording ID |
metadata.duration_seconds |
number | Total song duration (excluding count-in) |
count_in.bars |
1–8 | Number of count-in bars |
sections[*].start_bar |
integer ≥ 1 | Must start at 1, strictly ascending |
sections[*].transition |
"step" | "ramp" |
Step = instant change; ramp = linear interpolation |
sections[*].bpm |
20–400 | Only for step sections |
sections[*].bpm_start / bpm_end |
20–400 | Only for ramp sections |
sections[*].time_signature.denominator |
1, 2, 4, 8, 16, 32 | Must be a power of 2 |
Click sounds
| Beat | Frequency | Amplitude |
|---|---|---|
| Beat 1 (downbeat) | 880 Hz | Accented |
| Other beats | 440 Hz | Normal |
Both use a 12 ms sine wave with exponential decay (e^(-300t)).
Community Registry
The registry is a public Git repository containing .ctp.json files organised by artist:
registry-repo/
q/
queen/
b1a9c0e0-9c4a-4a6b-8b5a-1234567890ab.ctp.json # Bohemian Rhapsody
t/
the-beatles/
...
Contributing a tempo map
- Fork the community registry repo.
- Create a
.ctp.jsonfile for your song. Use the schema above. - Validate your file: paste the JSON into a CTP validator (coming soon) or use the API:
curl -X POST http://localhost/api/tracks \ -H "Content-Type: application/json" \ -d @your-song.ctp.json - Open a pull request to the registry repo.
Once merged, any ClickTrack instance syncing that registry will import your map automatically.
API Reference
GET /api/songs?q=<query>&limit=<n>
Search for songs. Hits local DB first, falls back to MusicBrainz.
Response:
{ "songs": [...], "total": 5 }
GET /api/tracks?mbid=<uuid>
List all community tempo maps for a song.
POST /api/tracks
Submit a new CTP document. Body must be a valid CTP JSON.
Response: 201 Created with the stored map record.
GET /api/generate?id=<tempo-map-uuid>&count_in=true
Generate and download a WAV click track.
id— UUID of the tempo map (from/api/tracks)count_in—trueorfalse(default:true)
Returns audio/wav with Content-Disposition: attachment.
Architecture
Browser
└── Next.js App Router (app/)
├── (web)/page.tsx — song search
├── (web)/track/[id]/ — track page + player
└── api/
├── songs/ — search + MB integration
├── tracks/ — CTP CRUD
└── generate/ — WAV rendering
lib/
├── ctp/
│ ├── schema.ts — TypeScript types
│ ├── validate.ts — Zod validation
│ └── render.ts — CTP → WAV (Node.js)
├── db/client.ts — pg Pool + query helpers
├── musicbrainz/client.ts — rate-limited MB API
└── registry/sync.ts — Git registry pull + upsert
Environment Variables
See .env.example for all variables with descriptions.
| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_URL |
Yes | — | PostgreSQL connection string |
REDIS_URL |
Yes | — | Redis connection URL |
NEXT_PUBLIC_APP_NAME |
No | ClickTrack |
UI display name |
REGISTRY_REPO |
No | — | GitHub repo URL for CTP registry |
REGISTRY_SYNC_INTERVAL |
No | 3600 |
Sync interval in seconds |
MUSICBRAINZ_USER_AGENT |
No | built-in | User-Agent for MB API requests |
License
MIT