What this page is. Every release note older than the six on the changelog, in the form it was published in. The oldest ones are in French and have not been retranslated. Each version here is still downloadable and still ships its own source, so an archived note is a record rather than a dead end.

These are the notes for versions before 0.1.1. The most recent 6 are on the changelog, which is also where the feed lives. Every version listed here is still downloadable from the release pages, and the source of each one is attached to it.

The 11 oldest notes are in French and have not been retranslated. They were written before the project's notes moved to English, and rewriting them now would put words into a version that shipped without them.

0.1.0

v0.1.0 on GitHub

Nothing in this version changes the rendering of a photo you have already adjusted. What changes is what an exported file can contain: a new HDR switch adds a gain map to a JPEG, and each frame now carries the EXIF you set for it. Both are off or empty until you touch them.

Open Negative Initiative leaves alpha

This is 0.1.0, the first beta. The orange "ONI is in Alpha" notice is gone from the card: the major defects reported through it have been fixed, and the export — the one part that still had to be reworked before saying so — has been rebuilt from the ground up. Bugs are still to be expected; please keep reporting them.

Export is a screen of its own

Export no longer opens a dialog. The photos about to be written take the whole card, one per row, and their settings sit in a panel on the right. Cancel and EXPORT are at the bottom right; Escape leaves, ⌘E confirms; a batch can be stopped between two files. The mode you came from — gallery or editor — is where you return.

Three presets — Instagram, Display, Print — set every row at once and lock none of them. The size is typed as width × height, linked by the frame's shape: the edge you type is the one the ceiling rides on every photo of the batch, so Instagram's "1440 px on the short edge" lands a landscape and a portrait alike. Exports never enlarge. Your export settings are remembered between launches; the default is now JPEG in sRGB, the space every reader — Instagram included — agrees on.

The HDR hack

A checkbox on JPEG exports, on in the Instagram preset. It changes no pixel: the file gets a flat gain map, so an HDR screen — a recent phone, an XDR Mac — shows the picture up to two stops above the page's white, while every other reader sees exactly the same picture as without it. It is written in the form Instagram, Threads and X keep through their re-encode: post the file uncropped, from a desktop browser at original ratio. Whether Instagram accepts a given file is Instagram's to decide, and not published; the file matches what is known to pass.

EXIF, frame by frame

Beside each preview sits the frame's own line: its file name, camera, lens, film (picked from the catalogue), ISO, date, artist and copyright. It opens on the scanner's own EXIF — the body, ISO and date of the camera or scanner you digitised with — so you can see these are not the photograph's, and replace them. What you type is kept with the frame for the next export. Nothing of the scanner is written unless you leave it there. The naming pattern is gone: the name is a field of the frame's line.

Also

  • A roll's film stock can no longer be unpicked by clicking the active one again: a roll was shot on some film.
  • Export previews render at 1600 px instead of 500, so the picture can be judged.

0.0.36

v0.0.36 on GitHub

Nothing in this version changes the rendering of a photo you have already adjusted. It is about the Projects tab, and about a few things that used to get stuck.

Projects: selection finally knows which project you meant

A photo that belongs to two projects shows up once under each. Clicking it used to light up every copy, in the grid and in the sidebar, and ⌘A then completed every project it belonged to. Now only the copy you clicked lights up, ⌘A completes the project you clicked in — or the one you selected in the sidebar — and a photo stays with the project it was picked in even as you ⌘-click your way into another one. Opening a photo from a project's grid opens it in that project, and clicking the same photo again under a different project unfolds that project in the sidebar.

Projects can be deleted

Delete Project sits in a project's context menu in the sidebar, behind a confirmation that says what stays untouched: the photos, their settings, and every other project or roll they belong to. Removing a project's last photo deletes the project on its own, and a box emptied of its last roll or project is pruned the same way.

Also

  • Thumbnails no longer get stuck loading after a big import: a tile still on screen retries on its own instead of waiting for a quit and reopen.
  • Clicking in the sidebar is faster with many rolls expanded: rows are no longer recut on every click, and the scroll that reveals a ringed row waits for the click to paint first.
  • The Library tab is now called Archive.
  • The project card's cover stack shows the project's real thumbnails instead of blank plates.
  • Reading a photo's settings no longer touches the original file unless the sidecar needs migrating.

0.0.35

v0.0.35 on GitHub

Camera RAW decoding is more accurate now, and existing photos are affected too. Every RAW you've already edited has had its balance nudged automatically to compensate, so most will look the same or very close to before. If one still looks a touch off, a press of your usual balance button settles it.

Truer colour, especially on unusual film

The old decode assumed every colour negative sat behind an orange base and quietly corrected for it. Films that don't, Harman Phoenix among them, were fighting that assumption instead of benefiting from it. RAWs now decode neutrally for every file, and the app's own colour tools do the actual correcting, exactly as they always did.

  • Scanner files (TIFF, JPEG, PNG, HEIC, linear DNG) were never affected by this.
  • A preset carrying Source balance may need a quick re-save if it lands wrong on a RAW now.

Also

  • A project's filmstrip now follows the same order as its grid, instead of silently regrouping a hand-picked, cross-roll order by roll.

0.0.34

v0.0.34 on GitHub

The photo grid and the sidebar tree both got a serious speed pass this release. A library with hundreds of photos, dense grid columns, and every roll expanded in the sidebar used to slow scrolling and hovering to a crawl — both now stay smooth by only drawing what is actually on screen, instead of holding everything live at once.

Drag to reorder actually reorders now

Dragging a photo to a new position inside a scanned roll could silently do nothing, because scanned frames often share one capture time for the whole roll and the app had no other number to place them by. It now finds a real gap to drop into regardless, and undoing or redoing a reorder correctly updates the grid instead of leaving it showing the old order.

Projects get a real info panel

Selecting a project's photo used to leave the right panel showing nothing useful, or the wrong roll's details. It now shows the project itself: its name, a stacked preview of its first photo, every film stock among its photos, and an editable description. Anchoring on one of its photos from the grid also opens the right project in the sidebar now, even when it was collapsed.

Also

  • Add to Project and Remove from Project, from the grid's own context menu, for a single photo or a whole selection.
  • New icons for a project group and a project box.
  • The info panel's own separators now reach the full width of the panel instead of stopping short of its edge.

0.0.33

v0.0.33 on GitHub

Drag and drop got a proper pass this release: move a photo into any roll or day by dropping it directly there, including between two photos that belong to a different group — that used to fail silently. Import works the same way now too: drag files or a whole folder from Finder anywhere in the library window, no need to open the file picker.

A hand tool for the correction brush

Painting dust and scratches used to leave no way to pan a zoomed-in photo — the brush took every drag. Hold Space to pan instead, exactly like Photoshop's hand tool: an open hand while held, a closed hand while dragging, and the brush's own preview circle steps aside so it never looks like it is about to paint.

The preset deck stopped grabbing the bottom edge

It used to rise from far away and leave an invisible strip above its own peek that intercepted clicks — corrections and crop handles along the bottom of a zoomed-in photo could miss for no visible reason. It now answers only where it is actually shown.

Also

  • A persistent status bar under the photo: name and pixel dimensions, or how many photos are selected, with three medal buttons that label the current photo or the whole selection at once.
  • The mirror buttons in Geometry now survive leaving the photo — they used to silently reset.
  • A new app icon.

0.0.32

v0.0.32 on GitHub

If you use Contrast and Colour (Temperature/Tint) together, their combined look shifts slightly in this version — Contrast now applies on its own, ahead of Colour, instead of being baked into the same table. Using only one of the two is unaffected.

Dust and scratch removal is rebuilt from the ground up: a classical, GPU-based search finds a matching patch elsewhere in the same photo — no neural network, nothing decided without a brush stroke. A manual curves editor also returns to the Color pane, five channels, drawn by hand.

  • Fixed: right-clicking a thumbnail in the gallery only opened its menu over the right half of the image.
  • Fixed: thumbnails reloaded on every launch instead of once.

0.0.31

v0.0.31 on GitHub

Nothing changes in how an already-adjusted photograph renders — exports and saved edits are pixel-identical to 0.0.30. This version gives a roll its own identity, adds seven film stocks, and fixes several selection, hover and comparison issues.

A roll can now carry its own film stock, shot date and camera body, editable from the info panel — the shot date is three plain fields (month, day, year) instead of a calendar picker, sized to match the toolbar's own buttons. Rolls and boxes can be reordered by dragging them, at the root or nested inside another box. An unassigned day is promoted to a real roll the moment it is imported, rather than staying a stand-in category.

Seven new film stocks: AgfaPhoto APX, Foma Fomapan 200, Fomapan 400, Fomapan 401, Lucky Color 200, Mira Color 400 and Mira Color 800.

  • Fixed: selecting a roll or a box in the sidebar sometimes stayed marked after the ring moved to a photo selected elsewhere, showing two things selected at once.
  • Fixed: hovering the gallery grid or the sidebar tree could land on the wrong row, or flicker, depending on exactly how the pointer crossed between cells.
  • A box's rolls now show inside the gallery grid too, not only in the sidebar — as a larger title, never a separate background or an expand arrow, so it reads as a bigger heading rather than a different kind of thing.
  • Fixed: the Before/After comparison measured against the bare, un-corrected negative — after dialling in white balance and levels, that comparison had nothing useful left to show. It now compares from the corrected image instead, and respects the frame's own negative/positive/monochrome mode, which it previously ignored.

0.0.30

v0.0.30 on GitHub

Nothing changes in how an already-adjusted photograph renders — exports and saved edits are pixel-identical to 0.0.29. This version refines the sidebar that shipped last time.

A small arrow now leads every roll, day and box row in the sidebar, separate from the rest of the row: click it to expand or collapse, click anywhere else to select — the two no longer do both at once. It sits flush against the sidebar's own left edge, no wasted margin before it.

  • "Ungroup" now takes the whole selection at once, however mixed: a roll among the selected rows falls back to its own capture day, a box among them dissolves back into whatever held it — one command instead of picking through each row's own menu.
  • Fixed: "All Photos" drew a faint line underneath it that no other row had.

0.0.29

v0.0.29 on GitHub

Nothing changes in how an already-adjusted photograph renders — exports and saved edits are pixel-identical to 0.0.28. This version is entirely new ground: organizing and finding photographs, not converting them.

A sidebar next to the gallery lists every roll and every capture day as its own row, mirroring exactly what the gallery already headers itself with. Click a row to see only that group; click "All Photos" to see everything again. Double-click any name — a roll or a bare day, in the sidebar or in the gallery's own heading — to rename it on the spot.

Rolls can now nest inside boxes — virtual containers, never real folders on disk, purely a way to arrange a growing library. Select several rolls or days and group them into a box from the sidebar's own menu, or drag one roll onto another to form one automatically. Drag a roll onto a box to move it in; drag a box onto another to nest it. "Unbox" dissolves a box back into whatever held it, keeping every roll inside it exactly where it was, one level up.

  • A medal filter sits in the top bar in gallery mode, in the mode tab's own slot: one button cycling ALL → BRONZE → SILVER → GOLD → ALL, showing exactly that tier and no other.
  • Negative/Positive, the framing button and the compare button no longer sit greyed out in gallery mode — they only ever did anything on an open photograph, so they are hidden there instead.
  • Fixed: double-clicking a roll's name in the gallery to rename it usually did nothing, because the header under the pointer during a scroll was a decorative copy with no gesture of its own.
  • Fixed: the sidebar's "All Photos" row drew a faint border no other row had; it was the only one built as a real button rather than a plain click target.

0.0.28

v0.0.28 on GitHub

Exports and saved edits render exactly as before. One thing changes on screen: while a slider is held past 100 % zoom, the canvas shows the stretched preview — soft during the gesture, sharp again the moment you release. That trade closes a serious latent leak.

The leak, first. Holding a Colour slider past 100 % zoom made the app file full-resolution working buffers on every notch and never re-use them — gigabytes within seconds, however much memory the machine has, and the less it has the sooner everything stops answering. Every version to date carried it. The preview-under-the-hand rule above removes the mechanism entirely; a twenty-second worst-case drag now holds flat where it used to take the whole machine.

Editing feel, measured on a real hand rather than a script. Recorded before and after over the same erratic session: seventeen freezes over 200 ms in fifteen seconds became one in a whole session; the worst five percent of frames went from 6 to 35 per second.

  • Adjusting by trackpad scroll now counts as a held hand, as dragging always did — so the heavy work every control defers during a drag is deferred for the scroll too. This was the single biggest cause of stutter, and it had been there since the scroll gesture shipped.
  • The histograms follow the hand at about sixty refreshes a second, and a finished measurement is never thrown away again — the plot used to only catch up after the gesture ended.
  • Full-resolution drags stopped rebuilding their cache on every notch; the region is rebuilt once, when you release.
  • Nothing a drag starts can outlive it and pile up any more — one measurement in flight at a time, the newest settings win.

Also in this version:

  • Applying a crop happens in two beats — the cropped frame renders first, the panel travels next — so slower machines no longer take both on one breath.
  • The Straighten slider now works from the frame the gesture started on: sweeping out and back returns exactly where you began, instead of ratcheting the crop tighter.
  • Launch no longer re-reads the whole library's EXIF twice per photograph; a large library opens without the disk churn.
  • While a photograph's first measurement is still running, the auto-balance plate says so instead of sitting grey unexplained.
  • Walking a roll with the arrow keys no longer builds full-resolution frames for photographs you have already left — on scan libraries this reached nineteen gigabytes.
  • The diagnostics console now stamps every watchdog line with the app's memory footprint, so a report tells the whole story on its own.

0.0.27

v0.0.27 on GitHub

Nothing here changes how a photograph is rendered or exported. This version only adds to what the app can say about itself when it stops responding.

Three explanations for the crop freeze have now been ruled out by measurement rather than argument: memory, a file on a network volume, and the canvas cache — squeezed to a quarter of what the affected machine has, it still behaved. The log from 0.0.26 dates a block but does not name what is inside it, and that is what this version adds.

  • Past one second of being unresponsive, the app now reads its own main thread and writes the function it is stuck in, with the frames above it. This is what a system spindump does; it runs from a thread that is not the blocked one, so the reading survives a forced quit.
  • One reading per block, six per session, forty frames deep. A log nobody can read answers nothing.

Same routine as before, and it is the last thing needed: Help → Show Console before reproducing, work until it happens, then Copy — or send the file the window names. What matters this time is the block of lines beginning stack ·.

0.0.26

v0.0.26 on GitHub

Nothing here changes how a photograph is rendered or exported. Frames you have already adjusted open and export exactly as they did, and no cached thumbnail is thrown away.

This version exists to find one fault rather than to fix it. Cropping a photograph, clicking to apply, and having the app stop responding has been reported twice and reproduced neither time — so the app now records what it was doing when it stops.

  • Help → Show Console. Open it BEFORE reproducing the fault, work until it happens, then press Copy. The window is frozen while the app is, so it also names a file: everything it holds is written to disk as it happens, and a forced quit keeps all of it.
  • A watchdog on its own thread records how long the app went unresponsive and what it was drawing at the time. It writes that line during the block, not afterwards.
  • The log also states which volume a photograph lives on, and whether a file provider owns it — a file that looks local in the Finder is not always a file the disk can hand over quickly.

Two real faults on that same path are fixed on the way: applying a crop no longer reads the disk on the thread that draws, and the crop gesture now defers the work that follows it the way every slider already did.

If it happens again, two things settle it: the console log, and the report macOS writes by itself in Console under Crash Reports, named for this app and ending in .hang.

0.0.25

v0.0.25 on GitHub

Nothing here changes how a photograph is exported. Frames you have already adjusted come out of the export bit for bit as they did. What can change is what the canvas shows while you work: on a Mac with less memory, the reduced preview is now held at zoom levels where the app used to decode real pixels. That is sharpness on screen, never in the file.

Three faults reported by testers turned out to be one. Past a certain zoom, or a crop tighter than about two thirds of a side, the canvas switches to the full-resolution frame — and that path asked the GPU for as much on a 16 GB laptop as on a 96 GB desktop, never handed the memory back, and said nothing at all when an allocation was refused.

  • The canvas now reads what the machine can actually give and caps itself at 4 GB. A large Mac is handed what it was already using, so nothing there renders differently. A smaller one is handed less, which is the whole point.
  • A refused frame falls back to the preview instead of going black. One tester saw photographs simply vanish above roughly half zoom; that was an allocation being refused and the refusal being swallowed.
  • Memory is handed back when the system says it is short, rather than held until the app quits.
  • Exporting no longer needs a restart to work again. A render refused for want of memory is retried once with the session caches dropped — which is exactly what relaunching the app used to do by hand.

If a crop or a deep zoom still stalls on your machine, the app can now say what it was drawing when it happened: run it with --fps, work until it stutters, quit, and send /tmp/oni-fps.txt. It names the source and the crop, which earlier reports could not.

0.0.24

v0.0.24 on GitHub

Nothing here changes how a photograph is rendered. Frames you have already adjusted open exactly as they did.

Installs are now counted. On the first launch after an update, the app sends its version number and nothing else; what is kept is that number and the country the request came from. No identifier, no address, nothing that singles anyone out.

0.0.23

v0.0.23 on GitHub

This version changes the rendering of photographs you have already adjusted in Positive mode. A slide or a scan set to Positive opened almost white until now, and it opens on a picture instead. If you graded a positive around that paleness, your handles are where you left them but the image under them has moved. Negative and B&W do not move at all — that was measured on a full-resolution negative, before and after, and every figure came back identical. Version 0.0.22 stays in the feed if you would rather not take this one.

Positive mode was leaving a logarithm in the picture

Converting a negative works in density, which means taking a logarithm. Positive mode gave up the inversion that follows it and kept the logarithm — correctly, because that is what keeps the sliders linear in stops. What it never did was undo that logarithm before showing you the result.

  • The symptom was a frame with no blacks in it. On a 16-bit scan of a slide, the median pixel came out at 224 / 227 / 246 of display code where the file itself holds 113 / 122 / 198, and the darkest thousandth of the picture opened in mid-grey, at 159 instead of 10. Over the brightest decade the app was rendering a quarter of the contrast the file carried.
  • No automatic button could fix it. The balance keys write the three per-channel levels and never the RGB set, which is the one carrying the gamma — so pressing Classic left the medians at 219 / 221 / 241, still white.

What a positive now rests on

  • The three black points start on the film base rather than under it. A physical black opens on black instead of on a grey veil, and nothing is clipped to get there.
  • The RGB midtone slider rests at 0.670 instead of 0.500. That is what undoes the encoding. The same scan now opens on 0.463 / 0.503 / 0.809 where it used to open on 0.747 / 0.769 / 0.919.
  • The rest stops short of matching the file exactly, on purpose. Landing on it takes the midtone handle to the very end of its travel, leaving nothing to add contrast with. A positive is meant to open on a usable starting point, not on a finished picture.

What happens to a positive you already adjusted

Its settings file is read onto the new resting point, so the fix reaches frames already in your library and not only the next import. A handle you actually placed is left exactly where you put it: only the ones still sitting untouched are moved. A negative or a B&W frame is read through without a single key being altered.

Also in this version

  • The film probe used for diagnostics now reports what the whole chain makes of a frame in Positive as well as in Negative, and reports quantiles alongside the extremes.

0.0.22

v0.0.22 on GitHub

Nothing in this version changes the rendering of a photo you have already adjusted. No control moved and no default changed. The only difference is what ships alongside the application.

The source code now ships with every release

  • Every release from this one on carries its own source archive, named OpenNegative-<version>-source.zip. It holds the Swift sources, the resources, the Metal kernels, the package manifest and the Makefile: what you need to build this exact version, and nothing from my own machine.
  • Do not confuse it with the "Source code (zip)" GitHub attaches on its own. That one is an archive of the release repository, which holds binaries and an update feed, and it contains no Swift at all. The file to take is the one with -source in its name.
  • One piece is still missing, and it was missing before too. The keycap button component lives in a separate private repository, so the archive does not compile as it stands: replace it with a plain button first. That is about an hour of work, and it is spelled out on the site.

The licence has not changed, and neither has what it gives you. GPL-3.0 asks for the source that corresponds to the binary you were handed; until now that source existed but you had to ask me for it, which is not the same thing.

0.0.21

v0.0.21 on GitHub

Nothing in this version changes the rendering of a photo you have already adjusted. One fix, on the thumbnail cache alone. Your settings, your rolls and your presets are untouched.

Thumbnails survive an unplugged drive

  • Launching with the originals' drive disconnected no longer empties the thumbnail cache. The tidying pass built its list of claimed files by reading each original, and silently dropped every one it could not reach — so a whole library on an external drive was read as files nobody wanted, and deleted. Measured on a 632-frame library: the 592 frames on the external drive lost their thumbnails, the 40 on the internal drive kept theirs.
  • An unreachable original now keeps its files, claimed through the fingerprint the index holds, which survives the drive going away. And an index that does not yet account for every member of the library deletes nothing at all: without knowing who is a member, no file can be called an orphan.
  • The tidying itself still works. A superseded thumbnail of an original that really was read is still removed, and so are the files of a photo taken out of the library. The fix is a rule about proof, not the housekeeping switched off.

Version 0.0.20 announced that thumbnails had stopped being thrown away. That was half true: the rebuild stopped inventing fingerprints, but the pass that sweeps unclaimed cache files kept reading an unreachable original as an unwanted one. This is the other half.

0.0.20

v0.0.20 on GitHub

Nothing in this version changes the rendering of a photo you have already adjusted. Contact sheets are new, so nothing existed to move; every other change is interface, speed or reliability.

Contact sheets

  • Make a contact sheet from a selection. Frames are laid six to a row in one scene-linear TIFF, which is imported like any other photograph — so one balance, one set of curves and one export cover the whole roll.
  • The automatic balance reads inside the frames only. A window is placed on each cell, well inside its edges, so the gaps and the film rebate never enter the measurement — which is what used to drag the whole sheet off colour.
  • The gaps export black. A sheet reads as a sheet instead of arriving on a white page.
  • Every step of the grind is named. A forty-frame sheet used to sit for ten seconds on a full progress bar with nothing to say — writing, adding and opening now each announce themselves.

Rolls

  • Group frames into a roll with ⌘G, and name it. The gallery cuts on rolls instead of capture days, so a film shot over a week stops being scattered across it.
  • Membership lives in the sidecar. Deleting the index or moving to another machine cannot lose a roll, and a paste can never move a photograph out of one.

Presets

  • Five film presets ship with the app — Azure, Cinestill 800T, Gold 200, Portra 400, Redscale. They are laid down once, then they are yours: rename them, retune them, or throw them away and they stay away.

Framing

  • A horizontal and a vertical mirror. Both act on the frame as you see it, so the axis means what it says whatever quarter turns are applied.
  • Rotation turns clockwise, which is the direction the button was already claiming.
  • An Apply button under the panel's reset. Committing a crop no longer means finding the gesture elsewhere.

The bar at the top

  • Three groups instead of one row. Framing and comparison, then editing, then the two that commit — read left to right rather than scanned.
  • Delete is red, reset is amber, new contact sheet is orange. A deliberate choice: the gestures you cannot casually undo are the ones that carry a colour.

Speed and reliability

  • The library is read off the main thread. This version rebuilds its index at first launch, which costs one reading of each original; it now happens behind a progress sheet instead of an empty window.
  • Each original is read once, not twice. The fingerprint and the capture date both wanted the same metadata and each asked for it — 12.93 ms per photograph instead of 25.19.
  • A disconnected disk no longer poisons the library. What cannot be read is skipped rather than recorded with invented dates, and plugging the drive back in repairs it with no gesture. Thumbnails stop being thrown away, and frames stop piling onto one impossible day.
  • The gallery draws only what is on screen. Landing on the six-hundredth frame of an import used to build six hundred cells; the count is now what a window holds.
  • Column count replaces tile size, six or twelve, and ⌘− / ⌘+ walk it.

