GitHub Pages Deployment
Join Android Closed Beta 🤖
Shrinks a video to a target file size, entirely in the browser. Nothing gets uploaded. Encoding happens locally via WebCodecs (hardware-accelerated when the browser/GPU support it), with an ffmpeg.wasm fallback for browsers that don't.
It's installable as an app. An in-page banner offers to install it directly, or you can use your browser's own "Install app" and/or "Add to Home screen" option. After the first load it works fully offline, and when a new version ships, a small banner offers to reload and update on your own schedule instead of forcing it mid-conversion.
- Pick a target size (defaults to 25MB, Discord's non-Nitro upload limit). Duration and target size get turned into a bitrate request for the encoder.
- That request is approximate, not exact: actual output size depends on content complexity and how closely the browser's encoder honors the request. The first pass aims a bit under target; if it overshoots anyway, a second pass re-encodes with a corrected bitrate. Landing under target on the first try is accepted as-is, however far under.
- If the resulting bitrate is too thin for the source resolution, the video is downscaled to a size it can actually render well (a 4K clip squeezed into a few MB looks better at 720p than blocky 4K). The original resolution is always kept when the bitrate can support it.
- H.265 gets used instead of H.264 automatically when the browser reports hardware HEVC encode support, since it's meaningfully smaller at the same quality. There's a toggle if you'd rather force H.264 for compatibility.
- Metadata (location, title, artist, comments, embedded images) gets stripped by default. Toggleable if you want to keep it.
The interface is available in 72 languages. On a plain visit it follows your device language; pick a different one from the in-app language picker and that choice is remembered. English ships in the main bundle, and every other language is a separate chunk fetched only when selected, so it stays available offline once the app is installed.
Only English (src/locales/en.json) is written by hand. The rest are generated by a GitHub Actions workflow whenever the English strings change, using i18n-ai-translate (via Gemini) with a committed cache so reruns only translate new or changed strings. Traditional Chinese (zh-TW) is derived from the Simplified output with OpenCC rather than the API, since the translator only accepts ISO 639-1 codes.
- Mediabunny for the WebCodecs demux/decode/encode/mux pipeline
- ffmpeg.wasm, self-hosted rather than loaded from a CDN, as the CPU fallback
- vite-plugin-pwa for the installable/offline bits
- i18next with
i18next-browser-languagedetectorfor in-app localization and language detection - React + Vite, deployed to GitHub Pages via Actions
video-shrinker's own source is MIT.
The CPU-fallback encoder (@ffmpeg/core, only loaded when a browser can't do
WebCodecs hardware encoding) is a separately-distributed FFmpeg build compiled
with --enable-gpl --enable-libx264 --enable-libx265, which makes that binary
GPL-2.0-or-later, not MIT/LGPL. It's self-hosted from this repo as a static
asset. Its corresponding source is the upstream project at the exact version
in use: ffmpegwasm/ffmpeg.wasm @ v0.12.10
(build config).
Everything else shipped to the browser is permissive: React and React-DOM
(MIT), @ffmpeg/ffmpeg's JS wrapper and @ffmpeg/types (MIT), and Mediabunny
(MPL-2.0). Build-only tooling (Vite, TypeScript, oxlint, the PWA/static-copy
plugins) isn't distributed with the app and isn't covered here.
npm install
npm run dev
npm run build