Skip to content

Lith printing - #798

Merged
marcinz606 merged 3 commits into
mainfrom
feat/lith
Aug 11, 2026
Merged

Lith printing#798
marcinz606 merged 3 commits into
mainfrom
feat/lith

Conversation

@marcinz606

@marcinz606 marcinz606 commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Lith printing as its own pipeline stage, B&W only, off by default.

The model

Two phases, following Rudman's "New Rules" and Moersch's Lessons 1-6:

  • a long low-gamma highlight branch fixed by exposure alone
  • a near-vertical infectious knee whose position is the snatch point
  • past it, the flat Dmax "lith band" with no tonal separation at all
D_h = f_max · (1 − exp(−f_rate · (D0 + 0.301·E·v) / f_max))
D   = D_h + (D_max − D_h) · σ((D0 + 0.301·E − K) / w)

Controls are Exposure (0 to 5 stops, default 2), Snatch Point (development-time proxy, sets K) and Abruptness (sets w). "abrupt" is Moersch's own word for the blackening, and his additive table is what maps it to the hydroquinone-to-alkali ratio.

Colour comes from the paper

PaperProfile gains a lith_path: four (a*, b*) anchors at output-density fractions 0.10 / 0.35 / 0.65 / 1.00, running peach, ochre, olive, neutral. The olive knot is the point, since on warmtone paper the transition between warm highlights and cold blacks really does go green.

This finally gives the four B&W profiles a reason to differ from each other. Fomatone liths warm and colourful; Neutral and the Ilford papers stay nearly colourless, which matches Rudman's lithability test reading an incorporated accelerator in Multigrade.

Toner interaction

Lith silver is fine, high-surface-area silver sitting on the steep part of the tone-vs-particle-size curve (Kong & Shore 2007), so the same bath moves it much further than normal filamentary silver. Selenium keeps its silver-proportional shape but reaches into the midtones and turns the green-black shadows magenta. Gold drops the bleach-limited weighting for a flat conversion, matching Moersch: "gold toner attacks all densities evenly". Sepia, iron blue, copper and vanadium are inert or redundant on lith and grey out.

Two latent bugs fixed

Both flattened the new colour away, and both were pre-existing assumptions that only a chromatic B&W print could expose:

  • The silver ledger rebuilt all three channels from the pixel's mean density. Harmless when the input is grey, but it meant a trace of any toner wiped the lith colour and replaced it with the toner's own. Now each channel uses its own density; the mean still drives susceptibility. Bit-identical for a grey print, so no existing toning moved.
  • toning.wgsl re-collapsed B&W to luma as a safety net, after the lith pass had put real colour on the print.

Verification

make all green: 3750 passed, 4 skipped.

CPU/GPU parity is bit-exact (max delta 0.0000) across disabled, enabled, snatch/abruptness extremes, lith+selenium and lith+gold.

Driven headlessly through the real app on a B&W scan: later snatch deepens the blacks and widens the band (0.5% → 8.2% → 20.5% of frame), the paper drives the colour (Fomatone 0.203 vs Ilford 0.072 mean R−B), and selenium bites harder under lith than without.

Docs updated in the same change: USER_GUIDE.md (new panel section with the ⓘ marker, plus the Paper profile bullet), PIPELINE.md (new stage section, the per-channel covering-power correction, the lith toner constants) and CLAUDE.md stage order.

Not modelled

Semiquinone/bromide diffusion halos, pepper fog, snowballs and the other fault modes.

Lith printing is a distinct darkroom process, not a curve preset: you
over-expose a lith-capable paper by several stops, develop it in a very
dilute low-sulphite hydroquinone developer, and snatch it out part-way
through the infectious-development cascade.

Add a Lith stage between Lab and Toning (GPU stage 5, B&W only, off by
default). It models the two phases the literature describes: a long
low-gamma highlight branch fixed by exposure alone, then a near-vertical
infectious knee, then the flat Dmax "lith band" with no separation left
in it. Controls are Exposure, Snatch Point and Abruptness, all named
after what a printer actually does at the tray.

Colour has no slider. Each B&W paper profile now carries a lith_path,
four (a*, b*) anchors indexed on output density running peach, ochre,
olive, neutral, so the paper chosen in Exposure is the only colour
control. This also gives the four B&W profiles a reason to differ:
Fomatone liths warm and colourful, Ilford Multigrade barely liths.

Toner interaction, from Moersch's Lesson 5: lith silver is fine
small-particle silver on the steep part of the tone-vs-grain-size curve,
so selenium reaches much further down the scale and turns the green-black
shadows magenta, and gold drops its bleach-limited weighting for a flat
conversion. Sepia, iron blue, copper and vanadium do nothing distinctive
on lith and grey out while lith is on.

Two latent bugs in the toning stage, both of which flattened the new
colour away and both fixed here:

- the silver ledger rebuilt every channel from the pixel's mean density,
  so any toner discarded colour the print already carried. It now uses
  each channel's own density; the mean still drives susceptibility.
  Bit-identical for a grey B&W print.
- toning.wgsl re-collapsed B&W to luma after the lith pass had added
  colour. Gated off under lith.

CPU and GPU agree bit-exactly across every lith case.
foot_veil was 0.20, so over-exposing barely lifted the highlight branch:
the whole highlight range printed between D 0.07 and 0.12, within a fifth
of a stop of paper white, and read as blown. Reference lith prints carry
a visible warm veil there — over-exposing two to four stops is what puts
tone in the highlights in the first place.

At 0.60 the highlight range spans D 0.21 to 0.42 at the default +2 stops.
Exposure 0 still prints clean white, so the slider keeps that end.

The old paper-white test encoded the wrong assumption; it now pins both
ends, plus a guard against the highlight branch collapsing again.
The escaped underscores in \text{} broke the math span. Also drops the
note about the removed lith grain.
@marcinz606
marcinz606 marked this pull request as ready for review August 10, 2026 19:48
@marcinz606
marcinz606 merged commit 3f803fe into main Aug 11, 2026
1 check passed
@marcinz606
marcinz606 deleted the feat/lith branch August 11, 2026 05:05
@marcinz606 marcinz606 mentioned this pull request Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant