Skip to content

vanessamadison/nuul

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

53 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐˜•๐˜ถ๐˜ถ๐˜ญ

Version TypeScript Next.js Zero Network Zero Storage Lightroom Presets License Author


๐˜–๐˜ท๐˜ฆ๐˜ณ๐˜ท๐˜ช๐˜ฆ๐˜ธ

Nuul is a browser-native privacy photo studio that strips EXIF, GPS, and device metadata from photos up to 50 MB โ€” entirely in your browser, with zero server storage and zero network transmission.

It is also a full consumer-grade filter app. Choose from sixteen built-in color grades or import your own Lightroom .xmp presets. Every export is re-encoded pixel-by-pixel through a full Lightroom-compatible pipeline: the output file contains only image data. No metadata containers, no GPS coordinates, no device fingerprints survive.

Nuul is for people who care about both how their photos look and what their photos reveal.


๐˜ž๐˜ฉ๐˜ข๐˜ต ๐˜ช๐˜ต ๐˜ญ๐˜ฐ๐˜ฐ๐˜ฌ๐˜ด ๐˜ญ๐˜ช๐˜ฌ๐˜ฆ

Original photo before processing
The original โ€” metadata intact, device and location embedded in every pixel container
Photo after Kelp filter applied
Filtered with Kelp โ€” deep organic tones applied, all metadata structurally removed at export

Nuul studio desktop view
Studio desktop โ€” filter carousel with live thumbnails, each card rendering the actual image in real time
Nuul studio mobile view
Mobile view โ€” Cornerclub selected, the 35mm warm grade active on upload



Nuul receipts view
Privacy receipts โ€” signed JSON logs of what was found and removed, stored in a private local vault
Nuul settings page
Settings โ€” local-only preferences, network monitor showing zero requests, developer mode for verification

๐˜’๐˜ฆ๐˜บ ๐˜Š๐˜ข๐˜ฑ๐˜ข๐˜ฃ๐˜ช๐˜ญ๐˜ช๐˜ต๐˜ช๐˜ฆ๐˜ด

  • Sixteen aesthetic filter presets Silverline, Cornerclub, Midnightrun, Loftsunday, Basement, Bluehour, Faded, Mocha, Vapor, Golden Hour, Ice, Kelp, Ritual Night, Candlelight, Soft Grain, and Ritual. Every preset applies the full seven-stage pixel pipeline. Filter choice is aesthetic โ€” privacy protection is identical across all.

  • Lightroom preset support Import .xmp files directly from Lightroom Classic or Lightroom CC. Presets are parsed in the browser and applied via a pixel-level color grading engine with no server, no conversion tool, no upload.

  • Complete metadata removal Canvas re-encoding strips EXIF, GPS coordinates, device make/model, embedded thumbnails, and all other metadata containers by construction โ€” not by deletion. The output blob is structurally clean.

  • OSINT obfuscation scan Before export, nuul scans for common leaks: API keys, emails, addresses, phone numbers via OCR; QR codes via pixel analysis; face regions via browser-native face detection; browser chrome and open tabs via heuristic screen analysis.

  • Privacy receipt Every export generates a signed JSON receipt documenting what was found and removed. Receipts are stored in a local-only vault within your browser, ensuring no data ever touches a server or is saved to an external database.

  • Zero-network architecture No analytics. No telemetry. No CDN requests during processing. Tesseract OCR assets are served locally. All computation runs in the browser tab.


๐˜๐˜ช๐˜ญ๐˜ต๐˜ฆ๐˜ณ ๐˜Œ๐˜ฏ๐˜จ๐˜ช๐˜ฏ๐˜ฆ

The nuul filter engine applies Lightroom-compatible color grading through a multi-stage pixel pipeline:

  1. Tone curve LUT โ€” exposure (linear EV scale), contrast (S-curve around pivot 0.5), highlights/shadows (tone-range adjustments), whites/blacks (endpoint compression)
  2. Per-channel RGB curves โ€” independent tone curves for red, green, blue channels using Fritsch-Carlson monotone cubic spline interpolation
  3. Temperature / Tint โ€” per-pixel color shift derived from the raw Kelvin value and green/magenta tint offset stored in the XMP
  4. Saturation โ€” global HSL saturation multiplier
  5. Vibrance โ€” selective saturation that boosts desaturated colors more, leaving already-saturated tones stable
  6. HSL per-hue adjustments โ€” eight Lightroom color ranges (red, orange, yellow, green, aqua, blue, purple, magenta) with cosine bell falloff
  7. Vignette + Grain โ€” radial gradient overlay and per-pixel luminance noise

Preview rendering runs at 900px for sharp real-time feedback. Export rendering runs at full resolution. Carousel thumbnails are generated live from the imported image at 120px.

Importing Lightroom Presets

Lightroom exports presets as .xmp files containing XML with crs: namespace attributes. Nuul parses these directly in the browser using the DOM parser and maps the following fields:

Exposure2012, Contrast2012, Highlights2012, Shadows2012, Whites2012, Blacks2012, Temperature, Tint, Vibrance, Saturation, Clarity2012, Dehaze, VignetteAmount, GrainAmount, SharpenAmount, PresetName

Multiple presets can be imported at once. Imported presets appear in the filter strip alongside built-ins with live thumbnails auto-generated from the current image.


๐˜—๐˜ณ๐˜ช๐˜ท๐˜ข๐˜ค๐˜บ ๐˜”๐˜ฆ๐˜ค๐˜ฉ๐˜ข๐˜ฏ๐˜ช๐˜ด๐˜ฎ

The privacy guarantee is architectural, not procedural. Nuul does not delete metadata from the source file. Instead, it draws the image bitmap onto an HTML Canvas element โ€” which captures only pixel data โ€” and encodes a new blob from that canvas. The resulting file has no metadata containers because none were ever written.

