AI · SaaS · Video
Capnivo
Capnivo turns a raw clip into a captioned vertical video: transcribe it, pick a caption style, trim the silences, and export something ready to post.
- Role
- Sole author of this repository — web app, render worker, billing
- Year
- 2026
- Live
- capnivo.io
- Next.js 16
- React 19
- TypeScript
- Tailwind CSS
- Supabase
- OpenAI
- Google Gemini
- FFmpeg
- Stripe

What it does
Short-form video is watched without sound far more often than with it. Captions are not a finishing touch; they are the difference between a clip being read and a clip being scrolled past.
Doing them by hand is slow and repetitive — transcribe, split lines so they land on the beat, style them, re-export when a word is wrong. Capnivo takes that loop and does the repetitive parts, while leaving the choices that make a clip look like it belongs to its channel.
Built for creators and small video teams publishing short-form clips on a schedule.
What I did
I am the sole author of every commit in this repository — 99 of them, across the web application, the render worker and the billing integration.
The codebase covers transcription, caption chunking and sentence splitting, caption styling and animation, transcript editing, silence detection, auto-zoom, b-roll handling, clip selection using a vision model, caption burn-in through FFmpeg, export, Stripe billing and an admin dashboard.
Rendering is deliberately not in the request path: a separate worker process does the FFmpeg work, because video encoding does not belong inside a web request.
Parts were written with AI assistance. Capnivo is a product with a public brand, so treat this page as a description of the engineering in this repository rather than a claim over everything carrying the name.
- Commits in this repo, all mine
- 99
- API routes
- 48
- Unit test files
- 61
How it is put together
A Next.js application for everything a person touches, and a separate worker for everything that takes minutes and a lot of CPU.
Clip to captioned export
Upload
The clip goes to storage with its limits checked before anything is spent on it.
Transcribe
Speech to text, then split into caption-sized units on sentence boundaries rather than on a fixed word count.
Edit and style
The transcript is editable, and the caption style, animation and timing are chosen from presets.
Clean up the footage
Silence detection, auto-zoom and b-roll are handled as separate passes over the same project.
Render, out of band
A worker process burns the captions in with FFmpeg. A render hash keys the output to its inputs so identical work is not repeated.
What is in it
Captions split where sentences break
Lines that break mid-phrase are harder to read than lines that break on a clause. Chunking works from sentence boundaries, not from a word count.
The transcript stays editable
Speech to text gets names and product words wrong. Fixing one word should not mean running the whole clip again.
Silence, zoom and b-roll as separate passes
Each is its own operation over the same project, so a creator can take the silence trimming without the zoom, and the render only applies what was asked for.
Render work keyed by its inputs
A hash over the render's inputs identifies the output. Nothing re-encodes because a page was reloaded.
Rotating a provider key is a verified operation
There is a script whose only job is to prove a new OpenAI key reached every surface, the worker included. A key rotation that silently misses one process is a failure that shows up later and somewhere else.
npm run verify:openai

What was hard
Video work does not belong in a web request
FFmpeg encoding takes minutes and saturates a core. Moving it into a worker process kept the application responsive and made a failed render something that can be retried on its own.
Repeating expensive work is the default failure
Without an identity for a render, every reload, retry or duplicate submission is another encode. Hashing the inputs gives the output a name, so the same work is recognised rather than redone.
A secret has to reach every process, not just the obvious one
The application and the worker are separate processes with separate configuration. Verifying a rotation with a billable call against each surface was more reliable than assuming the deployment carried it everywhere.
Where it stands
- The product is live at capnivo.io.
- 48 API routes and 61 unit test files in this repository, with rendering handled by a separate worker process.
Next case study
PushUp Rivals
An iOS game that counts your push-ups from the camera, with nothing leaving the phone.