Smaller things

  • Conversion mode applies to a whole selection, as does reset.
  • Clicking a tab slides the panel instead of cutting to it, so the swipe the panel already supported is discoverable without being told about it.

0.0.19

v0.0.19 on GitHub

Your photographs render exactly as before. The new stage added here is skipped, bit for bit, until you move one of its points, so a conversion already made comes back identical. Nothing in this version needs a migration and nothing is lost by going back.

One habit does change on the first click. A photograph you have already worked on now opens on the Color panel instead of the framing view. A scan you have never touched still opens on framing, which is what a fresh negative wants.

Spectrogram finishing: the colour mixer, as a wheel

Photoshop and Lightroom put a colour mixer behind rows of sliders, where reading which colour you are steering means reading a label. It is the same control here, drawn as the thing it describes: eight points on a wheel, one per colour range, at the top of the Grade panel.

Each point carries three things. Its angle is that colour's hue, its distance from the centre its saturation, and a second point riding the same spoke its luminance. Click a point, then use the arrow keys — left and right turn the hue, up and down steer whichever point you are holding. Tab walks the wheel clockwise, Option-Tab the other way. Four keys under the graph do the same for a hand that would rather not leave the mouse, and a slider sets how far one press moves.

Double-clicking a point puts its own axes back to rest, and only its own: clearing a hue does not quietly undo the luminance the other point on that spoke is carrying.

What the wheel promises is that a band reaches its own colours and no others. Pushing red to the end of its travel leaves a blue pixel where it was, exactly — not nearly. The luminance point is a gain in stops, so a colour can be taken four times down without ever reaching black, and the hue and the saturation ratio come through it untouched.

Lighting, on black and white

The block offered two tabs, RGB and Luma. On a monochrome render that distinction has already collapsed — everything becomes one channel at the end of the chain — so the two were the same control under two different laws, one of them labelled RGB on a grey image, and their effects added up. Monochrome now offers the one set, and the tab bar goes away with the choice.

The Exposure handle also sat two thirds along its track while reading its resting value, on black and white only, which reads as already pushed. It rests at mid-travel now, in every mode. Only the handle moved: the stored value and the rendering did not.

Framing has a button

Cropping and straightening was a panel among others. It is now a key at the head of the toolbar, and while you are framing that key turns green and carries a check: press it and you are back where you were working, or on Color if you had not been anywhere yet. ⌘K does the same, and the gesture is in the View menu like every other one the bar carries.

The zoom follows you between photographs

Opening the next frame refitted the view, so walking a roll at 100 % to compare grain from one frame to the next was not possible. The zoom is now kept, and so is the fitted state if that is what you were on; only the pan is dropped, since it describes a picture you have left. The blurred stand-in shown while the next frame decodes is drawn at that same zoom instead of flashing back to fitted.

What it runs on

macOS 13 Ventura or newer, on Apple Silicon or Intel — one universal build for both.

0.0.18

v0.0.18 on GitHub

One thing moves the rendering of photographs you have already adjusted, and it is named first. Sharpening and chroma denoise are now dosed in pixels of the full-resolution scan instead of a fraction of its width. On a scan whose long side is 8368 px — what the old fraction was calibrated on — nothing moves at all. On any other size it does: measured on scans of 6000 to 6240 px, the dose lands 34 to 39 % higher than before. Everything else comes back identical.

Two consequences worth knowing before you install. Every thumbnail is rebuilt once, on the first import or launch, behind a progress step — after that they are never rebuilt again. And a photograph adjusted in this version opens read-only in 0.0.17 and below: going back loses nothing, but the older version will not let you move a handle. Every published version stays in the feed, so going back remains possible.

The gallery no longer freezes

Dragging the scroll bar froze the app for two to five seconds, and macOS reported it as not responding. The cause was the container itself: it filled contiguously, so reaching frame 600 built all 600 on the way, and it paid that cost again on every switch between the grid and the editor.

The grid is now computed rather than measured — every tile's rectangle and the document's height are arithmetic, and only the cells the visible band touches are drawn. Landing on the last frame of a 600-photograph day builds 78 cells instead of 600. The scroll bar can be dragged end to end, and the scroll bar itself is honest, because the height is computed rather than estimated.

Thumbnails are written once

They were regenerated when the app opened, when the gallery reopened, and when the window was resized. They are now written once, at a fixed size, kept in the app's own files, and rebuilt only after you edit the photograph. Deleting a photograph deletes its thumbnails with it.

What you see: a step with a progress bar the first time, then a gallery that opens immediately and stays that way.

The gallery groups by capture date

It grouped by import date, so re-importing or re-scanning part of a roll split it across two headings. It now reads the capture date from the file. A roll stays together whatever evening you scanned it.

Six columns, or twelve

A strip of film is cut into six frames, so a row is a strip; twelve is that strip doubled, for sorting a roll rather than judging one frame. ⌘− and ⌘+ walk between the two, and the change is crossed with a fade instead of photographs sliding to new places.

Both are fully responsive: the count is what you choose, and the tile is what takes the room. On a wide screen the tiles are large. There is no width at which the grid rearranges itself behind your back.

The conversion works in density

Inverting a negative by 1 − x on linear data crushes everything into the first few percent of the range, which is why the handles felt unusable at one end and violent at the other. The whole chain now works in density, where a stop is a stop.

What changes for you: the three per-channel level windows read a density with three decimals instead of a percentage, and their track runs from −0.35 to 3. A black point can be placed under zero, which is where a channel transmitting past 1 actually lands — previously unreachable. Your existing adjustments were converted exactly; the arithmetic is an identity, and it is verified pixel for pixel against the frozen previous version.

A new control: Density ceiling

Scans differ in what they left under zero. The top of the density axis was a fixed constant that suited eight measured films differently, so it became a control — resting exactly on the old constant, so nothing moves until you touch it.

Raising it buys separation in a burnt patch, at the cost of pulling up whatever the scanner left under zero. It is the one handle that reaches a channel piled flat against the floor, which no gain and no black point can separate.

Colour is now Temperature and Tint

Colour was a single gesture that was hard to aim. It is now two named sliders, in the order and the sense every other application uses: pushing right warms. They are shouldered, so the travel is fine near the middle and never runs into a wall at the end.

They stay a finishing gesture and are deliberately independent of the balance gains above them: a finishing move must not displace the conversion, nor the answer an automatic button just gave. Colour density now sits with them, under the same heading, since it belongs to the same decision.

Lighting gains Shadows and Highlights

Exposure and White moved the two ends, and one gamma moved everything between them. That single exponent cannot fold two problems at once: after the medians are aligned, crossed dye curves leave the quarter-tones and the three-quarter-tones apart.

Two sliders now steer those two zones directly, and the same pair was added to the per-channel level windows. The ends stay pinned, so a black and a white placed on data keep their meaning whatever the inner three carry. Everything rests where it did, so nothing moves until you move it.

Three more automatic balance buttons

None of them is right on every photograph, and that is the point — each wins films the others lose, in a measured way. Whites places the white point on a higher quantile than Highs. Body moves both ends together so the tenth and ninetieth quantiles land on green's. AVG is the average position of all five handles across the other five buttons.

AVG is first on nothing and last on nothing, which is what it is for: it is the one to press when you do not want to try five. The buttons that assume nothing about the scene's colour are tinted apart from the ones that align a statistic across the channels.

Sharpening and chroma denoise are dosed in pixels

Both radii were a fraction of the image's width, calibrated on one scanner's output. The same setting therefore meant a different real dose on a scan of another size, silently.

They are now full-resolution pixels: a slider reading 3 px puts 3 px in the export. This is what moves the rendering, and only on scans whose long side is not 8368 px. Their ranges were tightened to what a preview can actually show — the bottom of the sharpening track is now the smallest radius a fitted preview can display at all, so no part of the gesture is inert on screen while still reaching the export. Use the loupe to judge the real dose: a fitted preview cannot show it, whatever the unit.

Navigating the photograph

Dragging now follows the pointer instead of moving against it. The wheel zooms in sixteen steps across the range rather than the barely perceptible amount it moved before. A double click goes to 100 %, and 100 % now really shows the pixels at any window size — the readout used to say "100 % · preview" on a large display while showing a reduced copy.

