Skip to content

lexluthor0304/NegativeConverter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

104 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Film Negative β†’ Positive Converter

Negative Converter sample output

Negative Converter helps film photographers turn scanned or camera-digitized film into clean, natural-looking positives with a workflow made for everyday editing. It brings frame cleanup, film-aware conversion, roll consistency, finishing controls, and export into one focused workspace.

Whether you are reviewing a fresh roll, restoring family negatives, or preparing a consistent set for sharing, the app is designed to make film conversion feel fast, approachable, and dependable while keeping your photos private.

🌟 Features

  • πŸ“· Supports PNG/JPG file uploads (including 16-bit PNGs via UPNG.js and .cr2, .nef, .arw, .dng, .raw, .rw2 raw files via LibRaw-Wasm)
  • πŸ”„ Rotation correction via slider or number input
  • βœ‚οΈ Visual cropping with drag-and-drop overlay
  • βš–οΈ One-click white balance by clicking a gray area in the image
  • πŸŽ›οΈ Live controls for:
    • Temperature & Tint
    • Vibrance & Saturation
    • Cyan / Magenta / Yellow (CMY) channels
  • 🎞️ Film presets for color negative, B&W negative, and positive slide stocks across Kodak / Fujifilm / Ilford
  • πŸ—‚οΈ Data-driven preset system loaded from negative2positive/presets/film_presets.json (supports alias fallback for older preset IDs)
  • πŸ” Optional lens profile workflow: search/select Lensfun profiles manually, or skip lens correction and continue
  • 🧷 Roll-level lens settings: lens correction on/off and parameters can be applied to selected files or reused via roll reference
  • πŸ›‘οΈ Privacy-friendly: all image processing happens locally in your browser
  • πŸ’Ύ Flexible export: PNG / JPEG / TIFF with selectable bit depth (8-bit, plus 16-bit for PNG/TIFF)

πŸš€ How to Use

Workflow (Step 1 β†’ 3)

  1. Step 1: Crop
    • Rotate / Auto Frame / Crop until only the film area remains
    • Click Next: Film Settings (negatives) or Next: Positive Mode (slides)
  2. Step 2: Film Settings
    • Pick film type: Color, B&W, or Positive
    • Color negatives: set the mask baseline (sample manually / auto-detect / roll reference)
    • Click Next: Convert and Continue
  3. Step 3: Adjust & Export
    • White balance + sliders + curves to taste
    • Export PNG / JPEG / TIFF

Film type quickstart

  • Color negative: Step 1 β†’ Next: Film Settings β†’ keep Color β†’ set mask β†’ Next: Convert and Continue β†’ Step 3
  • B&W negative: Step 1 β†’ Next: Film Settings β†’ select B&W β†’ Next: Convert and Continue (no mask) β†’ Step 3
  • Positive slide: Step 1 β†’ Next: Positive Mode (or select Positive in Step 2) β†’ Next: Convert and Continue β†’ Step 3

Batch workflow (multiple files)

  1. Click Add and choose multiple images (File List appears)
  2. Process one frame fully to Step 3
  3. Use Save Settings for the current frame, or Apply to Selected for roll-wide settings
  4. (Optional) Use Set Current as Reference + Apply Reference to Selected for roll reference
  5. Export via Export All (ZIP) or Download All Individually

Guided Mode

  • The Workflow panel includes a Guide toggle to show/hide in-app instructions (stored in localStorage)

βš™οΈ Technical Highlights

  • Uses UPNG.js to decode 16-bit PNGs
  • Uses a custom WebAssembly module based on LibRaw-Wasm to support .cr2, .nef, .arw, .dng, .raw, .rw2 formats
  • Uses UTIF.js + an in-app PNG encoder path to support TIFF export and 16-bit PNG/TIFF output options
  • Includes a simplified AHD demosaicing algorithm for Bayer-pattern raw data
  • Color adjustment logic is based on RGB ↔ HSL and RGB ↔ CMY conversions
  • Film preset metadata is loaded from JSON and grouped dynamically by film type in the UI
  • Optional lens correction uses @neoanaloglabkk/lensfun-wasm with npm local assets first + CDN fallback
  • Auto frame detection uses @techstark/opencv-js loaded dynamically from the npm package asset URL
  • Performance optimizations include:
    • Cached DOM access
    • Offscreen canvas reuse
    • Throttled rendering with requestAnimationFrame

