Fit any photo into any social format — losslessly — then turn your photos into a video with music. Everything runs in your browser. No photo or audio ever leaves your machine.
Tall or wide photos don't fit neatly into a square Instagram post, a 9:16 Reel, or a 16:9 YouTube frame — platforms crop them. Frame solves this without cropping: it grows the canvas to the target shape and fills the new margins with colors drawn from the photo itself, leaving your original picture completely untouched.
Then it can string those photos into a video slideshow with motion, transitions, title cards, and your own music — exported as an MP4 directly in the browser.
Two workspaces, one app:
| ▦ Frame photos | ► Make a video |
|---|---|
| Turn each photo into a social-ready image with filled margins, saved as a lossless PNG. | Arrange photos into a slideshow with music, captions, motion, and transitions. |
The original picture stays untouched — only the new margins are filled (here with the Blurred photo style). A wide photo into a 9:16 Story, and a tall photo into a 1:1 Post:
This is the invariant the whole app is built around and continuously tested against real Chromium:
- Your photo is drawn into the canvas at a 1:1 pixel scale with image smoothing off — never resized, cropped, or resampled.
- Only the new margin area is ever painted.
- If a photo already matches the target shape, downloading hands you back the original file, byte-for-byte — same format, same metadata.
- Otherwise you get a lossless PNG.
- The in-app Verify button re-checks any photo live and proves the picture region is bit-identical to your upload.
Platforms still recompress on upload — that's outside any tool's control — but the file you hand them here is pristine.
- 7 platforms, 30+ presets — Instagram, TikTok, YouTube, X, Facebook, LinkedIn, plus Custom any-ratio.
- 8 side-fill styles — Matched edges, Stretch, Mirror, Blurred photo, Frosted, Color gradient, Duotone, Solid color (auto/black/white/pick).
- Edge shading to softly deepen the frame edges.
- "Show where the original sits" guide overlay (never added to the download).
- Saveable presets — save a favorite fill + settings combo and one-click apply it.
- Batch — drop many photos at once and download them all.
- Drag-reorder clips, zoom the timeline, snap audio to cuts.
- Per-clip overrides — motion, color look, and outgoing transition per clip (or set global defaults).
- Motion — Still, Zoom in, Zoom out, Pan.
- Transitions — Cut, Crossfade, Fade black, Slide, Slide up, Wipe, Iris.
- Color looks — None, Warm, Cool, Mono, Vivid.
- Title / text cards with custom background & text colors.
- Captions per clip, alpha-crossfaded through transitions.
- Clips up to 10 minutes each, with an editable length field.
- Save any frame as a PNG — e.g. a Reel cover that exactly matches the video.
- Multiple tracks on independent lanes, positioned anywhere.
- Per-track volume, fade in / out, and auto-duck under voice.
- Voiceover recording straight from your microphone.
- "Match clips to music" to line the video up with a track.
- Fast export via WebCodecs (H.264 + AAC), muxed by Mediabunny — a 60s video exports in seconds, fully offline.
- Honest real-time recording fallback on browsers without WebCodecs (and it says so).
- Heads-up before export — estimated file size, resolution, quality, and whether your music lines up with the video length.
- Live progress with time-left, and a Cancel that cleanly aborts.
- 100% client-side — nothing is uploaded, ever.
- Session autosave to IndexedDB (settings + original media + timeline), restored behind an explicit bar on next visit.
- Ships as a web app, an installable PWA, and a desktop app (Windows / macOS) via Electron.
Sidebar to switch between framing and video; drop photos and see them framed instantly.
Instagram, TikTok, YouTube, X, Facebook, LinkedIn, or a Custom ratio — each with its real dimensions.
Eight fill styles, edge shading, a preview guide, and saveable presets.
Each card shows the framed result, the exact dimensions, and a lossless-PNG (or untouched-original) badge — plus Verify.
The optional guide overlays the untouched picture region — never part of the download.
Live preview, a draggable timeline, motion / transition / look / quality controls.
Independent audio lanes with volume, fade in/out, and duck-under-voice — and a heads-up when the music doesn't fill the video.
Select any clip for length, order, duplicate/delete, per-clip motion/look/transition, and a caption.
Drop in title cards with your own colors between photos.
Photos added on the Frame tab default to framed; photos added on the Make a video tab default to the untouched original — flip any one with a toggle.
A plain-language heads-up: rough size, resolution, quality, and whether the soundtrack fits.
| Platform | Presets |
|---|---|
| Post 1:1, Post 4:5, Post 3:4, Post 1.91:1, Story 9:16, Reel 9:16, Reel cover, Profile 1:1 | |
| TikTok | Video 9:16, Photo 3:4, Profile 1:1 |
| YouTube | Shorts 9:16, Video 16:9, Thumbnail 16:9, Banner 16:9, Profile 1:1 |
| X | Post 16:9, Post 1:1, Header 3:1, Profile 1:1 |
| Post 1.91:1, Post 1:1, Story 9:16, Cover 2.63:1, Profile 1:1 | |
| Post 1.91:1, Post 1:1, Cover 4:1, Profile 1:1 | |
| Custom | Any width × height |
Requires Node 18+ and a recent Chrome / Edge (for WebCodecs fast export).
git clone https://github.com/ali-dev178/frame.git
cd frame
npm install
npm run dev # web app at http://localhost:5173npm run dev:desktop # the same app inside an Electron window (hot reload)
npm run dist # build an installer into release/
npm run smoke:desktop # verify the WebCodecs export pipeline in packaged Electronnpm run build # typecheck + production build into dist/
npm run preview # preview the production buildGrab the installer for Windows or macOS from the Releases page.
Code-signing certificates cost money every year (Apple's Developer Program is ~$99/yr; Windows certificates are ~$200–600/yr). Frame is a free, open-source project, so paying for signing isn't worth it — the installers are shipped unsigned. That's completely safe (every build is produced in public by GitHub Actions straight from this source code — you can read exactly what runs), but your OS will show a scary-looking warning the first time. Here's how to get past it:
Windows — SmartScreen shows "Windows protected your PC":
- Click More info.
- Click Run anyway.
macOS — Gatekeeper says the app "can't be opened" or is "from an unidentified developer":
- Right-click (or Control-click) the app → Open, then Open again in the dialog; or
- Open System Settings → Privacy & Security, scroll down, and click Open Anyway next to the Frame message.
You only have to do this once per install.
Everything runs the same locally — no build needed to just use it:
git clone https://github.com/ali-dev178/frame.git
cd frame && npm install
npm run dev:desktop # the desktop app, or `npm run dev` for the browser- Drop photos onto the Frame tab (JPG, PNG, WebP — several at once).
- Pick a platform and format in the top bar (e.g. Instagram → Reel 9:16).
- Choose a side-fill style and tweak edge shading; save a preset if you like the combo.
- Download each image (lossless PNG, or your original untouched) — or Download all.
- Switch to Make a video, tick the photos you want, and arrange them on the timeline.
- Add motion, transitions, a look, title cards, captions, and your music.
- Export — a heads-up shows the size and fit; then a fast MP4 (or real-time fallback) with live progress.
On iPhone, set Camera → Formats → "Most Compatible" (or convert to JPG/PNG) — browsers can't decode HEIC. Frame tells you clearly if a file couldn't be opened.
| Capability | Chrome / Edge | Safari | Firefox |
|---|---|---|---|
| Framing & lossless PNG | ✅ | ✅ | ✅ |
| Fast MP4 export (WebCodecs) | ✅ | ❌ | |
| Real-time recording fallback | ✅ | ✅ | ✅ |
Frame detects capabilities at runtime and tells you honestly which path it will use before you export.
No UI framework — imperative DOM + canvas, TypeScript, Vite. CLAUDE.md documents the internals in depth.
src/
├── main.ts entry point
├── state.ts settings (S) + app collections + selectors
├── core/ config, layout, colors, naming, IndexedDB, project autosave
├── render/ margin fills, framed-canvas builder, single frame renderer
├── audio/ Web Audio scheduling, fades, offline mixdown
├── export/ capability probe, WebCodecs fast path, MediaRecorder fallback
├── platform/ the only browser-vs-desktop split (save adapter)
└── ui/ dom, controls, cards, studio, timeline, soundtrack, presets, restore
electron/ desktop shell (window + IPC + smoke probe)
Key ideas:
- One frame renderer (
drawAtTime) powers the live preview, the fast exporter, and the recording fallback — what you preview is exactly what exports. - UI is generated from config arrays — add a format, fill, motion, or transition by extending an array in
src/core/config.ts. - Two export engines, prefers fast, falls back honestly.
npm test # all tests once
npm run test:watch # watch mode
npx vitest run tests/unit/layout.test.ts # a single file- Unit tests (
tests/unit/) run in Node. - Browser tests (
tests/browser/) run in real headless Chromium via Playwright (npx playwright install chromiumif missing). tests/browser/lossless.test.tsenforces the pixels-are-untouched invariant for every fill mode and must stay green.
npm run dev # serve the app on :5173
node scripts/shots.mjs # captures docs/screenshots/*.png with generated sample mediaPush a v* tag → GitHub Actions builds Windows (NSIS) and macOS (dmg/zip, universal, unsigned) installers and attaches them to a GitHub Release. Each build first runs a WebCodecs smoke probe inside packaged Electron on that OS, so an installer only ships if fast export actually works there.
Contributions are welcome! Please:
- Open an issue to discuss substantial changes first.
- Keep the lossless invariant intact —
tests/browser/lossless.test.tsmust stay green. - Run
npm testandnpm run typecheckbefore opening a PR. - Match the existing imperative DOM + canvas style (no framework).
See CONTRIBUTING.md for details.
Frame is fully client-side. Your photos, audio, and videos are processed entirely in your browser (or the desktop app) and never uploaded anywhere. Session data is stored only in your browser's local IndexedDB.
MIT © 2026 Muhammad Ali