Explanations moved into the panels

Notes under blocks are gone. A question mark now sits to the right of each section title and opens a bubble on hover, drawn above the panel and confined to the picture area, so it never covers the control it describes. Every sentence was rewritten to say what the control does for you rather than how it works.

Smaller things

A resized export no longer leaves a translucent rim around its edge.

The window can no longer be dragged narrower than its own contents, which used to cut the interface off on the right instead of refusing.

Lighting's Shadows and Highlights sliders lighten to the right, like Exposure and White beside them. Only the handle was reflected: the stored value, the sidecar and the rendering do not move.

What it runs on

macOS 13 Ventura or newer, on Apple Silicon or Intel — one universal build for both. The interface is dressed for recent macOS; on older systems it is the same app with a plainer toolbar.

0.0.17

v0.0.17 on GitHub

Your photographs render exactly as before. No stage changed shape and no setting changed value: a conversion already made comes back identical, pixel for pixel. One thing does not survive going back, and it is named first.

Presets are stored in a new form. Yours are read and converted on first launch, carrying exactly what they carried before. But a preset saved from this version is not readable by an earlier one: going back to 0.0.16 or below would show your presets carrying nothing. The photo settings themselves are untouched by this and read on any version.

A preset carries settings, not tabs

Saving a preset offered three tabs — Balance, Rendering, Detail — and each was all or nothing. It now offers the settings themselves, grouped and named as the panels name them: white balance, colour, lighting, flat-field, grade, detail. The box opens with exactly what you moved on that photograph ticked, so saving a look no longer drags every resting stage along with it.

Three settings could not be carried by any preset at all, because no tab named them: colour density, texture and the flat-field reference. They can now.

The white balance is one choice rather than two boxes, because manual and automatic write the same two settings. A preset can carry the values this photograph holds, or a method — Classic, Mids or Highs — which is measured on each photograph it is applied to rather than copied. Hovering such a preset shows the balance it will produce, not the preset without it.

Saving now happens in the app's own window instead of a system alert, and the card's colour is chosen there rather than afterwards.

Copying settings adds, and no longer strips

A copy now carries only what the source photograph actually moved. Pasting can lend a look to a frame; it can no longer take one away because the source happened to leave a stage at rest. In the same move, the conversion mode stopped travelling: pasting from a negative no longer flips a positive scan, which it did silently on a mixed roll.

Smaller things

The negative/positive selector acts on what is selected in the gallery. It used to stay bound to the last photograph opened, so it wrote to that one whatever you had selected — and stayed lit after leaving the editor when nothing was selected at all.

The gallery opens at the end of the library again, where the latest import sits.

The loupe rests at 150 % and says so. Above 1:1 the pixels are interpolated: the sharpening dose is still the one that will be written, the pixels are no longer exact.

Cropping no longer vibrates — there are no notches to feel on a frame's edges. Swiping between panels gives one tick at the point of no return instead of a run of them along the way.

0.0.16

v0.0.16 on GitHub

Your photographs render exactly as before. No stage changed shape and no setting changed value: a conversion already made comes back identical, pixel for pixel. This version is about the button holding the far end of the bar.

The gallery imports where the editor exports

That button now carries the gesture that finishes the surface you are on: importing in the gallery, exporting in the editor. Each face has its own fill, so the colour states which gesture the slot is holding rather than leaving you to read the label.

Exporting from the gallery has not gone anywhere. It stays on the File menu and on the shortcut, count of targeted photographs included, and the import button says so on hover — the bar is one of four places that gesture is reachable from, never the only one.

A waiting update still outranks both faces

When a new version is waiting, that same button offers it, in orange, on either surface. The gallery is where the app lands, so a face hidden there would be hidden altogether. It still installs nothing without a click.

0.0.15

v0.0.15 on GitHub

Your photographs render exactly as before. No stage changed shape and no setting changed value: a conversion already made comes back identical, pixel for pixel. This version settles what the app itself looks like.

The app no longer follows the system's light appearance

Nothing pinned the interface's appearance, so a Mac set to Light dressed the app in a theme that was only ever half built. The canvas and the photograph tiles are fixed dark values by design — a photograph has to be the brightest thing on screen — while the panels, the title bar and the alerts took whatever the system handed them. Measured, a light system ringed a canvas at 0.106 with panels at 0.588: a fivefold mismatch, and nothing in the app could correct it.

The app now sets its own appearance at launch, before the first window is drawn, so it looks the same whichever theme the system is on. Alerts, panels and the title bar follow, which the SwiftUI setting alone would have left behind on the system theme.

Under the hood

A check reads the launch code as text and fails if that pin is ever removed, so a later change cannot quietly hand the interface back to the system. The export sheet carried its own dark override, which existed only to survive the light theme; it goes with it.

0.0.14

v0.0.14 on GitHub

Your photographs render exactly as before. No stage changed shape and no setting changed value: a conversion already made comes back identical, pixel for pixel. This version is about the memory the app holds while you work.

Scans no longer pile up in memory

Decoding a linear DNG built its pixels in a buffer the system's allocator then kept resident even after the app released it. Opening one photograph after another climbed by more than a gigabyte each time and never came back down. Those pages are now returned the moment a photograph is closed.

Measured over four consecutive opens of a 78-megapixel scan: what stays held afterwards went from 6.5 GB to 24 MB. Browsing a roll of scans now sits under a gigabyte where it used to reach four.

One less render at every open

Every photograph opened ran a full-frame measurement whose result nothing read. It is gone. The full-resolution frame is still decoded when a photograph opens — it has to be, for the loupe and for 100 % zoom — but off the main thread, so the window never waits on it.

A warning that could fire on a healthy photograph

The "flat decode" remark read an empty render as a flat frame. A buffer that comes back exactly zero everywhere is a failed measurement, not a photograph with no contrast, and it no longer raises the notice.

Under the hood

Nothing measured this app's memory before, which is why the leak of the previous version reached a user before it reached us. Four instruments now do, and they run from the command line: one drives thousands of slider writes, one opens photograph after photograph, one builds the state a step at a time, and one sweeps a slider across its whole travel counting the frames actually presented. Each reports the footprint alongside what Metal has allocated.

That last one found something the others could not: a probe that launches the app without a window renders nothing at all, and had been reporting comfortable numbers for a canvas that never drew a pixel. Work on drag responsiveness continues with an instrument that can now see it.

0.0.13

v0.0.13 on GitHub

Your photographs render exactly as before. No stage changed shape and no setting changed value: a conversion already made comes back identical, pixel for pixel. This version fixes a memory leak that a tester hit at 19 GB.

Browsing no longer holds what it opened

Opening one photograph after another kept about a full-resolution frame each time, and never gave it back. On a machine with plenty of RAM it stayed invisible for a long while; on a smaller one the app grew until the system started swapping. This is the defect behind the slowdowns reported after a long session.

The cause was the preloading of neighbouring frames: the app decoded the previous and the next photograph in advance so an arrow key would cost nothing. Its reservation marker never actually stuck, so every open relaunched the same decodes, and the frames piled up.

The preloading is gone rather than patched. Measured over twenty consecutive opens, the footprint is now flat where it used to climb by about 1.9 GB per photograph. The price is stated plainly: an arrow key now decodes on demand instead of finding the frame ready, so moving along a roll is a little slower. A long session's memory comes before one key's comfort.

Under the hood

Nothing measured the app's memory before this, which is why the defect reached a user before it reached us. Two instruments now exist, and both run from the command line: one drives thousands of slider writes, the other opens photograph after photograph, and each reports the footprint alongside what Metal has allocated. A slider drag, measured over five thousand writes, holds nothing.

Two smaller changes ride along: the curve pass no longer builds a callback on every evaluation, and each drawn frame now releases its temporaries at its own end rather than at the end of the run loop's turn.

0.0.12

v0.0.12 on GitHub

Your photographs render exactly as before. No stage changed shape and no setting changed value: a conversion already made comes back identical, pixel for pixel. This version adds a third mode next to Negative and Positive.

A black-and-white mode

B&W joins Negative and Positive in the top bar, because it is the same kind of thing: what the frame is, not a treatment you dial. It inverts and rests exactly like a negative, because that is what it is — printed grey.

It adds no channel mixer, and that is deliberate. The three sliders in the Colour block already do that work: each places one point at the middle of its channel's curve, never touching the black or the white point. Push red and the sky darkens — that is the Tri-X yellow filter, reached with a control you already know. The projection to grey happens only at the very end, and does nothing but render what those curves prepared.

Put differently: all the colour work still happens. It is what decides which grey.

What the mode takes away

Anything that only shapes colour leaves the screen rather than sitting there doing nothing:

  • The Grade tab — the finishing balance and zone saturation act on a chroma the last stage projects away regardless.
  • The Mids and Highs buttons of the automatic balance: aligning medians or highlights across channels shapes a cast. What remains is Equalise, which places each channel's ends and gives a clean base.
  • The Color density slider. Its value is kept and returns if you switch back to colour.

Cropping, the automatic balance, the three colours, the Lighting block, flat-field correction and the whole Detail tab all stay. The manual conversion is still there behind its door, called manual equalisation here.

Also

  • A photo set to black and white and then opened by an earlier version opens as a negative instead of refusing to open at all. Its settings are intact; only the mode is ignored by a version that does not know it.

0.0.11

v0.0.11 on GitHub

Le rendu de vos photos ne bouge pas. Aucun étage du pipeline n'a changé, aucun réglage n'a changé de valeur : ce que vous exportez sort identique au pixel près.

L'application dit qu'elle est en alpha

Une bulle orange s'affiche en permanence sous la barre, dans l'éditeur comme dans la galerie : ONI is in Alpha, bugs are to be expected. Elle porte un lien qui ouvre directement le formulaire de signalement.

Elle est posée dans la bande que le rendu ne peut pas atteindre, donc elle ne couvre jamais un pixel de la photographie.

Elle remplace le relevé des moyennes du négatif brut, qui affichait les valeurs R, V et B lues dans le fichier. C'était un instrument de mise au point du traitement, pas une information utile pour convertir une photographie.

0.0.10

v0.0.10 on GitHub

Le rendu de vos photos ne bouge pas. Aucun étage du pipeline n'a changé de forme et aucun réglage n'a changé de valeur : ce que vous exportez aujourd'hui sort identique au pixel près. Cette version corrige ce que l'aperçu montrait de faux, et deux détails de la fenêtre.

Le liseré autour de l'aperçu

Un cheveu clair bordait l'image à l'écran, présent depuis toujours, et la réduction de bruit chroma le transformait en cadre coloré. Il n'a jamais touché le fichier exporté ni la vue à 100 % — c'était un défaut de l'aperçu seul, et il avait trois causes empilées, toutes corrigées.

  • Le décodage réduit assombrissait sa rangée et sa colonne extérieures. Le rééchantillonnage y couvre moins d'un pixel de la source : jusqu'à 10 % d'écart avec la ligne voisine, mesuré. Inversé, cet écart de densité ressort en clair.
  • Réduire l'image pour la faire tenir dans la fenêtre laissait son pixel de bord à moitié couvert. À un ajustement de 0,73, il n'était couvert qu'à 7 % : il se mélangeait donc au fond de la carte, et ce mélange dessinait le trait.
  • Et surtout, le recadrage. Le cadre est calé sur la grille des pixels de la pleine résolution, pour que l'écran et le fichier décrivent exactement le même rectangle — on n'exporte pas un demi-pixel. Mais un cadre entier à 8368 px tombe forcément entre les pixels d'un aperçu de 2000, et son bord sortait alors à 67 % de couverture. Les étages qui lisent un voisinage — réduction de bruit, netteté, texture — recevaient là une couleur faussée et la portaient vers l'intérieur sur la largeur de leur rayon. D'où une bande colorée, et non un simple trait.

Le bord est désormais reconstruit depuis la ligne juste à l'intérieur, uniquement sur les copies réduites. À pleine résolution le cadre tombe sur des entiers, donc rien n'y est touché : le fichier exporté garde son propre bord, tel que la pellicule le donne.

La fenêtre

  • En plein écran, la barre du haut reprenait le matériau du système et se dotait d'un filet. Elle reste maintenant plate et sans filet, et le redevient à chaque passage en plein écran — macOS y remet sa parure à chaque bascule.
  • La carte de l'aperçu est à fleur des panneaux dans l'éditeur. La gouttière qu'elle gardait sur ses côtés arrêtait les séparateurs du panneau de gauche avant l'image, et donnait à la colonne de droite une marge plus large d'un côté que de l'autre. Elle ne sert qu'à la galerie, où l'image serait sinon collée aux deux bords.

Aussi

  • Les vignettes se régénèrent une fois : celles en cache portaient encore le liseré.

0.0.9

v0.0.9 on GitHub

Le rendu change si vous aviez réglé la réduction de bruit chroma à la main : l'étage a changé de place dans la chaîne. Il n'agit plus sur le négatif avant inversion mais sur l'image finie, et il fait enfin ce qu'on lui demande. Les photos dont vous n'aviez pas touché ce réglage ne bougent pas.

Le bleu fluo : la cause est trouvée

La version précédente désactivait la réduction de bruit chroma faute de comprendre pourquoi elle teintait les contours. C'est compris, et mesuré.

L'étage remplaçait la couleur d'un pixel par celle de son voisinage, exprimée en valeur absolue. Or il agissait sur le négatif, avant l'inversion — c'est-à-dire avant un logarithme. Prendre la couleur d'un voisinage clair pour la poser sur un pixel sombre pouvait retirer au canal bleu davantage que ce canal ne contenait. Le résultat passait sous le plancher, et le logarithme le renvoyait saturé : un liseré bleu du côté sombre de chaque contour.

Deux choses expliquent que ça se voyait surtout sur les scans en DNG ou en TIFF. Leur canal bleu part beaucoup plus bas — entre 0,04 et 0,10 quand le rouge est à 0,45 — parce que le masque orange du film l'écrase et que ces fichiers n'ont reçu aucune balance au décodage. Il n'avait donc presque aucune marge avant le plancher. Et l'effet se concentrait sur les contours, là où la couleur d'un pixel diffère le plus de celle de ses voisins : d'où cette impression d'accentuation colorée plutôt que de lissage.

L'étage rejoint la netteté et la texture, à la fin

Il agit désormais après l'inversion, en tête du groupe de finition. Trois conséquences :

  • Il débruite pour de bon. Mesuré sur l'image telle qu'on la voit, il retire la majeure partie du bruit de couleur ; à son ancienne place il en ajoutait sur les fichiers les plus touchés, tout en paraissant bien travailler dans l'espace où on le mesurait.
  • Il ne peut plus produire de bleu saturé. Sans logarithme en aval, un débordement devient au pire un point sombre, écrêté comme n'importe quel autre.
  • On lisse la couleur qu'on voit, et non une couleur que les courbes, la balance de finition et la saturation par zone vont ensuite redéformer.

Les deux curseurs restent à zéro au repos. L'étage fonctionne, mais c'est à vous de le doser : la loupe de l'onglet Detail montre son effet à l'échelle réelle du fichier.

Aussi

  • Le contrôle automatique qui surveillait cet étage mesurait la bonne grandeur dans le mauvais espace, et restait vert pendant tout le défaut. Il mesure désormais après l'inversion, et son contre-exemple est l'ancien traitement reconstruit à l'identique.

0.0.8

v0.0.8 on GitHub

La réduction de bruit chroma est désactivée, sur les photos déjà importées comme sur les nouvelles. Elle teintait les contours en bleu au lieu de lisser la couleur, et d'autant plus que la résolution était basse. Si vous l'aviez réglée à la main, votre réglage est conservé et continue de s'appliquer — c'est seulement la dose que personne n'avait choisie qui cesse d'agir.

Ce qui se passait

L'étage était censé lisser la couleur en laissant le grain intact. Sur certaines images il faisait l'inverse : au lieu de fondre les détails, il les dessinait, en bleu. Le défaut se voyait surtout sur les fichiers déjà réduits — scans en DNG linéaire, TIFF — et il empirait à mesure que la résolution baissait. Sur un RAW à pleine résolution il fallait pousser les deux curseurs au maximum pour le déclencher.

La cause n'est pas encore établie. Plutôt que de livrer une correction devinée, l'étage cesse d'agir tant qu'on ne le lui demande pas : les deux curseurs partent de zéro, et un double clic y revient. Il reste entièrement disponible, à la main, pour qui veut l'essayer.

Pourquoi vos photos déjà traitées sont concernées

Mettre un réglage par défaut à zéro ne change rien aux photographies déjà importées : leur sidecar porte la valeur qu'avait la version où elles ont été ouvertes, et il la garde. Elles auraient donc continué de bleuir.

Cette version reconnaît donc exactement les deux valeurs de repos que les versions précédentes posaient, et les relit à zéro. Toute autre valeur — celle que vous avez posée vous-même en déplaçant un curseur — est un choix, et elle survit intacte. Aucun autre réglage n'est touché.

Aussi

  • Le format des sidecars passe en version 2. Les fichiers écrits par cette version restent lisibles par les précédentes, qui ignoreront simplement la mise à zéro.

0.0.7

v0.0.7 on GitHub

Le rendu de vos photos change. Trois étages du pipeline ont été corrigés, et les conversions déjà faites ne ressortiront pas identiques : la texture a changé de forme, la netteté et la réduction de bruit avaient des rayons trop petits pour agir, et les DNG linéaires étaient réduits d'une façon qui fabriquait du bruit couleur. Aucun réglage n'est perdu — vos sidecars se relisent tous — mais rouvrez une photo déjà traitée avant d'exporter.

Cette version refond aussi l'organisation des panneaux de fond en comble. C'est la mise à jour la plus large depuis le début.

Le panneau est réorganisé par usage, plus par ordre de traitement

Quatre onglets au lieu de trois, et ils suivent maintenant la façon dont on juge une photographie plutôt que l'ordre dans lequel la machine calcule :

  • Frame — le cadrage, d'abord, pour que la balance automatique et l'histogramme ne comptent que les pixels gardés.
  • Color — arriver à une image juste : balance automatique, couleur, densité, lumière.
  • Grade — prendre parti sur une image juste : balance de finition et saturation par zone.
  • Detail — réduction de bruit, netteté et texture.

Les courbes sont devenues des curseurs. Trois couleurs et un contraste, là où il fallait poser des points à la main. Le contraste rejoint les niveaux dans un bloc Lighting, parce qu'il se juge contre l'exposition qu'il façonne et non tout seul. Ses libellés disent enfin ce qu'ils font : Exposure, Black point, White point, et pousser vers la droite éclaircit dans les trois cas.

La conversion manuelle passe à la fin, derrière une porte. Le chemin court vient en premier ; la balance de source et les niveaux par canal restent accessibles d'un clic, dans une boîte sombre qui dit qu'on est ailleurs. La correction de flat-field en sort, puisqu'elle corrige l'éclairage du banc de numérisation et non la photographie.

Color density, un réglage neuf

Un curseur qui écarte les trois canaux les uns des autres sans toucher à la saturation. Le gris est son point fixe : une zone vraiment neutre ne bouge pas, et tout ce qui s'en écartait s'en écarte davantage, proportionnellement à ce qu'il en était déjà loin. Une couleur ressort alors plus claire que le gris avec lequel elle était à égalité — ce sont les aplats qui gagnent en présence, pas la chroma qui monte.

Detail : une loupe, et une texture qui ne moutonne plus

Une loupe remplace l'histogramme dans cet onglet. La réduction de bruit et la netteté agissent sur un rayon de deux pixels du fichier : aucun aperçu réduit ne peut les montrer, l'information n'y est pas. La loupe affiche la zone au centre de la vue à l'échelle réelle du fichier, 1:1 ou 2:1, avec exactement le dosage qui sera exporté. L'histogramme de sortie, lui, n'apprenait rien ici — aucun de ces trois étages ne déplace visiblement la distribution.

Texture est un contraste de bande médiane : ce que la netteté ne peut pas atteindre et ce que la clarté déborde. Elle a été entièrement refaite depuis sa première version, qui posait des plaques au lieu de dessiner. Elle repose maintenant sur une base qui suit les contours, ce qui divise le halo par cinq à l'endroit où il se voyait, et elle s'affiche dès l'aperçu au lieu d'exiger le zoom à 100 %.

La netteté perd son seuil, qui restait à zéro sans que rien ne prouve qu'il servait. Son rayon est ancré au milieu de sa course, là où le geste se fait réellement sur un scan de film.

Trois réglages qui ne faisaient presque rien

  • La netteté et la réduction de bruit chroma étaient trop faibles pour agir. Leur rayon est une fraction de la taille de l'image, et il était descendu sous le seuil où un flou gaussien rend son entrée inchangée. Sur un fichier de 8368 pixels la réduction de bruit travaillait sur 0,84 pixel ; elle en couvre 8,4 maintenant.
  • Le rayon suivait la largeur, pas le grand côté. Un scan en portrait recevait jusqu'à trois fois et demie moins de dose que le même scan en paysage.
  • La texture était purement sautée sur l'aperçu. Le curseur se réglait à l'aveugle : il n'agissait qu'à 100 % de zoom et dans le fichier exporté.

Les DNG linéaires : un bruit couleur qui n'existait pas

Un DNG linéaire — un scan à trois canaux, non mosaïqué — était réduit en prenant un pixel sur n, sans moyenner. L'aperçu portait ainsi près d'une fois et demie l'énergie fine du fichier réel, et près du double en couleur : le bruit chroma qu'on cherchait à retirer était en partie fabriqué par cette réduction. Les vignettes et la mesure que lit la balance automatique étaient les plus touchées. Elles moyennent désormais toute la zone qu'elles remplacent.

La barre et les gestes

  • Le avant/après devient une touche de la barre.
  • Un bouton coupe et rend le son des touches.
  • Nouvelle icône d'application.
  • Le quart de tour, le son et le avant/après ont des icônes qui nomment le geste et non l'état.

Aussi

  • La balance automatique lisait une mesure qui portait déjà la densité couleur et la texture : elle se posait donc sur une image qu'elle avait elle-même modifiée.
  • Un export par lot demande confirmation avant de remplacer des fichiers existants, comme le faisait déjà l'export d'une seule photo.
  • Plus aucun chemin du disque de développement dans le binaire livré.

0.0.6

v0.0.6 on GitHub

Le rendu de vos photos ne bouge pas. Aucune constante, aucun étage du pipeline n'a changé : les conversions déjà faites ressortent identiques au pixel près.

La galerie défile enfin correctement

Faire défiler la grille ou la pellicule saccadait, et d'autant plus que le pointeur passait sur les vignettes. Trois causes, aucune dans le traitement d'image :

  • Rien ne limitait le travail de fond. Chaque vignette qui apparaissait lançait sa propre tâche : trente-six cellules à l'écran, c'était trente-six décodages de RAW en même temps. Ils sont maintenant mis en attente, et surtout servis en commençant par les dernières demandées — c'est-à-dire ce que vous regardez, au lieu de ce que vous venez de dépasser.
  • Une vignette qui arrivait faisait redessiner toute la galerie, deux fois. Elle ne redessine plus que sa propre case.
  • Les images étaient reconstruites à chaque affichage. Elles sont décodées une fois, quand elles sont produites.

Les vignettes sont aussi demandées à la taille où elles sont affichées, au lieu de toujours la plus grande : trois fois moins de pixels à la taille par défaut.

Aussi

  • Le survol d'une vignette ne fait plus travailler ses trente-cinq voisines.
  • L'app ne peut plus être lancée sous Rosetta par erreur sur un Mac Apple Silicon.

Si vous êtes sur macOS Ventura : toujours vrai, vous êtes les premiers à y lancer l'app et rien n'y a été testé ici. Signalez ce qui cloche, même un détail.

0.0.5

v0.0.5 on GitHub

Le rendu de vos photos ne bouge pas. Aucune constante, aucun étage du pipeline n'a changé : les conversions déjà faites ressortent identiques au pixel près.

Le retour vers la galerie ne bloque plus

