AudiOasis: The Product Layer Behind a Music Intelligence System

A case study in product design, design systems, and AI-assisted development.

My role

Solo project. I owned the concept and product decisions, the interface and design system, and the engineering. Built with Python (Flask + htmx + Alpine.js), a design system I authored in Figma via the Figma MCP, and Claude Code as an implementation partner.

The underlying audio-analysis engine is my own, evolved over 2+ years. This case study focuses on the product I built on top of it.

Music Match

What it is

Recommendation engines are good at finding things that look or sound similar. They are much worse at explaining why two songs belong together.

I wanted something different: a way to curate my own library based on how music actually sounds and how it flows, track to track.

AudiOasis is the result: a local music-intelligence suite built on top of an audio-analysis engine I’ve evolved for more than two years. It turns that analysis into two tools:

The suite runs over my personal Roon library of over 32,000 tracks. It replaces a collection of Streamlit prototypes with a custom product experience built on a design system I created specifically for it.

A note on the name: AudiOasis is a tip of the hat to KEXP’s long-running Audioasis, a program showcasing Pacific Northwest music. I’m a KEXP member, and the name is intended as homage.

One constraint shaped everything that followed:

Keep 100% of the logic in Python — but own every pixel.

At a glance

From model to product

AudiOasis is the latest iteration of a question I started exploring years ago:

Can I describe what music sounds like well enough to sequence it automatically?

The answer evolved through three eras of tooling: early ChatGPT exploration (2024), two Streamlit front-ends (2025) — TrackMatch and the Micro-Set Playlist apps, and finally AudiOasis (2026).

The important throughline: the interface changed repeatedly, but the underlying model remained the source of truth.

Each version was a better way to reach the same Python core. AudiOasis is the first one that feels less like a prototype and more like a product I could hand to someone else.

Timeline

Why leave Streamlit?

Streamlit was the right tool for proving the engine. It was the wrong tool for turning it into a product.

The reason became a design principle:

I wanted a real, branded interface — but I refused to move the matching logic into the front end to get one.

Streamlit intentionally blurs the boundary between interface and logic. That makes it excellent for exploration, but limiting for a crafted product: you inherit its layout, components, and interaction model.

I wanted typography I chose, components I designed, and interactions I controlled, without rewriting a proven Python engine in JavaScript.

That constraint — keeping the logic in Python while owning the experience — became the foundation for the architecture that followed.

Building the design system first

AudiOasis looks like a product because, before I wrote the app, I built the system it would be built on.

I created a separate, code-first design system using Claude Code and the Figma MCP server as a bridge between design and implementation. The goal was not to create a library of screens; it was to create a shared language of decisions that could be reused, tested, and extended.

The conventional pipeline is design → spec → handoff → build. The problem is the translation layer between design intent and working software. I wanted to remove that gap: the design decisions themselves would become the thing the product was built from.

The premise:

The design artifact is the production artifact.

Tokens are the documentation. Components are the specification. The browser is the design canvas.

This also defined how I use AI. Claude is an implementation partner, not a design decision-maker. It works from explicit conventions, a CLAUDE.md rules file, and documented decisions; it can extend the system, but the taste calls — what the system should be — remain mine.

The foundational investment was not a collection of screens. It was a reusable system of decisions that could be applied consistently, whether by me or by an AI-assisted workflow.

The design system as a production system

The system is deliberately opinionated: everything is a token, never a one-off value.

A few examples:

The most important part was creating a reliable loop between design and implementation:

Code is the source of truth; Figma mirrors it.

I author tokens in code, then reflect them into Figma Variables and Styles so both sides use the same system. A mapping layer keeps Figma naming and CSS naming aligned without forcing either side to compromise.

Claude works inside those boundaries:

The workflow became:

  1. Read the design context and variables.
  2. Implement against the existing token system.
  3. Compare the result visually.
  4. Refine the system when a new pattern emerges.

AI accelerates repetition, but it does not replace judgment. Roughly 15% of any generation still requires manual work: interaction states, responsive behavior, animation, and the decisions that only become obvious once something is on screen.

