Framegrab
A local-first video frame extractor: native-resolution frames pulled entirely in the browser, nothing uploaded.
- Kind
- Tool
- Role
- Solo: design and engineering
- Period
- 2026
- Status
- Live
- Stack
- Vite, React 18, TypeScript, Zustand, Web Workers
How do you pull clean, native-resolution frames out of a video without sending the file anywhere?

No backend, by design
The video is read with File + ObjectURL and never leaves the device: no API, no database, no analytics. That makes privacy an architectural property rather than a promise: there is no server that could receive the file.
Frames are exported individually or as a ZIP, encoded in the browser.
Everything on-device
Decoding, frame analysis and encoding all happen in the browser; the only server involved is the one serving the static build.
Client CPU and memory carry the load, but there is no infrastructure to run, and no upload to leak.
Native resolution, flat memory
Frames are drawn to a canvas sized to the video’s real dimensions, not the CSS player size, so every export is native resolution. Batch extraction seeks sequentially, holds one full-resolution blob at a time, commits results to the store in chunks, yields to the event loop, and can be cancelled mid-run.
Analysis runs off the main thread in a worker that only receives ImageBitmaps; the video file itself is never copied into worker memory, which keeps long and 4K videos from crashing the tab.
Sequential seeks, chunked commits
Extraction does one frame at a time and commits state in chunks, so memory stays flat and the interface keeps responding instead of freezing on the first big video.
Deliberately slower than maximally parallel extraction: for a local tool the worst failure is a crashed tab, not patience.
Find Best Frames
A two-phase finder surfaces the shots worth keeping without allocating a single full-resolution image. The scan probes up to 150 evenly spaced timestamps at thumbnail size and scores them in the worker on sharpness by Laplacian variance, exposure quality, and a 64-bit perceptual hash.
Selection normalises sharpness against the scan’s own maximum, blends in exposure, drops blurry frames using an adaptive floor from the scan median, removes near-duplicates by hash distance, and enforces a minimum time gap between picks. Only the winners are extracted, at native resolution, when they are saved.
Scoring is a plug-in, not a pipeline
Analysis sits behind a FrameAnalyzer interface, so new signals (faces, composition, duplicate detection) can be added without touching the extraction pipeline.
A little indirection up front, in exchange for isolating the part most likely to change.
Want something like this built?
Email me