Skip to content

cloakyard/cloakimg

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

101 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
CloakIMG β€” A minimal photo editor that respects your photos

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

MIT License Deployed on Cloudflare Workers Platform: Web & PWA

100% client-side On-device AI


✨ Features

CloakIMG is a full-featured photo editor, all running 100% client-side:

🎨 Editing Tools

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

🧠 On-Device AI

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 .onnx weights 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 AiErrorBoundary so 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.

🧭 Workspace

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

πŸ“€ Import, Export & Persistence

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

πŸ”’ Privacy First

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

πŸ› οΈ Tech Stack

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

πŸš€ Getting Started

Prerequisites

  • Node.js β‰₯ 24.x (LTS recommended)
  • Vite+ (vp) β€” install globally via npm i -g vite-plus

Installation

# Clone the repository
git clone https://github.com/cloakyard/cloakimg.git
cd cloakimg

# Install dependencies
vp install

# Start the development server
vp dev

Available Commands

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

πŸ“ Project Structure

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

Folder conventions

  • src/components/ β€” anything that's used by both the landing and the editor (or by the top-level App shell). 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.

βš™οΈ How It Works

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 working HTMLCanvasElement; Undo / Redo walk 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-p3 where 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 subjectMask module-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 (under src/editor/ai/runtime/) so the editor stays responsive on big photos. Source pixels travel as transferable ImageBitmaps 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 ArrayBuffer payloads. 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.


🚒 Deployment

CloakIMG is deployed to Cloudflare Workers (static assets) on every push to main.

The deployment pipeline:

  1. Checks out the code
  2. Installs dependencies with vp install
  3. Builds the production bundle with Vite
  4. Publishes the dist/ folder to Cloudflare via Wrangler (wrangler.jsonc)

The custom domain img.cloakyard.com is bound to the Worker through Cloudflare DNS.


🀝 Contributing

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.


πŸ“„ License

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

About

A open source, minimal, browser-based photo editor that respects your photos

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

Packages

 
 
 

Contributors