That boundary is part of the method.

Figma token sheet
Filters

How it feeds AudiOasis

The reason this architecture matters is that the design system was built to be consumed, not admired.

The durable asset is the token layer: framework-neutral CSS custom properties that AudiOasis can use directly without a build step. The app vendors in a snapshot of the design system, pinned to a specific commit, so it stays self-contained while every color, spacing decision, type scale, and interaction state still traces back to one governed source.

The design system’s own component experiments were built in Vue; AudiOasis itself runs on Flask, htmx, and Alpine.js. That separation was intentional.

The system was never tied to a framework. It was a visual and behavioral foundation that could wrap a product built with different technology because the decisions lived at the right layer.

That is what allowed a custom Python application to feel like a coherent product rather than a prototype.

The AudiOasis architecture: own the pixels, keep the logic in Python

The entire suite is built around one rule:

Keep the intelligence in Python. Own the experience in the browser.

Every meaningful interaction is an HTTP request that Flask answers with a rendered HTML fragment:

Route → Python core → Jinja partial → updated interface

htmx turns ordinary HTML attributes into those interactions — searching, filtering, regenerating playlist slots, importing data — while keeping the matching and sequencing logic in Python by construction.

Alpine.js handles only presentational state: the app toggle, expanding panels, and lightweight interactions.

The result is a deliberately simple stack:

That simplicity matters because AudiOasis lives locally alongside my Roon server. It launches with flask run and stays easy to maintain.

I considered React and Vue, but both would have pushed more state and logic into the client. That would have created a second implementation of the part that mattered most: a Python engine I had already spent years evolving.

Architecture

Music Match: refining a proven engine

Music Match was where the new product layer met an existing capability.

Its predecessor was TrackMatch, and the underlying matching engine was already proven. The goal was not new science; it was turning a useful prototype into a focused product experience.

What changed was the interaction model:

Mix Maker: designing for flow, not just similarity

If Music Match was a refinement, Mix Maker was the real rebuild.

The original Micro-Set Playlist App proved the idea: small groups of songs could become the building blocks of a larger playlist. But its sequencing model was based on emotion — choosing what mood should come next.

Mix Maker replaces that with a structural model: choosing what sound should flow next.

Micro-Set Playlist App Mix Maker
Grouping Mood and emotion themes Five-axis bipolar sonic model + tempo tags
Sequencing Emotional paths and weights Connector-based edge matching
Editing Generate a path Locks, replacements, and live regeneration
Integrity Avoid repeated artists???? Song deduplication + artist spacing + visible gaps
Output Cards / Spotify links???? Roon-ready .m3u export

The key shift:

The old system asked:

What feeling should come next?

Mix Maker asks:

What transition will sound right next?

Each micro-set has an entry and exit character. The sequencer chains those edges together, like musical dominoes, using a strictness cascade that relaxes when a perfect transition is unavailable.

The result is a playlist generator that is intentionally collaborative:

Per-slot locks let you preserve a discovery while the engine rebuilds around it. Replacement options come from valid candidates, not arbitrary search results. And when the rules cannot be satisfied, Mix Maker leaves a visible gap instead of quietly breaking them.

A playlist that admits “I need your input” is more useful than one that silently cheats.

Mix Maker

Closing the loop: the whole pipeline in the app

The final step was turning AudiOasis from two tools into something I could actually maintain.

Previously, updating the system meant a collection of manual steps: export data, run scripts, import files, restart tools, and remember the sequence.

I moved that workflow into the product itself.

No terminal. No separate maintenance scripts. No restart.

This is the difference between a clever engine and a product I actually use.

When I add music to my Roon library, keeping AudiOasis current is now part of the same listening workflow it was built to support.

File uploads

The product decisions behind the system

The engineering exists to support a handful of product decisions — the kind users rarely notice directly, but feel in the experience:

The through-line is deciding what the machine should do, what the person should control, and where those two should meet.

AudiOasis started as an experiment in whether music could be modeled by how it sounds. It became a product by focusing less on what the system could automate and more on how a person should interact with it.