Privacy-first toolbox. Everything runs in your browser. No file is ever uploaded.
omne is a collection of 81+ online tools — PDF, video, audio, image, SVG, passwords, JSON, text, encoding, QR codes, colors, developer utilities — that run entirely client-side. No backend, no analytics, no third-party CDN, no cookies. Your files never leave the tab.
Bilingual English / French, light / dark mode, ⌘K command palette.
These are not feature ideas — they are constraints. Everything in the codebase has to honor them.
- 🛡️ Privacy by design. Files never leave the device. Processing happens in the browser, in WebAssembly when needed. There is no "trust us, we'll delete it later" — there is no server to delete from.
- 🔍 Verifiable, not promised. Open the DevTools Network tab during a conversion: you should see zero outbound requests. If you ever do, that's a bug, not a feature.
- 🚫 No tracking, ever. No analytics, no telemetry, no cookies, no third-party CDN, no fingerprinting. We don't know who you are and we don't want to know.
- 🧱 Self-hosted heavy assets. ffmpeg.wasm, pdf.js worker, the QR scanner and the background-removal ONNX model are all served from this origin so processing doesn't leak metadata to a CDN.
- 🆓 No friction. No signup, no account, no paywall, no rate limit, no "premium tier". Open the page, do the thing, close the tab.
- 🌍 Open source, built in the open. Every line is auditable on github.com/freezer71/omne — also linked from the site header and footer. If you can't verify a claim by reading the code, the claim doesn't count.
- 🌗 Accessible and bilingual. Light & dark themes with pre-paint anti-flash, English & French at full parity (enforced by CI), and an
⌘Kpalette for keyboard users.
This is the reason the project exists, not a marketing line:
- Every conversion runs in the browser via Web APIs, WebAssembly and Web Workers.
@ffmpeg/ffmpeg(video, audio) andpdfjs-dist(PDF) are self-hosted under/public/ffmpeg/and/public/pdfjs/— the DevTools Network tab must show zero outbound traffic during processing.- The app serves
Cross-Origin-Opener-Policy: same-originandCross-Origin-Embedder-Policy: require-corpto enableSharedArrayBuffer(required by the multi-thread ffmpeg build). Because every asset is same-origin, this hardens the page instead of breaking anything. - No external services: no Sentry, no Google Analytics, no tracking pixel, no asset CDN.
Any contribution that would break that promise (server-side processing, telemetry, remote asset loads) is out of scope.
Requirements: Node.js 20+.
npm install # runs the postinstall that copies ffmpeg + pdf.js + qr-scanner into /public
npm run dev # http://localhost:3000
⚠️ npm run devdeliberately uses Webpack (next dev --webpack). Turbopack panics on HMR through[locale]dynamic routes.npm run dev:turboexists for spot checks only.
After a clean clone, if ffmpeg/pdf.js features fail, re-run:
node scripts/copy-ffmpeg.mjs && node scripts/copy-pdfjs.mjs && node scripts/copy-qr-scanner.mjs| Category | Tools |
|---|---|
| Merge · Split · Rotate · Watermark · PDF → images · Extract images · Images → PDF | |
| Video | Convert (MP4 / WebM / GIF) · Trim · Split · Merge · Compress · Resize · Mute · Crop · Rotate / flip · Extract frames · Watermark · Speed · Short Studio (split + numbered watermark, optional 9:16) |
| Audio | Convert · Extract (from video) · Trim · Merge · Volume / normalize / fades · ID3 tags |
| Image | Convert · Compress · Resize · Crop · Rotate / flip · Remove background (local AI, beta) |
| SVG | Viewer · Editor · Optimize (SVGO) · → PNG / JPG / WebP · → Data URL · Favicon generator |
| Password | Generate · Passphrase (Diceware) · Hash (SHA) · bcrypt · Strength meter |
| JSON | Format · Tree · JSONPath · Table · CSV ↔ JSON · Diff · Schema (AJV) |
| Text | Case · Counter · Lorem Ipsum · Diff · Regex tester · Slugify · Sort lines · Escape/Unescape · Whitespace cleaner · Markdown preview · Find & Replace |
| Encoding | Base64 · URL · JWT · Hex · HTML entities · Binary · Morse code |
| QR | Generate (WiFi, vCard…) · Scan (camera or image) · Barcode generate (Code 128 / EAN) · Barcode scan |
| Color | Converter (hex / rgb / hsl / oklch) · WCAG contrast · Palette from image · Gradient builder · Tints & shades · Blender · Color-blindness simulator |
| Developer | Skills installer builder (parse npx skills add commands) · Browse & install skills (search the skills.sh catalog, discover top skills by All Time / Trending 24h / Hot, and install multiple in one one-liner) |
Source of truth: lib/tools/registry.ts. The sitemap, the home page cards and MIME-based drop routing all derive from it.
- Next.js 16 (App Router) + React 19
- TypeScript (strict)
- Tailwind v4 — CSS-first config (no
tailwind.config.js), tokens in@theme inlineinsideapp/globals.css - @ffmpeg/ffmpeg for video and audio — multi-thread build (
@ffmpeg/core-mt) to parallelize across CPU cores - pdf-lib + pdfjs-dist for PDFs
- @huggingface/transformers for image background removal (local ONNX model)
- SVGO for SVG optimization
- AJV for JSON Schema validation
- In-house i18n — no runtime, static dictionaries at
messages/{en,fr}.json - Vitest (unit + components + setup) + Playwright (e2e on chromium + webkit)
proxy.tsat the project root replaces the oldmiddleware.tsconvention. It sniffsAccept-Languageand 307-redirects bare paths into/[locale]/....- All routes live under
app/[locale]/.... There is noapp/layout.tsx— the only root layout isapp/[locale]/layout.tsx. paramsis a Promise in every page, layout andgenerateMetadata:const { locale } = await params;is mandatory.
Each tool follows the same 5-file skeleton plus one registry entry:
- Pure logic —
lib/tools/implementations/<id>.ts(testable in Node, no React) - Client component —
components/tools/<id>-tool.tsx('use client') - Page —
app/[locale]/<category>/<id>/page.tsx(Server Component) - OpenGraph image —
app/[locale]/<category>/<id>/opengraph-image.tsx - Registry entry —
lib/tools/registry.ts - Translations —
tools.<category>.<id>keys in bothmessages/{en,fr}.json(seoblock included, parity checked in CI)
Real-time preview is mandatory: every tool must render a live result that reflects the current parameters — drive it from a ~150–250 ms debounced effect, no "Apply" button before the preview shows. For heavy pipelines (ffmpeg, pdf.js, remove-bg), render a lightweight proxy (lower resolution, first page/frame).
Full details in CLAUDE.md.
| Command | Description |
|---|---|
npm run dev |
Dev server on :3000 (Webpack — see warning above) |
npm run build |
Production build |
npm run start |
Serve the production build |
npm run lint |
ESLint (flat config) |
npm test |
Vitest (unit + components + setup) one-shot |
npm run test:watch |
Vitest in watch mode |
npm run test:ui |
Vitest UI |
npm run test:e2e |
Playwright (chromium + webkit) |
npm run test:all |
Vitest then Playwright |
npm run favicons |
Regenerate app/favicon.ico (16/32/48) + app/apple-icon.png (180×180) from app/icon.svg |
Run a single test:
npx vitest run path/to/file.test.ts -t "test name"
npx playwright test tests/e2e/<name>.spec.ts --project=chromiumomne/
├── app/[locale]/ # Pages (Server Components) per category/tool
├── components/tools/ # Client components for each tool
├── lib/
│ ├── tools/
│ │ ├── implementations/ # Pure logic (testable outside the DOM)
│ │ ├── registry.ts # Source of truth for the catalog
│ │ ├── metadata.ts # SEO / OG / hreflang
│ │ └── mime-router.ts # MIME-based routing on drop
│ ├── i18n/ # Server-side dictionaries (server-only)
│ ├── ffmpeg-loader.ts # Singleton ffmpeg.wasm
│ └── file-utils.ts # downloadBlob, outputName, etc.
├── messages/{en,fr}.json # Translations (parity enforced in CI)
├── public/
│ ├── ffmpeg/ # ffmpeg-core.js + .wasm (postinstall-copied, gitignored)
│ ├── pdfjs/ # pdf.worker.min.mjs (same)
│ └── theme-init.js # Static script to avoid theme flash
├── proxy.ts # /[locale] redirect (replaces middleware.ts)
├── scripts/ # copy-ffmpeg, copy-pdfjs, copy-qr-scanner
└── tests/
├── unit/ # Vitest (Node env)
├── components/ # Vitest (jsdom) + RTL
├── setup/ # Cross-cutting tests (i18n parity…)
└── e2e/ # Playwright
- Implement the pure logic in
lib/tools/implementations/<id>.tswith a unit test. - Build the client component in
components/tools/<id>-tool.tsxwith a real-time preview. - Wire the page under
app/[locale]/<category>/<id>/page.tsx+ theopengraph-image.tsx. - Add the entry in
lib/tools/registry.ts. - Add
tools.<category>.<id>keys (including theseoblock) inmessages/en.jsonandmessages/fr.json. - Verify:
npm test(i18n parity + unit + components), thennpm run test:e2e.
The sitemap, MIME-based routing and the ⌘K palette pick the new tool up automatically.
Open source, built in the open.