Skip to content

MohaElder/openenlarge

Repository files navigation

OpenEnlarge icon

OpenEnlarge

Develop your film negatives with real physics — not a flipped tone curve.

License: MIT Release Platforms CI

Download · Website · How it works

OpenEnlarge

What is OpenEnlarge?

OpenEnlarge is an open-source desktop darkroom for color film negatives. It inverts and develops scans of negatives into finished positives — the job a darkroom enlarger does for optical prints.

Most tools treat a negative scan as a generic image and fit per-channel tone curves to flip it. OpenEnlarge instead works in the density domain, using a Beer-Lambert model of how dye layers absorb light. Density is linear in dye concentration; transmittance is not — which is exactly why a naive invert-and-flip looks wrong. Working in density first, then applying creative finishing on top, yields cleaner, more faithful color.

Every image is developed with a density-domain matrix inversion grounded in the Beer-Lambert model — a physically-based engine, not a per-channel tone curve.

Negative → Positive

Negative (scan) Developed (OpenEnlarge)
before after

Features

  • Density-domain inversion — physically-based Beer-Lambert engine, not a flipped curve
  • Decodes RAW, TIFF, JPEG & PNG — Fuji RAF, Panasonic RW2, Nikon NEF, Sony ARW, Canon CR3, Hasselblad 3FR and DNG, plus 16-bit TIFF, JPEG and PNG → linear RGB
  • Tethered shooting — watch a folder and auto-import + develop new scans as they land, so finished positives appear as you shoot ("shoot & see")
  • Per-roll base calibration — sample the orange film base once per roll and apply it
  • Full develop controls — tonal curve, color grading, color wheels, exposure/black/gamma
  • Crop, rotate, straighten, flip with a live viewport and histogram
  • Batch export to 16-bit TIFF / PNG / JPEG — with an optional batch crop applied across the whole selection in one pass
  • In-app updates — checks on launch or on demand from Settings and installs the new version in place
  • Headless CLI (film-cli) for scripting and batch inversion
  • Cross-platform — macOS, Windows, Linux, built on Tauri

Architecture

Component Path Responsibility
film-core crates/film-core Pure Rust engine — decode, density-domain inversion, calibration, export. No UI deps.
film-cli crates/film-cli Headless CLI over film-core for batch/scripted inversion.
App shell app/ Tauri 2 + SvelteKit UI wrapping film-core.

Download

Grab the latest installer for your OS from the Releases page:

  • macOS.dmg (Apple Silicon)
  • Windows.msi or .exe
  • Linux.AppImage or .deb

Build from source

Prerequisites: Rust (stable), Node.js ≥ 18, and the Tauri prerequisites for your OS (on Linux: libwebkit2gtk-4.1-dev, libgtk-3-dev, librsvg2-dev, libappindicator3-dev, patchelf).

# Run the desktop app in dev mode
cd app
npm install
npm run tauri dev

# Build a release installer for your OS
npm run tauri build

CLI usage

The engine also runs headless. From the repo root:

# Invert a scan with the density-domain engine → 16-bit TIFF
cargo run -p film-cli -- input.tiff -o output.tiff

# Sample the film base from a rect (x,y,w,h) and pick a stock profile
cargo run -p film-cli -- input.tiff -o out.tiff --stock portra400 --base-rect 0,0,128,128

Run cargo run -p film-cli -- --help for all options.

How it works

A developed color negative is three stacked dye layers (Cyan, Magenta, Yellow) over an orange base. A scan is the forward model:

I_i = ∫ L(λ) · S_i(λ) · 10^(−D(λ)) dλ          (spectral integration)
D(λ) = D_min(λ) + Σ_j C_j · D_j(λ)              (Beer-Lambert: density linear in dye conc.)

OpenEnlarge's default engine inverts this in the density domain:

Ĉ = M_post · log₁₀(M_pre · I₀ / I)

It recovers dye concentrations with a cross-channel matrix instead of flipping each channel independently — the difference that makes color come out right. The deep version lives in docs/superpowers/specs/2026-06-03-film-inversion-poc-design.md.

Contributing

Issues and pull requests are welcome. Before opening a PR, run the same checks CI does:

cargo fmt --all --check
cargo clippy --workspace --all-targets
cargo test --workspace
cd app && npm run check && npm run test:unit

License

MIT © 2026 mohaelder

About

Develop your film negatives with real physics — a density-domain inverter.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors