A fast, modern, privacy-focused photo editor that runs entirely in your browser.
No uploads, no servers, no tracking β your photos never leave your device.
Includes on-device AI for subject detection, scoped tone edits, portrait blur, and background removal.
Try it here β img.cloakyard.com
CloakIMG is a full-featured photo editor, all running 100% client-side:
Every tool you need on a single canvas. Tools tagged with β¨ use on-device AI subject detection (see below).
| Tool | Description |
|---|---|
| Crop & Rotate | Free, fixed-ratio, or preset aspect-ratio crops. Rotate 90Β°, mirror, and straighten with a tilt slider |
| Perspective | Drag four corner handles to undo keystoning or warp the photo onto a target quad β async chunked warp keeps the UI responsive on big images |
| Adjust β¨ | Exposure, contrast, highlights, shadows, whites, blacks, saturation, vibrance, temperature, vignette, sharpen, and a Catmull-Rom tone curve β scoped to whole / subject / background |
| Levels β¨ | Photoshop-style input black/white/gamma + output black/white sliders, scoped to whole / subject / background |
| Selective Colour β¨ | Per-band Hue / Saturation / Luminance across eight colour bands, scoped to whole / subject / background |
| Filters β¨ | 27 hand-tuned presets organised by character (subtle, warm, vintage, saturated, cool, cinematic, faded, sepia, monochrome) with intensity + grain β also subject/background scoped |
| Background Blur β¨ | Portrait-mode-style depth-of-field β the subject stays sharp while the background gets a tunable gaussian blur |
| Remove BG β¨ | One-click background removal via RMBG-1.4 running locally in a Web Worker (WebGPU when available, WASM fallback), plus a chroma-key fallback for flat studio backdrops |
| Spot Heal | One-tap blemish removal with content-aware patching |
| Redact | Black-bar, blur, or pixelate sensitive regions. Rectangular, freehand, and OCR-friendly text redaction |
| Frame | Polaroid, film, modern matte, and ratio frames with adjustable colour and thickness |
| Border | Solid pixel-thick border or aspect-padded matte (Square, 4:5, 16:9 etc.) with custom colour |
| Resize | Lanczos-3 resampling for pin-sharp downscales β pick exact pixels, percentage, or aspect-locked dimensions |
| Draw & Pen | Pressure-sensitive freehand brush plus a vector pen with anchor handles for clean curves |
| Shapes | Rect, rounded-rect, ellipse, lines, arrows, polygons, hearts, stars β fill, stroke, opacity |
| Text | Multi-line text with font, weight, size, colour, italic, underline, stroke, character spacing, and curve-along-arc controls |
| Stickers | Built-in sticker sets plus your own custom uploads (saved locally and reusable across sessions) |
| Watermark | Repeatable text or image watermark with anchor + opacity |
| Place Image | Composite a second photo on top β drag, scale, rotate |
| Colour Picker | Eyedrop any pixel into the active swatch |
Every model runs locally in your browser. Your photos are never sent anywhere.
CloakIMG includes a built-in subject-detection model (RMBG-1.4, an IS-Net derivative) loaded through Transformers.js that powers a handful of tools:
| Capability | What it does |
|---|---|
| Subject scoping | Adjust, Levels, Selective Colour and Filters expose an Apply to: Whole / Subject / Background control β common moves like "darken the background" or "boost saturation on the subject only" become a one-tap operation. |
| Background blur | A new Background Blur tool keeps the subject pin-sharp while gaussian-blurring the background for a phone-style portrait look. |
| Background removal | Remove BG runs the same model end-to-end, alpha-keying your subject for transparent PNG export, sticker creation, or compositing. |
| Shared cache | The mask is detected once per image and reused across every subject-aware tool. Trigger detection in any one of them and the rest are instantaneous. |
How the privacy story holds up:
- No server inference. The model is loaded as static
.onnxweights and run in a Web Worker via ONNX Runtime Web β WebGPU on supported devices, WebAssembly SIMD elsewhere. Inference is read-only β nothing about your photo is sent back over the network. - No telemetry. The image bytes never leave your browser tab. You can verify in DevTools β Network that the only traffic is the one-time model download from
huggingface.co. - Explicit, informed consent. No bytes are fetched until you tap Download in a dialog that names the model, lists every tier with strengths and trade-offs, shows an "Already downloaded" badge for tiers that won't re-fetch, and lets you back out with Not now.
- First run only. The model files (~44 MB Fast / ~88 MB Better / ~176 MB Best β the same RMBG-1.4 weights at int8 / fp16 / fp32) download on first use and are then cached by the browser. After that, every AI feature works fully offline.
- Quality you control. Pick the smallest dtype for fast detection on phones, or the largest for the cleanest edges β all three are real ONNX models, no quality-degrading client-side approximation.
- Lazy-loaded. Opening a subject-aware tool doesn't pull in any AI code; only picking the Subject or Background scope (or hitting Apply on Remove BG) actually starts the download.
- Honest cancel. Mid-detection Cancel actually terminates the worker thread β no graceful interrupt exists in ONNX runtime, so we own the worker outright. Pipeline weights stay cached for the next attempt.
- Localised error recovery. AI render errors are caught by an
AiErrorBoundaryso a model load failure can never take down the rest of the editor; the user keeps their work-in-progress and can retry from the consent dialog. - Verbose logs on demand. Run
localStorage.ai_debug = "1"in DevTools to surface every state transition (runtime,worker,subjectMask,consent) under a[ai]prefix when reproducing a bug.
Designed for fast, focused editing
| Feature | Description |
|---|---|
| Layered editing | Text, watermark, watermark image, and draw layers stay non-destructive until you commit β toggle visibility, reorder, edit live |
| Undo / Redo | Full history stack with keyboard shortcuts (βZ / ββ§Z) β every tool commit is reversible |
| Compare | Hold to flash the original side-by-side with the current edit |
| Reset to original | One click rewinds the working canvas to the source image |
| Pan + pinch zoom | Two-finger gestures on touch, Space-drag + Cmd-scroll on desktop β anchored zoom that feels native |
| Keyboard driven | Single-key shortcuts for every tool, slider nudges with arrow keys, modifier-locked rotation |
| Mobile-first | A bottom sheet panel, sticky toolbar, and touch-tuned hit-targets so editing on a phone never feels cramped |
| Light & dark mode | Follows your OS prefers-color-scheme and toggles manually from the topbar |
Your work is always within reach
| Feature | Description |
|---|---|
| Drag, drop, paste | Drop a file from anywhere, paste from clipboard, or pick from disk β all routed through the same code path |
| HEIC support | Native HEIC/HEIF decoding via libheif-js (WASM), so iPhone photos open directly without conversion |
| EXIF reader | Inspect camera, lens, exposure, GPS, and date metadata before you export |
| EXIF stripping | One-tap toggles to scrub GPS, camera info, or timestamps from JPEG exports |
| Recents | The last 10 files you opened, stored locally in IndexedDB so you can pick up where you left off |
| Autosave drafts | Your in-progress edit is auto-saved every few seconds so an accidental close doesn't lose work |
| Batch mode | Apply the same recipe (resize, format convert, watermark, EXIF strip) across dozens of files in one pass |
| Export | JPEG, PNG, WebP β pick quality, target size bucket, and whether to retain the EXIF block |
| Wide-gamut output | Display-P3 canvas binding where supported, so colours hold up on modern phones and laptops |
| No uploads | Every byte stays on this device |
| No server-side processing | Zero network requests for your image data β verified by a strict Content Security Policy |
| On-device AI | The RMBG-1.4 subject-detection model runs in a Web Worker via ONNX Runtime Web (WebGPU when available, WASM fallback). Image pixels never leave the tab |
| One-time, cached model | The model file is downloaded once on first AI use and cached by the Service Worker; thereafter every AI feature works offline |
| No data collection | No analytics, no tracking, no cookies |
| One-tap EXIF strip | Scrub GPS, camera info, and timestamps from JPEG exports with a single toggle |
| Strict CSP | Content Security Policy blocks any unintended egress |
| Fully offline capable | Works without an internet connection after initial load |
| Category | Technology |
|---|---|
| Framework | React 19 |
| Styling | Tailwind CSS 4 |
| Canvas | Fabric.js 7 for layered objects + a pure 2D canvas pipeline for filters and per-pixel ops |
| Build Tool | Vite+ (Vite + Rolldown unified toolchain) |
| Language | TypeScript 6 |
| HEIC Decode | libheif-js β Rust/C HEIC decoder compiled to WebAssembly, lazy-loaded only when needed |
| On-device AI | Transformers.js hosting RMBG-1.4 on ONNX Runtime Web (WebGPU primary, WASM fallback) in a Web Worker we own |
| PWA / Offline | Workbox via vite-plugin-pwa |
| Toolchain CLI | Vite+ (vp) |
| Hosting | Cloudflare Workers static assets |
- Node.js β₯ 24.x (LTS recommended)
- Vite+ (
vp) β install globally vianpm i -g vite-plus
# Clone the repository
git clone https://github.com/cloakyard/cloakimg.git
cd cloakimg
# Install dependencies
vp install
# Start the development server
vp dev| Command | Description |
|---|---|
vp dev |
Start the Vite dev server with hot reload |
vp build |
TypeScript check + production build |
vp preview |
Preview the production build locally |
vp check |
Run format, lint, and type checks |
vp test |
Run tests |
cloakimg/
βββ public/ # Static assets (icons, manifest, OG image, 404 page)
βββ src/
β βββ main.tsx # App entry point
β βββ App.tsx # Routing between landing & editor
β βββ style.css # Global styles + keyframes
β βββ tokens.css # Design tokens (colour, type, motion)
β β
β βββ components/ # Cross-cutting UI primitives shared by landing + editor
β β βββ icons.tsx # Lucide-style icon set used app-wide
β β βββ ModalFrame.tsx # Shared dialog frame (centered + bottom-sheet variants)
β β βββ ErrorBoundary.tsx # Top-level render-error catch
β β βββ DropZone.tsx # Drag/drop/paste + file-picker zone
β β βββ OrientationLock.tsx # Portrait-only guard for narrow phones
β β βββ SamplePhoto.tsx # Inline sample photo for the landing CTA
β β
β βββ constants/ # Single source of truth for app-wide constants
β β βββ links.ts # GitHub repo / org / author / issues URLs
β β
β βββ utils/ # Pure, stateless helpers shared across features
β β βββ formatBytes.ts # Exact + rough byte-count formatters
β β
β βββ landing/ # Landing page (hero, sunset backdrop, features, footer)
β βββ editor/ # The single-canvas editor
β βββ EditorContext.tsx # Document, history, layers, autosave
β βββ ImageCanvas.tsx # Fabric-backed canvas + pan/zoom/transform
β βββ tools/ # One file per tool (Crop, Adjust, Filter, Redact, β¦)
β βββ ai/ # All on-device AI code lives here:
β β βββ subjectMask.ts # central mask service (singleton, shared across tools)
β β βββ useSubjectMask.ts # React hook on top of the service
β β βββ log.ts # structured logger (`[ai] runtime β¦` etc.)
β β βββ runtime/ # main-thread bridge to the AI Web Worker
β β β βββ runtime.ts # runAi() singleton + abort handling
β β β βββ worker.ts # Web Worker hosting transformers.js pipelines
β β β βββ segment.ts # smartRemoveBackground() facade + tier registry
β β β βββ types.ts # shared AiRequest / AiResponse / AiProgress types
β β β βββ cache.ts # CacheStorage probe (isHfModelCached)
β β β βββ progress.ts # multi-file download progress aggregator
β β βββ ui/ # AI surfaces (consent / progress / scope / error boundary)
β β βββ MaskConsentDialog.tsx # tier picker before any byte is fetched
β β βββ MaskConsentHost.tsx # listens to mask state, mounts the right modal
β β βββ MaskDownloadDialog.tsx # live progress + cancel + retry
β β βββ MaskScopeRow.tsx # "Apply to: Whole / Subject / Background" segment
β β βββ DetectionStatus.tsx # inline progress / ready / error chips
β β βββ AiSectionHeader.tsx # uppercase "Smart adjustments" eyebrow
β β βββ ScopeGate.tsx # greys out controls while detection is loading
β β βββ AiErrorBoundary.tsx # local fallback so AI errors never crash the editor
β βββ ExportModal.tsx # Format, quality, EXIF, target size pipeline
β
βββ index.html # HTML entry point + meta/OG tags + CSP
βββ vite.config.ts # Vite + Tailwind + PWA configuration
βββ wrangler.jsonc # Cloudflare Workers static-assets config
βββ tsconfig.json # TypeScript configuration
βββ package.json
src/components/β anything that's used by both the landing and the editor (or by the top-levelAppshell). Feature-local components stay inside their feature folder (landing/,editor/) so the boundary stays clear.src/constants/β values that are referenced from more than one place and would be painful to update if duplicated (URLs, slugs, hard-coded keys). Single-file defaults stay local; only promote here when a constant becomes shared.src/utils/β pure, framework-agnostic helpers. No React, no DOM-specific state, no project-specific assumptions. Domain helpers (canvas math, EXIF, colour spaces) live next to their tools instead.
CloakIMG is a single-page React app that keeps every photo entirely in memory and in IndexedDB.
- One working canvas β every tool commits its bake into a single
workingHTMLCanvasElement;Undo/Redowalk a history stack of canvas snapshots so any edit can be unwound. - Layered overlays β text, watermarks, drawings, stickers, and pen paths render as live Fabric objects on top of the working bitmap. They stay non-destructive until export (or until you deliberately commit them).
- Live previews at 25% β Adjust and Filter previews render through a downsampled (β€720px long-edge) copy of the working canvas, so slider drags stay buttery on multi-megapixel images. The full-resolution bake runs once when you switch tools.
- Wide-gamut path β every off-screen canvas is bound to
display-p3where supported, so colour-managed sources hold their punch on modern phones and laptops. - HEIC β iPhone photos are decoded via libheif-js (lazy-loaded WASM) so HEIC files open without converting upstream.
- Subject mask service β a single
subjectMaskmodule-level cache holds the RMBG-1.4 cut for the active image; every subject-aware tool peeks first and only pays the inference cost when the cache misses (new image, dimensions changed). Detection runs in a dedicated Web Worker (undersrc/editor/ai/runtime/) so the editor stays responsive on big photos. Source pixels travel as transferableImageBitmaps in both directions β no PNG round-trip. - EXIF surgery β JPEG exports rebuild the APP1 segment from the original bytes; per-export toggles let you keep the full metadata, or selectively scrub GPS, camera info, and timestamps before download.
- Recents + autosave β the last 10 opened files (and an in-progress draft of your active edit) live in IndexedDB as
ArrayBufferpayloads. Files round-trip reliably across sessions on every browser including iOS Safari. - PWA β an aggressive Workbox runtime cache lets the editor open instantly and run fully offline once installed.
All operations happen in-memory or in IndexedDB. The strict Content Security Policy in index.html blocks any outbound network requests for user content β it is architecturally impossible for your photo to leave your device.
CloakIMG is deployed to Cloudflare Workers (static assets) on every push to main.
The deployment pipeline:
- Checks out the code
- Installs dependencies with
vp install - Builds the production bundle with Vite
- Publishes the
dist/folder to Cloudflare via Wrangler (wrangler.jsonc)
The custom domain img.cloakyard.com is bound to the Worker through Cloudflare DNS.
Contributions are welcome β new filter presets, tool refinements, accessibility fixes, and HEIC/RAW format coverage especially. Open an issue or a pull request to get started. See CONTRIBUTING.md.
This project is licensed under the MIT License β feel free to use it for both personal and commercial purposes. See the LICENSE file for details.
Built with β€οΈ by Sumit Sahoo