Trying out Content Driven Development
Trying out Content Driven Development
I have been building Cratebase over the past few months — an operating system for music libraries. I'm building it to solve a personal problem - I have loads of audio files downloaded from Bandcamp, Soundcloud, and other places, and I needed a central place on my computer to manage and listen to all these files.
While I could use macOS's built-in Music app, I wanted to keep my streaming library separate from my downloaded tracks. The Music app also lacks some organizational features I need to manage my tracks efficiently.
I have been making good progress with the app but lost motivation for a bit. I came across this nice article — it talks about content-driven development as a way to sustain motivation while working on a side project, and I thought I'd try that.
This is the first entry in my dev log for Cratebase. Since it's the first one, I'll talk about the tech behind Cratebase and the features I've built already. Then I'll discuss the features I shipped last week and what I plan to work on this week.
The Tech Behind Cratebase
Cratebase is an Electron app with React on the renderer side [ie the frontend]. Currently, I'm only building macOS targets since I don't have Windows or Linux machines to test those builds—this may change in the future.
On the React side, I'm using the standard stuff: Tailwind for styling, React Router for routing, and Radix UI for the base unstyled UI components.
For state management, I chose MobX since the app has a lot of derived state, and MobX handles that flawlessly with computed values. I've been really enjoying working with MobX so far. I'll have a separate post about how I set up MobX and the cool utilities I'm using with my setup.
The main process (backend) runs on Node. For the database, I use SQLite along with Drizzle.
The Electron app is built with Vite through a package called electron-vite. For publishing and building macOS versions of the app, I use Electron Builder.
I have a separate Hono server with a PostgreSQL database that handles software updates, license management, and additional features that can't be handled locally—like track metadata searching. Both the server and database are currently deployed on Render.
All the apps and packages live in a monorepo powered by Turborepo.
Current Features
Tracklist
Currently, I have a working tracklist:
The tracklist is a table powered by TanStack Table, so I have features like column visibility, column reordering, sorting, and filtering built in. To handle large amounts of data, the table's rows are virtualized using TanStack Virtual.
At the top of the tracklist, there are quick-access links for recently added, favorite, and archived tracks:
The most challenging aspect of the tracklist was adding keyboard controls—since this isn't natively supported by TanStack. The main difficulty stems from the virtualized rows—simply attaching a keydown listener won't work.
Track Metadata
I have a side panel that users can use to update the metadata of a track — this currently only saves changes to the SQLite database and does not write the changes to the audio file itself:
Track Archive
I implemented an archive to store tracks that you don't want to see in your main tracklist:
There is a cleanup option in the context menu that allows users to pick how they want the archived tracks to be handled - I'm wondering if I should add an automatic setting that cleans up archived tracks automatically after a set amount of time.
Quick Filters
I have a filter button on top of the tracklist — this allows users to add quick filters to the tracklist bases on track fields like duration, genre & artist.
You can also save your filters as presets and apply them to a tracklist at any time:
Audio Player
I have also built the audio player using the HTML audio element with all necessary controls, and it works quite well:
The only downside is that it doesn't support .AIFF audio files. My current workaround is to convert these files to MP3 and send that as a blob for the element to play. This causes a significant amount of lag when playing a AIFF tracks — I'm looking for a way to solve this.
While building the audio player, I learned about the Media Session API - a really cool API that lets you customize media playback and send track metadata to the host OS. With this API, we can display the currently playing track in the macOS title bar:

What I worked on last week
Last week, I mainly worked on the tracklist queue. When you play a track, there is now a little button by the title that opens a queue of tracks that will play next:
I considered whether making this a popover was the best choice. I wanted to implement it similarly to how the Music app on macOS works, where the queue opens as panel on the right. Let me know what you think.
The most challenging aspect of implementing the queue was ensuring it worked properly with repeat and shuffle features. I also had to carefully consider how the "Play Next" right-click option would interact with these queue behaviors.
In the end, it came out really nice - I used MobX's reaction utility a lot to ensure my state was synced correctly.
What I'm working on this week
This week I plan to work on the metadata features. I have three metadata features in mind:
-
Find metadata - This service will let users search for track metadata using title and artist information. I'll likely integrate with an external service like Discogs or MusicBrainz for the searches. I'm still brainstorming how the UI would look, especially when there are multiple search matches. For inspiration, I'm looking at how other apps like OneTagger handle this.
-
Reload Metadata - This feature will overwrite the metadata stored in the SQLite database with metadata sourced from the audio files' ID3 tags
-
Write Metadata to File - This feature allows users to write the current metadata stored in the SQLite database to the audio files' ID3 tags.
I think the find metadata feature will be the most complicated to implement—but let's see how it goes.
I currently have a soft deadline to ship an MVP for this app by summer—hopefully writing about my progress will help me hit the deadline.
Thank you for reading.