How I shipped FRAMEnest in 8 weeks
A retrospective on building a desktop AI app for film studios — the macOS code-signing pain, GPU memory tradeoffs, and why I'd reach for Tauri next time.
FRAMEnest is a desktop app I built for Lengi Productions, a festival film studio sitting on 1.4TB of footage from one shoot. Their editors were spending the first three days of every project just finding the clips they needed — scrolling through proxy files, opening shot logs, asking the DP what was on which card.
I shipped a working v1 in 8 weeks, solo. Here's what actually happened — including the parts that almost broke me.
What it does
FRAMEnest sits on a director's local drive and quietly builds an index of every clip. It pulls a frame every N seconds, runs each frame through a vision model, and tags clips with shot type, lighting, mood, and content. Editors search "wide shot, golden hour, two people, laughing" and get hits across 5TB in under a second.
It runs entirely local. No upload. That was non-negotiable for the client — festivals send unreleased footage and a leak ends careers.
Stack I landed on
- Electron for the shell. I know — Electron is heavy. But the alternatives all had a gotcha. More on that below.
- React + Tailwind for the UI, since the client wanted it to feel like a Mac-native app and I had a design system ready.
- Local SQLite via
better-sqlite3. Postgres would've been overkill and the client didn't want a server running on their workstations. - FFmpeg as a subprocess for frame extraction. Bundled in the app via
ffmpeg-static. - A multimodal vision model for tagging. Started with the OpenAI API for v1, then moved to a local model running on the user's GPU for v2 to keep footage off any external server.
Week 1-2: getting frames out of weird codecs
The client shoots on RED. RED uses .R3D files, which is a proprietary RAW codec. FFmpeg won't read them out of the box — you need RED's SDK, which they license per-app, with a 2-6 week procurement timeline.
I didn't know this when I quoted the project. I found out on day 3.
The fix: while I waited on the SDK, I built the whole pipeline against proxies (the client always generates 1080p ProRes proxies for editing anyway). The full-res R3D index became a v2 feature once the SDK arrived. The lesson: always ask about codecs in the discovery call. Proprietary RAW formats are everywhere in pro film and they all have licensing requirements.
Week 3-4: the AI tagging pipeline
The naive thing is to send every frame to a vision API and ask "describe this." It works. It also costs $400 per TB at OpenAI prices and takes overnight.
What I actually did:
- Extract one frame per second using FFmpeg.
- Run a tiny CLIP-style embedding model locally to get a 512-dim vector per frame.
- Cluster adjacent frames by cosine similarity — if two seconds of frames are nearly identical, only tag one of them.
- Send only the novel frames to the vision model. For a typical 90-min wedding shoot, this cut tagging from 5,400 frames to about 600.
- Cache aggressively. Re-running a folder is essentially free.
This took 1.4TB from "$200, overnight" to "$8, an hour."
Week 5-6: the macOS code-signing hellride
Electron apps on macOS need to be:
- Code-signed with an Apple Developer ID
- Notarized by Apple's servers (uploaded, scanned, stapled)
- Hardened-runtime enabled with the right entitlements
If any of those is missing, macOS shows the user a "this app cannot be opened because the developer cannot be verified" wall. End of trial.
The client did not have an Apple Developer account. Getting one takes 2-7 days. Once you have one, the certificates take another day. Then notarization fails the first 4 times because of bundled binaries (FFmpeg, the model files) that need their own entitlements and signatures.
I built the whole CI/CD on GitHub Actions: macOS runners do the macOS build + sign + notarize, Windows runners do the .exe + Authenticode signing in parallel. A git tag v0.4.2 produces signed installers for both platforms in about 18 minutes.
If you take one thing from this post: start the Apple Developer signup on day 1 of the project. Not week 5. Day 1.
Week 7: GPU memory and OOMs
Running a vision model on the user's local GPU sounds clean until you discover what happens when their GPU only has 6GB of VRAM and someone else is also using it (Premiere, DaVinci, Chrome with 50 tabs).
Three fixes:
- Stream frames through the model instead of batching. Slower throughput, but predictable memory.
- Detect available VRAM at startup and downgrade to a smaller model variant if needed.
- CPU fallback path. Slower again, but it doesn't die. I'd rather have a tagging job that takes 4 hours than one that crashes after 90 minutes and loses progress.
What I'd do differently
Tauri instead of Electron. Electron is fine. It's familiar. Every dev I'd hand this off to could read the code. But the bundle was 240MB and the cold-start was 1.8 seconds. Tauri would've been ~12MB and instant. The cost is a smaller ecosystem and learning Rust for the parts that need native APIs.
Vector DB earlier. I built search against the SQLite full-text index, then bolted on similarity search later. Should've started with lancedb or qdrant-rs (both embeddable, no separate server) and treated everything as a vector lookup from day one.
More E2E tests. I wrote unit tests for the indexing pipeline and integration tests for the API. I had zero E2E tests for the actual desktop app. The first time someone clicked "Cancel" mid-tagging, the app crashed. The second time, the cache corrupted. Both shipped to the client. I caught them, but Playwright + Spectron from week one would've caught them earlier.
What worked
- Daily Loom updates to the client. They never wondered what I was doing. Three of them asked for new features mid-build that I could absorb because we were talking constantly.
- Live-preview from week 1. Even when it was a button that did nothing, the client could click it. That made the whole project feel real to them weeks before it was actually real.
- Saying no to scope creep. They asked for a web companion. They asked for a collaboration mode. They asked for AI-generated rough cuts. All good ideas. All v2. I shipped v1.
The app is live, the editors stopped sending angry Slack messages about lost footage, and the client paid the final invoice in 4 minutes when I delivered. That's the part of the job that doesn't fit in a build log but is the whole point.
✦ Keep reading
More in this category →Got an idea you want to build?
Hire me →