← Writing

My product has no front door

September 7, 2026

Wave AI Clipper takes a long recording — a podcast, a YouTube episode — transcribes it, finds the moments worth cutting, and renders vertical clips with the speaker’s face followed and captions burned in. Indonesian audio, because that’s the part the existing tools handle worst.

I’m stuck on it, and I’d been telling myself I was stuck on whether the idea can work. Writing it out is how I found that the block is somewhere else entirely.

What actually got built

The pipeline runs end to end. Uploads go browser-to-storage directly, so a 2 GB file never passes through Next.js. Deepgram transcribes with speaker diarization. DeepSeek reads the transcript and returns the opening and closing sentence of each interesting moment instead of timestamps — models are bad at time arithmetic and good at quoting, and matching those quotes back against the transcript removes the entire class of clips that begin mid-word.

The parts I’m actually proud of are smaller than that.

Crosstalk. Two people talking over each other produce diarization segments 0.4 seconds long, and a crop that follows them literally becomes a strobe light. So: segments under two seconds merge into their neighbor, three speaker changes inside five seconds stops trying to pick a winner and cuts to a group shot, and a new speaker has to hold for 300ms before the frame moves — which it does 200ms early, so the cut doesn’t feel late.

Keyframe lead-in. A stream copy can’t start mid-GOP, so every clip file begins earlier than its own start time — 0.6 seconds on a YouTube 720p source, four seconds on a sparsely-keyframed encode. The render starts its first segment at the real start and trims the audio by exactly the same amount, because audio never enters the filter graph. Miss that and every clip drifts out of sync by an amount that changes per source, which is a bug you’d chase for a week.

Ceph rejecting botocore’s streaming checksums, found by running the real pipeline rather than by reading documentation.

There’s an architecture document recording why each of these decisions is the way it is, written before the first line of code.

What didn’t get built

The application has no landing page. app/page.tsx is a redirect: session goes to the jobs list, no session goes to the sign-in form. A stranger who typed the domain would be asked to log into a product they have never heard of.

Browser uploads don’t work in production yet. The bucket’s CORS document is missing ExposeHeaders: ETag, and without each part’s ETag the browser can’t assemble the multipart completion call, so the upload dies at 100% with no useful message. It is one command away from fixed. I know what the command is. I wrote it into the architecture doc under remaining CORS work and never ran it.

Face tracking — the thing I spent the most design effort on, the thing that would make the output different from every other clipper — has never been tested against a real face. Not once. My own product document says it in plain language: no customers, no benchmarks, no real user footage.

The hardest engineering problems are solved. The cheapest and most important question is untouched.

Why I’m actually stuck

I built the part I’m good at until it ran out.

Every remaining task is one I’ve never done. Show it to a podcaster. Ask someone what they’d pay. Sit beside a person while they upload their own episode and watch where they get confused. Write the sentence explaining what this is to someone who’s never heard of it — the sentence that would go on the landing page that doesn’t exist.

None of those are hard the way crosstalk hysteresis is hard. They’re hard because they return an answer I don’t control. A render pipeline either produces a watchable clip or it doesn’t, and I can find that out alone at 2am with ffmpeg and a log file. Whether anyone wants this is a question only other people can answer, and I kept building instead of asking one of them.

So can it succeed?

I don’t know, and I can’t know from here, because every assumption underneath it is still untested:

  • That Indonesian creators are unhappy with how the existing tools handle Bahasa Indonesia. I believe this. I have verified it with zero people.
  • That Rp149.000 a month is a price a solo podcaster pays. Picked by feel, against no comparison and no conversation.
  • That manual bank transfer is an advantage, not a leak. I think it genuinely is one — plenty of Indonesian creators can’t easily pay a card in USD — but every payment needs a human to approve it, and that human is me, at whatever volume this reaches.
  • That the clips are good enough to post without opening another editor. That’s the entire product promise. It has never been true in front of a real user, because it has never been in front of one.

All four were answerable in a week of talking to people. None of them get more answerable by building.

The test

Small enough to be embarrassing:

  1. Run the CORS fix. One command.
  2. Upload one real episode. Mine, if it has to be.
  3. Render one clip and post it somewhere public.
  4. Show it to five Indonesian podcasters and ask what they’d pay.

If the clips are bad, that costs an afternoon instead of a rebuild I’d have designed around the wrong problem. If nobody wants it, I get to stop carrying it — and stopping is a real result. A project I decide to kill costs nothing. A project I hold open without deciding charges me a little every time I remember it exists.

The answer isn’t in the repository. It was never going to be, which is why I could keep working and never get closer to it.

Working on something similar? Get in touch.