Live: https://encoding-helper.brain-bbqs.org
A didactic, in-browser video encoding lab. Load an MP4 and it inspects the container, teaches you how MP4 storage and H.264 encoding actually work (tied to the numbers in your file), runs empirical seeking tests, and reencodes video directly in the browser, while always producing a copy-paste ffmpeg command for local/headless/batch use.
Companion to Video Info Tool and Frame-Accurate Video Player.
- Inspect - rich metadata plus a visual MP4 atom map (
ftyp/moov/mdat/moof, byte offsets & sizes, moov-before-mdat "faststart" detection) and per-frame GOP/I-frame/B-frame structure - Atom Map - the box tree on its side: left to right across the file, one lane per nesting level, so it stays the same height whether the video runs ten seconds or ten hours. Siblings split their parent's width by how many boxes each subtree holds, so every box is drawn and labelled at once; click any box to zoom into it, and on a file with more boxes than fit, neighbours merge into counted blocks rather than being dropped. Hover or tab to a box for its offset and byte count
- Bitrate over time - the video track's rate plotted one window of playback at a time, straight from the sample table (no decoding), against the track average, so you can see where the bits actually went instead of only what they averaged to. Skipped for a file the container declares constant-bitrate and whose frame sizes bear that out, since the plot would be a flat line
- Identify the codec - infers the codec family from the container (H.264/AVC, H.265/HEVC, VP8/VP9, AV1, AAC, Opus, FLAC, MP3, AC-3/E-AC-3, PCM) and decodes its embedded profile/level/tier straight out of the RFC 6381 codec string, with a short explainer on what that codec actually is and why you'd (not) choose it
- Teach - interactive explanations tied to the loaded file: CRF vs. bitrate, x264 presets, GOP/keyframe interval, I/P/B frames,
yuv420pchroma subsampling, even-dimension requirements, and the moov-atom/faststart tradeoff - Measure - empirical seeking tests (nearest-keyframe distance per timestamp, decode wall-clock, keyframe-interval histogram) with a scatter plot of distance vs. decode time, plus before/after compression stats
- Reencode In-Browser - encodes the whole video to H.264/MP4 in its own tab, saved back to disk via the File System Access API, with two engines:
- ffmpeg.wasm (exact) - runs the literal CRF/preset command, byte-for-byte equivalent to the CLI, lazy-loaded (~30 MB), GPL
- mediabunny / WebCodecs (fast) - hardware-accelerated, no CRF (bitrate/quality-preset only), surfaced honestly as an approximation
- Compare Quality (A/B) - encodes just a short window (1-10s) of the video at the chosen CRF/preset, then decodes the original and the result side-by-side with synchronized pixel-level zoom & pan, one-click Fit/Actual Size (100%) buttons, a pixel grid that appears once zoomed in far enough to make individual pixels visible, and a scrub slider, so you can judge a quality setting before committing to a full reencode. It also estimates the data savings: the encoded snippet against what the same seconds cost in the source, that ratio projected onto the whole file, with a range on it
- Always emits a CLI command - a live, editable
ffmpegcommand mirroring sleap-io'sreencode, for anyone who wants to run it locally, headless, or in batch - Full Analysis - one button beside the tabs bundles everything the tabs worked out into a single document: container and track metadata with the explainer that goes with each number, the bitrate plot, the atom map, GOP/keyframe structure with its histogram, the seeking test with its scatter plot, Compare Quality and in-browser reencode results (once run), and the CLI command. Read it in the page, save it as a self-contained
.htmlfile (no external assets, so it opens anywhere), print it to PDF, or copy it as Markdown - Shareable links - the active tab lives in the URL (
?tab=seek), and a video loaded from a remote URL is recorded alongside it (?src=…) and re-opened automatically, so a link points a colleague at the same file on the same tab - Light/dark theme with an OS-preference default and a header toggle, styled after clip-extractor and bbqs-uploader
- Load a video via drag-and-drop, the file picker, or Load Sample (bundled
mice.mp4) - Explore the Inspect tab for metadata, the codec explainer, the atom map, and GOP/frame structure
- Run the Seeking Test to measure nearest-keyframe distance and decode latency across the timeline (and see it plotted)
- Tune CRF, preset, keyframe interval, B-frames, faststart, and audio handling in the Reencode & CLI tab
- Copy the generated
ffmpegcommand, or head to the Reencode In-Browser tab and click Encode (exact) / Encode (fast) to transcode the whole video in the page and save the result - Try different CRF/preset values on a short clip in the Compare Quality tab and compare against the original side-by-side before running the full encode
- Click Full Analysis (to the right of the tabs) for the whole thing as one document: save it as HTML, print it to PDF, or copy it as Markdown
A TypeScript + Vite app; the video-handling libraries are ordinary npm dependencies rather than CDN <script> tags:
- mediabunny - metadata, packet/GOP analysis, frame seeking, WebCodecs-based fast reencode (lazy-loaded via a dynamic
import()the first time a file is loaded, and bundled into its own chunk) - mp4box.js - MP4 atom map and sample table (keyframes/GOP/B-frames); ships no TypeScript types, so a small local
.d.ts(src/lib/mp4box.d.ts) declares the slice this app actually uses - @ffmpeg/ffmpeg + @ffmpeg/util - exact in-browser reencode. As a proper ESM package, Vite bundles and loads its worker itself; the
ffmpeg-core.js/.wasmbinaries (~30 MB) are still fetched from the jsdelivr CDN at runtime via@ffmpeg/util'stoBlobURL(), the officially documented pattern, and only once an "exact" encode actually runs
- GitHub Pages serves no custom headers, so only the single-thread ffmpeg.wasm core is used (no COOP/COEP, no
coi-serviceworker) - this keeps the tool a single self-contained page at the cost of some encode speed - WebCodecs exposes no CRF control, only target bitrate/quality presets - the "fast" engine cannot byte-match the CLI command, and the UI says so
- Firefox's H.264 WebCodecs encoder support is weak; the fast engine feature-detects and falls back to ffmpeg.wasm/CLI-only
- ffmpeg.wasm is GPL-licensed and lazy-loaded on demand; credited in the dependency list above and in the Reencode tab, which names the engine at the point of use
npm install # install dependencies
npm run dev # start the Vite dev server
npm run build # typecheck + production build to dist/
npm test # run the unit test suite (vitest)
npm run lint # eslint (type-aware, strict)
npm run test:integration # Playwright integration tests (tests/integration/)
npm run storybook # component sandbox at http://localhost:6006 (stories/)Source lives under src/: src/lib/ holds pure logic (formatting, the MP4/codec parsers, the CLI-command builder, the ffmpeg.wasm/mediabunny encode engines), src/ui/ holds one renderer module per tab, and src/main.ts wires it all up to the static skeleton markup in index.html. Unit tests for the pure lib/ modules live under tests/unit/; Playwright integration tests live under tests/integration/. Component snapshots are driven by Storybook and Chromatic (both a Storybook build and a dedicated Playwright suite under tests/chromatic/), which visually regression-test the UI on every push.
New vibe: encoding-helper — didactic in-browser video encoding lab (inspect, teach, seek-test, reencode). See GitHub issue #66 for the full spec, motivated by the BBQS Day 3 working session on video → behavioral annotation pipelines (Acquisition and QC tracks), with sleap-io's
reencodeas the shared transcoding baseline this tool makes legible.