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.
- π· 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)
- Step 1: Crop
- Rotate / Auto Frame / Crop until only the film area remains
- Click Next: Film Settings (negatives) or Next: Positive Mode (slides)
- 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
- Step 3: Adjust & Export
- White balance + sliders + curves to taste
- Export PNG / JPEG / TIFF
- 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
- Click Add and choose multiple images (File List appears)
- Process one frame fully to Step 3
- Use Save Settings for the current frame, or Apply to Selected for roll-wide settings
- (Optional) Use Set Current as Reference + Apply Reference to Selected for roll reference
- Export via Export All (ZIP) or Download All Individually
- The Workflow panel includes a Guide toggle to show/hide in-app instructions (stored in localStorage)
- Uses
UPNG.jsto decode 16-bit PNGs - Uses a custom WebAssembly module based on
LibRaw-Wasmto support.cr2,.nef,.arw,.dng,.raw,.rw2formats - 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-wasmwith npm local assets first + CDN fallback - Auto frame detection uses
@techstark/opencv-jsloaded dynamically from the npm package asset URL - Performance optimizations include:
- Cached DOM access
- Offscreen canvas reuse
- Throttled rendering with
requestAnimationFrame
Film Negative β Positive Converter
This repo includes a Tauri wrapper to package the web app as an offline desktop application for Windows / macOS / Linux.
npm ci
npm run dev:webThis 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.
npm run tauri:devnpm run tauri:buildBuild outputs are placed under:
src-tauri/target/release/bundle/
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.appMethod 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.
- 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
- isolates GIO module loading to avoid host
- Optional override for DMABUF behavior:
- force enable:
NEGATIVE_CONVERTER_DMABUF=on ./Negative\ Converter*.AppImage - force disable:
NEGATIVE_CONVERTER_DMABUF=off ./Negative\ Converter*.AppImage
- force enable:
- If startup still fails on older distros, use the compatibility AppImage (
*_legacy-glibc235.AppImage).
- Update versions:
src-tauri/tauri.conf.jsonsrc-tauri/Cargo.toml
- Merge to
main - GitHub Actions automatically:
- creates a
vX.Y.Ztag - publishes a GitHub Release with the installers
- (optional) syncs installers to Cloudflare R2 under
negative-converter/release/vX.Y.Z/
- creates a
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_IDR2_SECRET_ACCESS_KEYR2_BUCKETR2_ENDPOINT(e.g.https://<accountid>.r2.cloudflarestorage.com/)
Option B: Cloudflare API token (no S3 keys)
CLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_IDR2_BUCKET
Feel free to fork, open issues, or submit pull requests with ideas or improvements.
This tool is designed to be simple, fast, and modifiable.
MIT License
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.
