Home /

The DJ tools I kept trying to build

How four years of DJ-library friction led from Python scripts to a Go desktop app and the AI-assisted Crate Lynx.

About four years ago, I got my DJ equipment back out after putting the thing in a cupboard for five years. I had been working professionally as a software engineer for roughly a year. DJing was new to me, and in a lot of ways programming still was too.

I very quickly became discontent with the work involved in managing music for it.

I would discover tracks on a streaming platform and add them to a playlist when I had the energy. Later, I had to find the local files, download them one by one, convert incompatible formats, check the quality, fix the metadata, and put them into a crate. I had used both MediaHuman's YouTube to MP3 Converter and Audio Converter at different points, although I generally avoided building my library from YouTube rips because I did not trust the quality.

Then there were cue points, tags, stems, and the question of what to do when a track fitted two parts of a tagging system that had seemed perfectly sensible until that track arrived. It was all very manual, it was all very painful, and it was all just annoying with so many annoying steps.

The especially annoying bit was keeping track of state. That playlist became a crate, but the streaming playlist kept changing. Which new tracks did I need to find? Which ones had I already downloaded? Which ones had failed the first time? Did I get it? Did I not?

None of these jobs was enormous on its own. Together they created a manual process with enough small decisions and missing context that I often only did it when I felt I had the energy. It also gave me a sequence of real software problems to work on.

The scripts probably got the most use

My first attempt was a small collection of Python scripts. They predated the agent-assisted workflow I used for the later projects, and they answered one job at a time rather than forming a product.

One walked through my library and converted FLAC, AIFF, OGG, M4A, and WAV files to 320kbps MP3 with FFmpeg, while moving the originals elsewhere. Another ran Demucs over folders of tracks, then used FFmpeg and MP4Box to package the results as Traktor .stem.m4a files.

The quality checker used a Python command-line package called Spectro. It ran spectro check over each MP3 and sorted the output into CSV files depending on whether the file seemed fine, had a suspicious maximum frequency, produced some other result, or failed. I had half remembered this as some application called Speccy. The old source code was more reliable than my memory.

Ironically, these basic scripts probably received the most real DJ use. I was DJing most actively at the time, so rough tools that solved one job were still useful tools. They did not need a coherent interface or a grand plan. I ran them because I needed the output.

They also show how much smaller my programming ambitions were. I could see plenty of friction beyond conversion and stems, but I did not yet have the knowledge, energy, or motivation to turn all of it into one maintained system.

DJ Management Utils taught me Go

At the end of 2023, those scripts started turning into DJ Management Utils, a desktop application written in Go with Fyne.

I was probably using GitHub Copilot by then, although my memory of the exact setup is fuzzy. Copilot was rudimentary compared with the coding agents I use now. The process was still overwhelmingly manual: I was writing the application, working through the architecture, and learning how to coordinate expensive audio jobs with Go concurrency.

The application pulled together MP3 conversion, Demucs stem generation, SoundCloud playlist handling, and Traktor library data. The part I remember most fondly was not merely converting the audio. I reverse-engineered enough of Traktor's collection format to write the stem versions back into the library automatically.

I converted my whole library to stems. I did not then play with those stems as much as the scale of that operation might suggest, but I used the resulting library for a couple of sets. I was doing less DJing by that point, so the more ambitious application got less practical use than the scrappy scripts.

As a programming project, though, it was much more important. It taught me an awful lot about Go, particularly concurrency and how to structure a real application rather than a one-off script. I think it contributed greatly to getting my first Go job.

The project that helped my programming career was not the project I used most while DJing. Its value came partly from solving the problem and partly from giving me a problem difficult enough to learn through.

The first Crate Lynx became unworkable

By the middle of 2025, coding agents had made a much more ambitious version feel possible. My private GitHub history contains three Crate Lynx experiments created between 28 June and 10 July: a playlist prototype, a clustering experiment, and a larger rewrite with 188 commits.

The focus then was more heavily on autonomous playlist generation. I was extracting audio features with Librosa, building hierarchical clusters from rhythm, tonal, and timbral properties, naming those clusters, and trying to turn them into playlist trees that DJ software could use.

That work was entirely AI-assisted. I think I was mostly using Claude Sonnet 3.5 through Claude Code, although the repositories establish the use of Claude Code rather than the exact model.

It was entirely vibe-coded, but I say vibe-coded, like vibe-engineered. The agent wrote the code, but I was granular about the features and did a lot of the thinking about what the system should become. It was not a prompt for “make me a DJ app” followed by a finished application.

It still reached a point where it was unworkable. My judgement was that Sonnet 3.5 was not strong enough for that project. My workflow was also much more manual then, but that changed separately over the projects that followed. One of the experiments could build nested M3U playlists from audio clusters, while the larger rewrite went deeper into analysis but still listed playlist export as future work. I had pieces of the idea, but not a system I wanted to keep extending.

AI made a larger project possible to attempt. It did not yet make it possible for me to finish that project well.

The version I took to a party

The current Crate Lynx began in April 2026 and has publicly available source code. It is a local, self-hosted application that connects streaming playlists to the high-quality files in my own library.

It can sync YouTube Music playlist metadata, ingest and fingerprint local audio, suggest links between streaming tracks and local files, and keep the final approval manual. From those links it can export M3U playlists and Rekordbox XML. The current application also returns to the sonic playlist-tree idea from the 2025 experiments.

The feature list is not the main reason this version feels different. Both the models and the way I work with them changed while I was building it.

At first I was still asking the agent to write a collection of Markdown task files. I would finish one session, open another, and say, “Okay, pick up the next task.” I even had a skill for doing that handoff. It was more capable than Copilot, but the human workflow still involved manually moving the project forward one bounded session at a time.

Over the course of the project I became more comfortable giving agents larger pieces of work and letting them run for longer. Stronger models made that easier, but this is not a clean model benchmark. I got better at specifying and supervising the work at the same time.

Crate Lynx is now mostly done in a way the 2025 version never was. More importantly, I have used it for the thing it exists to do. I generated an export, put it on a USB drive, took it to a friend's party, and DJed from it. It was really useful. The software made it out of the repository and into a set.

Now I have AI and I can finally consolidate these ideas into something solid and cool.

What I still want it to do

Programming has been a huge asset to my DJing and I suppose DJing has been a huge asset to my programming. It gave me problems I actually cared about across scripts, desktop GUI development, audio processing, concurrency, data modelling, matching systems, web applications, and now coding-agent workflows.

The automation is not there to replace taste. Choosing music, deciding what fits together, and reacting to a room are still the interesting parts. What I want to remove is the cognitive load of the whole manual process, which I have never found particularly enjoyable.

There is still plenty I want to try. I might use local models to generate playlists, or build a system that suggests the next track from the current track and the earlier tracks in that session. I might have a go at an autonomous mixing system. I also want tighter integration so that exporting becomes unnecessary.

The version in my head is this: the library is in one place, I open the DJ software, and it is already there and ready.