This means:

  • EXIF data (camera settings, timestamps, software) is gone because canvas has no mechanism to carry it
  • GPS coordinates are gone for the same reason
  • Device identifiers (Make, Model, Serial) are gone
  • Embedded thumbnail variants stored in JPEG headers are gone
  • ICC color profiles are stripped (canvas uses sRGB)

The approach is verified post-export using exifr.parse() on the output blob. A result of null or an empty object confirms clean output.


๐˜–๐˜š๐˜๐˜•๐˜› ๐˜š๐˜ค๐˜ข๐˜ฏ ๐˜—๐˜ช๐˜ฑ๐˜ฆ๐˜ญ๐˜ช๐˜ฏ๐˜ฆ

flowchart TB
    Input[Image File] -->|Decode| Bitmap[ImageBitmap]
    Bitmap --> Canvas[Analysis Canvas 1400px]
    Canvas --> OCR[Tesseract OCR]
    Canvas --> QR[jsQR โ€” QR Detection]
    Canvas --> Face[Browser FaceDetector API]
    Canvas --> Screen[Screen Heuristics]
    OCR --> PII[PII Rules โ€” API keys, emails, addresses]
    PII --> Findings[ScanFindings]
    QR --> Findings
    Face --> Findings
    Screen --> Findings
    Findings --> Risk[Risk Level: social / work / high]
    Risk --> Studio[Studio UI]
    Studio --> Filter[Color Grade Filter]
    Filter --> Export[Canvas Re-encode โ†’ Clean Blob]
    Export --> Receipt[JSON Privacy Receipt]
Loading

๐˜›๐˜ฆ๐˜ค๐˜ฉ ๐˜š๐˜ต๐˜ข๐˜ค๐˜ฌ

Core

  • Next.js 14, React 18, TypeScript 5
  • Tailwind CSS

Privacy Pipeline

  • exifr โ€” EXIF parsing for scan and post-export verification
  • jsQR โ€” pure-JS QR code detection from ImageData
  • Tesseract.js โ€” local OCR (assets served from /public/tesseract/, no network)
  • Browser FaceDetector API โ€” face region detection where available
  • Canvas 2D API โ€” pixel-level filter engine and metadata-stripping re-encode

Distribution

  • Static export via next export โ€” deployable to Vercel, Netlify, Cloudflare Pages, or any CDN
  • No backend required. No server. No database.

๐˜˜๐˜ถ๐˜ช๐˜ค๐˜ฌ ๐˜š๐˜ต๐˜ข๐˜ณ๐˜ต

Requirements

  • Node.js 20 or newer
  • npm or pnpm

Local setup

# Install dependencies
npm install

# Start dev server
npm run dev
# โ†’ http://localhost:3000

Deploy to Vercel (recommended)

# One-time setup โ€” connect your GitHub repo at vercel.com
# or push and deploy from CLI:
npx vercel --prod

Deploy to Cloudflare Pages

Set build command npm run build, output directory out. Connect the GitHub repo. Free tier, global CDN, no traffic limits.

Static export

npm run build
# Output in /out โ€” drop onto any static host

๐˜œ๐˜ด๐˜ช๐˜ฏ๐˜จ ๐˜ ๐˜ฐ๐˜ถ๐˜ณ ๐˜“๐˜ช๐˜จ๐˜ฉ๐˜ต๐˜ณ๐˜ฐ๐˜ฐ๐˜ฎ ๐˜—๐˜ณ๐˜ฆ๐˜ด๐˜ฆ๐˜ต๐˜ด

  1. In Lightroom, right-click a preset โ†’ Export โ†’ save as .xmp
  2. In Nuul Studio, click Import Lightroom preset (.xmp) in the filter strip
  3. Select one or multiple .xmp files
  4. The preset appears in the strip with a live thumbnail generated from your current image
  5. Click to apply โ€” the preview updates in real time at 900px
  6. Export โ€” the filter is baked into the output at full resolution before metadata stripping

๐˜’๐˜ฏ๐˜ฐ๐˜ธ๐˜ฏ ๐˜“๐˜ช๐˜ฎ๐˜ช๐˜ต๐˜ข๐˜ต๐˜ช๐˜ฐ๐˜ฏ๐˜ด

  • Canvas re-encoding converts to sRGB โ€” wide-gamut (P3, ProPhoto) color profiles are not preserved
  • Face detection uses the browser FaceDetector API โ€” not available in Firefox or older Safari; falls back silently
  • OCR accuracy depends on image resolution and font clarity; capped at a 3 s timeout to prevent UI blocking
  • Receipt Vault uses localStorage โ€” IndexedDB migration is planned
  • Nuul does not protect against reverse image search or determined OSINT adversaries with access to the visual content

๐˜™๐˜ฐ๐˜ข๐˜ฅ๐˜ฎ๐˜ข๐˜ฑ

  • Tone curve editor with draggable control points
  • HSL per-channel color adjustments
  • .cube LUT support for cinematic color grades
  • AI-assisted face and object blur
  • Full IndexedDB receipt vault with export and search
  • Plugin API for custom filter presets

๐˜“๐˜ช๐˜ค๐˜ฆ๐˜ฏ๐˜ด๐˜ฆ ๐˜ข๐˜ฏ๐˜ฅ ๐˜Š๐˜ฐ๐˜ฏ๐˜ต๐˜ข๐˜ค๐˜ต

Nuul is released under the MIT License. See the LICENSE file for details.

Author: Vanessa Madison Site: vanessamadison.com

For collaboration, preset sharing, or security review discussions, open an issue or reach out through the site.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors