I Asked AI to Port MilkDrop to the Web. It Didn’t.

 ·  4 min read

Churn rendering the Hurricane Nightmare preset by Rovastar and Geiss
Churn running Rovastar + Geiss - Hurricane Nightmare (Posterize Mix)

I thought I had used AI to port MilkDrop3 to the web.

I had not.

The realization came embarrassingly late. The app was already built and deployed, and I was thinking about writing a post called Working with AI to Port MilkDrop to the Web. Before I started, I asked one basic question: what part of Churn actually came from MilkDrop3?

The answer was essentially none of it.

What I thought Codex built

I gave the MilkDrop3 repository to Codex, running GPT-5.6 Sol, and asked whether it could make a web version, perhaps with Next.js, WebGL, or WebAssembly. A working visualizer appeared surprisingly quickly.

It reacted to music, rendered familiar presets, worked in a browser, and was eventually running at churn.leyanlo.com. I asked Codex to add microphone input, shared tab audio, local files, preset search, fullscreen controls, high-density display support, beat-synchronized preset changes, metadata, icons, and automatic deployment.

It looked like a successful port because I was judging the result by what it did, not by how it worked.

What Codex actually built

The rendering engine came from Butterchurn, an existing browser implementation of the classic MilkDrop visualizer. The presets came from Butterchurn Presets, which packages converted presets while retaining their artist names.

The real architecture was much simpler than the story in my head:

MilkDrop3 ── inspiration only

Churn interface and browser audio

      Butterchurn renderer

      Butterchurn Presets

None of MilkDrop3’s Windows, Direct3D, HLSL, WASAPI, .milk2, sprite, or desktop editor implementation was ported. It was not a runtime dependency at all.

Using Butterchurn was a sensible technical decision. Recreating a native Direct3D renderer in a browser would have been a much larger and riskier project. The problem was not the decision.

Codex had told me it would use Butterchurn instead of a direct WebAssembly port and exclude MilkDrop3-only features. I did not stop to ask what that meant, and Codex did not stop to say plainly: this is not a port. The architecture was disclosed, but Codex never checked whether I understood its consequence.

It should have said something like:

Butterchurn already provides a browser renderer for classic MilkDrop presets. I can build a new product around it, but that would not be a port of MilkDrop3. Do you want to proceed?

That conversation should have happened before implementation began.

Why I missed it

I did not know enough about either project to recognize the substitution. From my perspective, I had supplied a repository and received a browser version of what it did.

The polished result reinforced the misunderstanding. Every bug fix made the app feel more legitimate. Codex fixed high-density display rendering, checked that the visuals responded to audio, tested every packaged preset in Chromium, and checked for JavaScript, WebGL, shader compilation, and program-linking failures.

None of those checks asked whether I understood what Codex had built.

This is the part I find most interesting about working with AI. Broken output is often obvious. A working result based on a mistaken premise can be much harder to notice.

The work that was real

The project was not worthless. Its value was just different from what I thought.

Churn is an interface and integration layer around Butterchurn. It makes the renderer easier to use with several browser audio sources and keeps all audio analysis on the device.

Churn’s audio source chooser with demo, shared audio, microphone, and local file options

The audio inputs and controls are Churn; the rendering engine is Butterchurn.

It also provides search and navigation across 395 packaged presets. The individual artist names remain part of every preset title.

Churn’s searchable browser listing 395 visual presets and their artist names

The preset browser keeps the original names and artist credits visible.

That is legitimate product work. It simply is not a new renderer or a MilkDrop3 port.

Fixing the story

Once I understood the distinction, I considered abandoning the project. I still liked the app, though, and deleting it would not make the misunderstanding more useful.

Instead, I renamed it Churn, and Codex rewrote its public description. The site and README now say plainly that Butterchurn supplies the renderer, Butterchurn Presets supplies the converted preset collection, and Churn contributes the interface, audio integrations, validation, and deployment.

The app survived. The original story did not.

What I would ask next time

Before asking AI to build from an unfamiliar repository, I would ask four questions first:

  1. What existing libraries or projects already solve part of this?
  2. Which parts of the result will be newly implemented?
  3. Which parts will come from other people’s work?
  4. Does the proposed architecture still match what I mean by “port”?

AI is very good at connecting disparate technologies. That is also why it can obscure where one project ends and another begins.

The dangerous result is not necessarily broken software. Sometimes it is working software that you fundamentally misunderstand.