Optimize rekordbox playlists for harmonic mixing using Google OR-Tools constraint programming.
- ✨ Longest Path Optimization: Finds the maximum number of tracks that can be mixed together
- ⚡ Energy Flow Management: Enforces non-decreasing energy progression with max +1 increase per transition (1-5 range)
- 🎵 Harmonic Mixing: Uses the Camelot Wheel system for key compatibility
- 🚀 Energy Boost Transitions: Strategic +2 hour jumps (5A→7A) for crowd excitement, used sparingly (max 3 per set)
- 📊 Transition Quality Scoring: Intelligently weights transitions by harmonic quality (1.0 = perfect, 0.6 = energy boost)
- 🎧 Rekordbox Integration: Read playlists directly from your local Rekordbox 6/7 database (tested with v7.2.8)
- 🔊 BPM Matching: Supports direct, halftime, and doubletime BPM compatibility
- ⚙️ Configurable Strictness: STRICT, MODERATE, or RELAXED harmonic compatibility levels
- 📤 Rekordbox Export: Export results to Rekordbox XML or write directly to the Rekordbox database
- 🚀 Fast: Powered by Google OR-Tools CP-SAT solver (award-winning constraint solver)
- 📦 SDK + CLI: Use as a Python library or command-line tool
uv add djkr8Or with pip:
pip install djkr8from djkr8 import PlaylistOptimizer, Track, HarmonicLevel
tracks = [
Track(id="track_001", key="8A", bpm=128),
Track(id="track_002", key="8B", bpm=130),
Track(id="track_003", key="9A", bpm=125),
]
optimizer = PlaylistOptimizer(
bpm_tolerance=10,
allow_halftime_bpm=True,
max_violation_pct=0.10,
harmonic_level=HarmonicLevel.STRICT,
)
result = optimizer.optimize(tracks)
for i, track in enumerate(result.playlist, 1):
print(f"{i}. {track.id} ({track.key}, {track.bpm} BPM)")# Basic usage
djkr8 tracks.json
# With custom settings
djkr8 tracks.json --bpm-tolerance 8 --harmonic-level moderate
# Energy flow management
djkr8 tracks.json --energy-weight 5.0 # Prioritize higher energy tracks
djkr8 tracks.json --allow-energy-drops # Disable strict non-decreasing energy constraint
# Save results to JSON
djkr8 tracks.json --output result.json
# Use with Rekordbox (v6/v7)
krate --rekordbox # List playlists
krate --rekordbox --playlist "Techno" # Optimize specific playlist
krate --rekordbox --playlist "Techno" --output r.xml # Export to Rekordbox XML
krate --rekordbox --playlist "Techno" --write-to-db # Write directly to Rekordbox DB
# Enable verbose logging
djkr8 tracks.json -v # INFO level
djkr8 tracks.json -vv # DEBUG levelThe tool provides two ways to save your optimized playlists back to Rekordbox:
Export the results to an XML file that can be imported into Rekordbox:
krate --rekordbox --playlist "My Playlist" --output optimized.xmlIn Rekordbox:
- Go to File > Import > Import Playlist
- Select
optimized.xml - The playlist will appear in the
ROOTfolder (e.g.,My Playlist_20260115_120000)
Write the optimized playlist directly to your Rekordbox 6 database:
krate --rekordbox --playlist "My Playlist" --write-to-db- Close Rekordbox before running this command.
- This modifies your
master.dbfile directly. - Backup your database before using this feature.
JSON file with tracks containing id, key (Camelot notation), and bpm:
{
"tracks": [
{"id": "track_001", "key": "8A", "bpm": 128},
{"id": "track_002", "key": "8B", "bpm": 130},
{"id": "track_003", "key": "9A", "bpm": 125}
]
}Adjacent tracks must have compatible BPMs within tolerance:
| Track A | Track B | Tolerance | Match? | Reason |
|---|---|---|---|---|
| 128 BPM | 130 BPM | ±10 | ✅ | Direct (diff = 2) |
| 128 BPM | 64 BPM | ±10 | ✅ | Half-time (128 = 64×2) |
| 75 BPM | 150 BPM | ±10 | ✅ | Double-time (75×2 = 150) |
| 128 BPM | 100 BPM | ±10 | ❌ | Too far |
Harmonic compatibility levels:
STRICT (default):
- Same key (8A → 8A)
- ±1 hour same letter (8A → 7A, 9A)
- Same hour different letter (8A → 8B)
MODERATE:
- Above + ±1 hour different letter (8A → 9B, 7B)
RELAXED:
- Above + ±3 hours (8A → 5A, 11A)
Maximize playlist length while optimizing transition quality and respecting harmonic constraints.
Multi-Objective Optimization:
- Primary: Maximize playlist length (more tracks = better)
- Secondary: Maximize transition quality scores (smoother mixes = better)
- Constraints: Energy flow, harmonic violations, energy boosts
| Parameter | Default | Description |
|---|---|---|
bpm_tolerance |
10.0 | Maximum BPM difference for direct match |
allow_halftime_bpm |
True | Enable half/double-time matching |
max_violation_pct |
0.10 | Max percentage of non-harmonic transitions |
harmonic_level |
STRICT | Harmonic compatibility strictness |
enforce_energy_flow |
True | Enforce non-decreasing energy with max +1 increase (next >= current and next - current <= 1) |
max_energy_boosts |
3 | Maximum number of energy boost transitions (+2 hours on Camelot wheel) per playlist |
transition_quality_weight |
10.0 | Weight for transition quality in objective function (higher = prefer quality over length) |
time_limit_seconds |
60.0 | Solver time limit |
Based on Mixed In Key's Energy Boost technique, you can use strategic harmonic jumps to add excitement:
+2 Hours (Energy Boost): Jump forward 2 hours on the Camelot wheel
# Example: 5A → 7A
# Gives a quick burst of energy to wake up the dancefloor
optimizer = PlaylistOptimizer(max_energy_boosts=3) # Allow up to 3 boosts-5 Hours (Armin Variation): Jump backward 5 hours
# Example: 12A → 7A
# Used by Armin Van Buuren and Skrillex for dramatic energy spikesBest Practices (from professional DJ analysis):
- Use energy boosts sparingly (every 20-30 minutes, not constantly)
- Top DJs average 2-3 energy boosts per set
- Combine with energy level increases for maximum impact
- Default limit:
max_energy_boosts=3per playlist
The optimizer now scores each transition by harmonic quality:
| Transition Type | Quality Score | Example |
|---|---|---|
| Perfect match (same key) | 1.0 | 8A → 8A |
| ±1 hour, same letter | 0.95 | 8A → 9A |
| Relative major/minor | 0.9 | 8A → 8B |
| ±1 hour, different letter | 0.8 | 8A → 9B |
| Energy Boost (+2) | 0.6 | 5A → 7A |
| Armin Variation (-5) | 0.5 | 12A → 7A |
| Mood shift (±3 hours) | 0.4 | 8A → 5A |
| Clash | 0.0 | 8A → 2B |
Quality scores are weighted in the objective function, so the solver prefers smoother transitions when possible.
See examples/ directory:
example_tracks.json- Sample input datasdk_usage.py- SDK usage demonstrationlogging_example.py- Logging configuration example
# Clone repository
git clone https://github.com/yourusername/djkr8
cd krate
# Install with dev dependencies
uv sync --dev
# Install pre-commit hooks
uv run pre-commit install
uv run pre-commit install --hook-type commit-msg
# Run tests
uv run pytest
# Lint and format
uv run ruff check # Check for issues
uv run ruff check --fix # Auto-fix issues
uv run ruff format # Format code
# Run pre-commit on all files
uv run pre-commit run --all-files
# Run example
uv run python examples/sdk_usage.pyThe optimizer uses Google OR-Tools CP-SAT solver with:
- Binary Variables:
included[i]= track i is in playlist - Edge Variables:
edge[i,j]= track j follows track i - Circuit Constraint:
AddCircuitensures valid track ordering - BPM Constraints: Only create edges between BPM-compatible tracks
- Harmonic Soft Constraints: Penalize non-harmonic transitions
- Objective: Maximize
sum(included)
MIT
Built with:
- Google OR-Tools - Constraint programming solver
- pyrekordbox - Rekordbox database access
- Camelot Wheel system by Mark Davis (Mixed In Key)