Live Demo

Film Negative β†’ Positive Converter

πŸ–₯️ Desktop App (Tauri)

This repo includes a Tauri wrapper to package the web app as an offline desktop application for Windows / macOS / Linux.

Development

npm ci
npm run dev:web

Vercel deployment (important)

This app must be deployed from the Vite build output, not by serving source files directly.

Required settings:

  • Root Directory: repository root
  • Install Command: npm ci
  • Build Command: npm run build:web
  • Output Directory: dist

npm run build:web generates negative2positive/dist (for local/Tauri) and also syncs it to root dist (for Vercel output pickup).

If Vercel serves negative2positive/index.html directly, module imports like pako / utif / jszip will not resolve in browser and upload buttons can stop working.

Desktop dev (Tauri)

npm run tauri:dev

Build installers

npm run tauri:build

Build outputs are placed under:

  • src-tauri/target/release/bundle/

macOS installation troubleshooting

If macOS shows "Negative Converter is damaged and can't be opened", this is because the app is not yet notarized by Apple. Use one of these methods:

Method 1 β€” Terminal command (recommended):

xattr -cr /Applications/Negative\ Converter.app

Method 2 β€” Right-click open: Right-click (or Control-click) the app β†’ select Open β†’ click Open in the confirmation dialog.

Method 3 β€” System Settings: Go to System Settings β†’ Privacy & Security, scroll down and click Open Anyway next to the blocked app message.

Linux AppImage troubleshooting

  • Run AppImage directly, not with sudo.
  • The desktop app now applies AppImage-only runtime guards:
    • isolates GIO module loading to avoid host gvfs/GLib ABI mismatches
    • standard AppImage keeps DMABUF when render nodes are usable, and auto-falls back when not
    • compatibility AppImage (*_legacy-glibc235.AppImage) defaults DMABUF off for startup stability
  • Optional override for DMABUF behavior:
    • force enable: NEGATIVE_CONVERTER_DMABUF=on ./Negative\ Converter*.AppImage
    • force disable: NEGATIVE_CONVERTER_DMABUF=off ./Negative\ Converter*.AppImage
  • If startup still fails on older distros, use the compatibility AppImage (*_legacy-glibc235.AppImage).

Release (GitHub Actions)

  1. Update versions:
    • src-tauri/tauri.conf.json
    • src-tauri/Cargo.toml
  2. Merge to main
  3. GitHub Actions automatically:
    • creates a vX.Y.Z tag
    • publishes a GitHub Release with the installers
    • (optional) syncs installers to Cloudflare R2 under negative-converter/release/vX.Y.Z/

Cloudflare R2 sync (optional)

If you want the release workflow to upload installers to R2, add one of these GitHub Actions secret sets:

Option A: R2 S3 API token

  • R2_ACCESS_KEY_ID
  • R2_SECRET_ACCESS_KEY
  • R2_BUCKET
  • R2_ENDPOINT (e.g. https://<accountid>.r2.cloudflarestorage.com/)

Option B: Cloudflare API token (no S3 keys)

  • CLOUDFLARE_API_TOKEN
  • CLOUDFLARE_ACCOUNT_ID
  • R2_BUCKET

πŸ’‘ Development & Contributions

Feel free to fork, open issues, or submit pull requests with ideas or improvements.
This tool is designed to be simple, fast, and modifiable.

πŸ“„ License

MIT License

πŸ™ Acknowledgments

Special thanks to LibRaw-Wasm by ybouane,
which made it possible to support various raw image formats such as .cr2, .nef, .arw, .dng, .raw, and .rw2 directly in the browser via WebAssembly.
Your work was an essential reference and greatly accelerated development.