En 0.0.4, fermer une photo pouvait figer l'app : l'image restait au centre de l'écran, floutée, les deux panneaux disparaissaient et la galerie n'apparaissait jamais. Il fallait quitter et relancer.

La photo était animée d'un mouvement qui l'emmenait de sa vignette au canevas à l'ouverture, et refaisait le chemin inverse à la fermeture. C'est ce retour qui pouvait rester en l'air. Ce mouvement est supprimé — la bascule est maintenant immédiate, et seuls les deux panneaux latéraux s'animent, à l'ouverture comme à la fermeture.

C'est un choix, pas un pansement : le mouvement était beau et compliqué, il valait mille lignes, et il produisait un blocage complet. La bascule instantanée ne peut pas rester en l'air.

Si vous êtes sur macOS Ventura

Rien de nouveau depuis la 0.0.4 sur ce point, mais ça reste vrai : vous êtes les premiers à y lancer l'app, et rien n'a jamais été testé sur Ventura ici. Signalez ce qui cloche, même un détail d'affichage.

0.0.4

v0.0.4 on GitHub

Le rendu de vos photos ne bouge pas. Aucune constante, aucun étage du pipeline n'a changé : les conversions déjà faites ressortent identiques au pixel près.

macOS Ventura suffit désormais

L'app demandait Sonoma (14) ; elle demande maintenant Ventura (13). C'est deux versions de macOS de moins que la première publication, et ça vise une machine précise : un Mac qui ne peut pas monter plus haut.

Une cinquantaine d'endroits du code utilisaient des fonctions apparues après Ventura. Elles ont été réécrites avec des moyens plus anciens qui font la même chose — et partout, pas seulement sur les vieux systèmes : une fonctionnalité écrite deux fois est une fonctionnalité vérifiée une seule fois. Les animations, notamment, ne sont plus demandées au système mais calculées ; l'équivalence a été mesurée au seizième chiffre après la virgule, et les mouvements sont identiques.

Ce que ça implique si vous êtes sur Ventura : vous êtes la première personne à y lancer l'app. Elle compile et passe ses 967 contrôles, mais aucun d'eux n'a tourné sur Ventura — personne n'en a sous la main ici. Signalez ce qui cloche, même un détail d'affichage.

Le paquet de courbes s'ouvre sur le contraste

Les six presets de contraste passent en première page et les six d'exposition en seconde. Les courbes elles-mêmes sont inchangées.

Un défaut d'affichage corrigé

Dans la galerie, les vignettes pouvaient rester sur un rouet indéfiniment et les tuiles ne réagissaient plus au survol — jusqu'à ce qu'un clic débloque tout d'un coup. Une optimisation de la 0.0.3 faisait considérer la grille comme inchangée alors qu'elle avait de quoi se redessiner.

0.0.3

v0.0.3 on GitHub

Le rendu de vos photos ne bouge pas. Aucune constante, aucun étage du pipeline n'a changé : les conversions déjà faites ressortent identiques au pixel près.

L'app tourne beaucoup plus vite

Sur un négatif de 448 Mo, manier un curseur coûtait 29 ms par image — 34 images par seconde, et une image sur sept dépassait le seuil où l'œil voit une saccade. C'est maintenant 4 à 5 ms, soit environ 230 images par seconde. Le geste est fluide, et l'histogramme suit la main au lieu de l'attendre.

Trois causes, aucune dans le traitement d'image : les mesures d'histogramme se relançaient à chaque cran, l'historique d'annulation s'enregistrait à chaque cran, et la pellicule se redessinait entièrement alors qu'elle n'affiche que des vignettes.

Elle tourne sur plus de Mac

  • Mac Intel : l'app est désormais universelle. Elle ne démarrait pas du tout sur ces machines.
  • macOS Sonoma (14) au lieu de Sequoia (15).

Aussi

  • Un badge signale une source 8 bits sous l'histogramme : ces scans n'ont que 256 valeurs par canal, ce qui donne un tracé en peigne. Ce n'est pas un défaut de l'app, et le badge est là pour que ça se lise comme tel.
  • L'app vérifie les mises à jour toutes les douze heures au lieu de vingt-quatre.

Si vous avez la 0.0.1, elle ne démarrait sur aucune machine sauf celle qui l'a compilée — retéléchargez plutôt que d'attendre une mise à jour, elle ne peut pas vous parvenir.

0.0.2

v0.0.2 on GitHub

Correctif d'urgence. Le rendu de vos photos ne bouge pas : cette version ne change aucun réglage, aucune constante, aucun étage du pipeline. Les conversions déjà faites ressortent identiques.

La 0.0.1 ne démarrait pas, sur aucune machine sauf celle qui l'avait compilée. Elle cherchait ses icônes dans un dossier qui n'existe que sur l'ordinateur du développeur, et s'arrêtait net quand elle ne le trouvait pas — d'où une fenêtre de plantage sans le moindre rapport lisible. Si vous avez tenté la 0.0.1 et abandonné, c'était ça, et ce n'était pas votre installation.

Aussi dans cette version

  • Les kernels graphiques étaient compilés pour une version de macOS plus récente que celle que l'app annonce supporter. Sous macOS 26, tous les étages de traitement disparaissaient en silence : l'image s'affichait sans conversion. Corrigé, et un contrôle refuse désormais de construire une version qui recommencerait.
  • Le lecteur de fichiers TIFF et DNG ne vérifiait pas ce qu'il lisait : certains fichiers pouvaient faire disparaître l'app, y compris en faisant simplement défiler la galerie. Il borne maintenant tout ce qu'un fichier lui annonce.
  • Un export par lot pouvait, dans un cas précis, écrire par-dessus le scan d'origine. C'est désormais refusé avant que quoi que ce soit soit écrit.

Merci à celles et ceux qui ont signalé le plantage : c'est exactement à ça que sert une alpha.

0.0.1

v0.0.1 on GitHub

Première version, et c'est une alpha. Elle ne déplace le rendu d'aucune photo déjà réglée, parce qu'il n'y en a pas encore : il n'existe aucune version antérieure à celle-ci.

Le numéro dit ce que c'est : 0.0.1. L'app est utilisable de bout en bout — on importe, on convertit, on exporte — mais elle est calibrée sur les pellicules d'une seule personne, et c'est précisément ce que cette alpha cherche à corriger. Les retours sur ce qui sort faux chez vous valent plus que les retours sur ce qui manque.

Cette ligne est en tête parce que toutes les suivantes le seront aussi. La règle de ce projet est de ne pas geler le rendu — l'app existe pour être calibrée sur du vrai film, et une constante mal choisie ne se voit que sur les négatifs de quelqu'un d'autre — mais de geler la surprise. Toute version qui déplacera une conversion déjà enregistrée le dira ici, avant l'installation, et vous pourrez la refuser.

Ce que fait cette version

  • Conversion de négatifs couleur C-41 photographiés au boîtier, à partir du RAW. L'inversion se fait en densité, donc les curseurs se comportent linéairement en stops.
  • Gains par canal, niveaux par canal et RVB, courbes, balance de finition, saturation par zone, débruitage chroma, netteté, recadrage et redressement.
  • Trois boutons de balance automatique, qui proposent des positions : le survol les dessine sur l'histogramme, et rien ne s'applique sans un clic.
  • Presets, galerie, labels, export TIFF 16 bits ou JPEG.
  • Tout reste en flottant du RAW au fichier écrit, sans aucun tampon intermédiaire quantifié. C'est vérifié à chaque build, pas espéré.

Ce qu'il faut savoir avant de commencer

  • C'est un alpha. Le rendu peut bouger d'une version à l'autre — voir la première ligne.
  • Vos originaux ne sont jamais touchés. L'app importe par lien et ne fait que les lire. Vos réglages vivent dans un petit fichier à côté de chaque RAW.
  • macOS 15 ou plus, Apple Silicon.
  • L'app cherche ses mises à jour seule et n'en installe jamais une d'elle-même : elle vous montre ces notes et attend.

Un défaut à signaler ? La version se lit dans la fenêtre « À propos », et si c'est un problème de décodage ou de couleur, joignez le RAW — une capture montre le symptôme, le fichier le reproduit.