Prologue — before the loops begin
This devlog is a preface, written before the real looping starts. I wanted to lay down how I got here and how I'm looking at this project.
How we got here
The repo was empty. The user's first line was "do you know the oiiai cat?" I said yes; the next line was "go find me the mp3." Grabbing the 90KB original from Myinstants is where this actually began. From there, the project ballooned one sentence at a time, following the user's voice.
Roughly in chronological order:
- Downloaded the original → a single HTML with Web Audio API, waveform, and segment sliders.
- Promoted to a Vite project + Hangul jamo key mapping (swapped
ㅗ→ㅜ). - UX for picking segments by dragging on the waveform. The first version was too hard to grab, so I redesigned it as three interactions: "click = select / body drag = move / edge drag = resize."
- Simplified
ㅣto a single key, pinned the user-tuned timestamps as defaults, added A/B bonus slots. - In response to "make it flashy as hell," a full-screen FX canvas — particles, rings, text, flashes, beams.
- Hit lag from rapid presses, so I did an optimization pass — removing
shadowBlurwas the decisive one. DPR capped at 1.5, per-color batching, MAX_ITEMS at 900. - Press-and-hold → exponential acceleration + EDM drop (with screen shake) after more than one second.
- Enter → random DJ effect, which then expanded into a slot system mapped to keys 1–9 with selects. Effects grew from 11 → 24.
- Pressing the same DJ key twice overlapped the sound — fixed by putting every
BufferSource/Oscillatorinto anactiveDjNodesSet and stopping them on the next trigger. - Rerouted DJ effects to use the A-segment sub-buffer — tune A and the DJ tone moves with it.
- The A key → random cat GIF pop (3 animations).
specs/LOOP.mdas the autonomous loop protocol,tests/as the Playwright pipeline (desktop + mobile), and this devlog practice too.
Looking at the code, src/main.js by itself has gotten too heavy; effects.js at least has clear boundaries. All tuning data is in localStorage and is already at v6 — the "bump version and drop it" pattern has worked up to this point, without migrations.
My take
This project started as a "cat-meme toy," but the user kept raising the bar, and now it's sitting right at the edge of "musical toy" territory. It's not intentional architecture — it's accumulated. So pinning down the three axes ("viral + DJ + mobile") in LOOP.md feels late but correct. Without that direction, I'd probably have just kept adding more dazzling FX until I stopped.
Writing the spec, the most surprising thing was realizing mobile is essentially empty. Right now this is a keyboard-only app. There's card-click as a touch fallback, but the UI itself assumes desktop, and — more to the point — I've never verified on a real device how Web Audio wakes up on iOS Safari's first touch.
The DJ axis looks showy with 24 effects, but there are no controls that an actual DJ would want. No BPM, no loop layers, no wet/dry. It's "press a button, a five-second sample fires." That's a toy, not a tool.
The viral axis is the lightest. There's nothing to share. No recording, no link sharing, nothing to point at besides a screenshot.
Feel / self-evaluation
The visual and audio punch is already strong. The combo of cat + particles + drop when you hit A lands a laugh on the first try. Making that the default is an asset. But an asset is only the front door, not a reason to stay.
- Viral: 10% — pretty to look at, no exit.
- DJ: 30% — the sound tools are there; there's nothing a pro would twist.
- Mobile: 5% — effectively uncharted.
What I want to do next
Once the loops start, I want to crack mobile first. Open the app on mobile right now and it's likely that nothing works. A touch pad layout + iOS audio unlock alone will change the feel entirely. Viral and DJ both compound on mobile, so mobile has to lead.
After that, session recording. The moment people can record and share, the viral axis goes from 0 to 1. For the DJ axis, BPM lock — snapping STUTTER, GATE, and ECHO to BPM alone should strip the amateur feel.
Above all, I want to hold the discipline of visible change every loop. A pure-refactor loop is a failed loop. Every loop should leave a "before/after" Playwright screenshot.
Mindset
The loops run alone. The user won't steer each iteration. So there are two things I want to be especially careful about.
First, don't defer judgment. Twenty-four items in the backlog doesn't automatically mean twenty-four meaningful loops. Each time, ask "does this move the product forward?" If the answer is fuzzy, pick something else. Don't grab something just because it's in the spec.
Second, obsess over completion. I'd rather have something small and finished than something half-built. A finished tap-tempo beats a half-done BPM system. The user looks at what's there when they open it again. "Almost-worked" has no value.
Above all, this is a toy too. Don't lose the fun. A cat GIF popping out is this project's identity.
Notes
- The user prefers Korean conversation and short terminal output. Devlogs and LOOP.md are Korean-primary.
- localStorage is at v6. The "bump and drop" pattern worked. The moment URL sharing lands, that schema becomes an external contract and migrations become a real concern.
- Some of the 24 DJ effects (OVERDRIVE↔DISTORT, ECHO↔REV-ECHO) look redundant. Consolidation or differentiation is cleanup for later.
- FX canvas uses
mix-blend-mode: screen— it looks great on desktop, but on mobile Safari it's unknown. Verify when doing mobile work. playwright.config.jshasreuseExistingServer: true, so tests are much faster with the dev server up. During loops, keep 5174 alive.- Before entering a loop, start with
npm run test:desktopto confirm a green baseline. If something's broken, fix that first before starting new work.
From the next loop onward, this actually begins. Small, finished changes, a screenshot every time, starting from whichever of the three axes is the weakest. Something the user can go "oh" at when they open it again.