DarkRoom, shown
A tour of what the application does, one picture per thing. Every image on this page was captured from the desktop build driving itself — nothing is a mock-up, and nothing has been retouched outside DarkRoom. Where a feature is better seen moving, it moves.
The requirements behind each feature are in requirements.md; the reasoning is in the design documents linked from each section. This page is only about what you see.
The photographs are the author's. None show a person.
Opening a library
DarkRoom opens on a library: a folder on this machine, a folder a sync client keeps, or a Nextcloud account. A folder needs no password and uploads nothing.

Once a folder is named, it is the library — you are not asked for it again,
and Open library opens it whole. Subfolder… narrows the scan to part of
it. The formats ticked are what the scan looks for; RAW is on and JPEG off
by default, because a RAW editor's sensible default is the file the camera
wrote first.

The library

Photographs are ordered by capture time, with a month heading where each begins. The strip on the left is the timeline — drag it to jump to a year. The bar above the grid filters by rating, flag, colour label and where the file is (on this device, or only on the server).
Rating and flagging
Hover a cell and the stars appear; click one. The filter chips above the grid
count what each rating holds, and clicking 3+ shows only those.

Colour labels work as Lightroom's do: 6 red, 7 yellow, 8 green, 9
blue, on the photograph under the pointer or on the selection, and the same
key again takes the label off. Label on the selection bar offers all five,
purple included, and None. Each label is drawn with its initial on it, so
it reads without telling the colours apart, and the filter bar has a chip for
each. In develop, the top bar names the open photograph's label and sets it,
and the same keys work there.

Getting about
Drag the timeline to scrub through years; Ctrl and the wheel resize the thumbnails.


Selecting several
Select in the header — or Ctrl-click — starts a selection. Shift-click picks
a range. The bar at the foot of the grid is everything a selection can be done
to: collections, keywords, presets, export, and merging to a panorama.

Keywords on that bar opens a sheet; type a word and press return, and it
is on every photograph selected. The list below the field is every keyword
the library has, ticked where the selection carries it.

Collections
+ at the head of the sidebar makes one. Drag a photograph — or the whole
selection — onto its row to file it there; click the row to see it. A
photograph can be in several, and the badge on its cell counts them.

Collections nest. Drag one onto another to put it inside; + with a
collection selected — or New collection inside from its menu — makes a
child. A parent shows everything its children hold, and its count says so.
Right-click a row (hold it, on a tablet) for the menu: rename, nest,
move back to the top level, keep it offline, delete.



Bursts
Frames taken a moment apart that look alike fold into one cell, with a badge counting them. Click the badge — tap it, on a tablet — to open the burst in the grid, and again to fold it back. A folded burst shows its earliest frame; to have it show another, open it and click the ring on the frame you want.
With faces indexed, narrowing the grid to a person puts Eyes open beside
their name on the filter bar, which leaves out the frames where they blinked.
Developing a photograph
Click a thumbnail to open it. The column on the right is every adjustment; the strip at its head narrows it to one group.


Light
Exposure, contrast, highlights, shadows, blacks, whites and a tone curve.
Hold Before to see the photograph as it was.

Looking closer
Double-click for 1:1; drag to move about; double-click again to fit. The wheel zooms to any amount in between. Past 1:1 the file's own pixels are drawn as hard-edged blocks rather than smoothed, so what you see is what the sensor recorded.

Moving between photographs
The roll along the foot of the canvas holds the photographs the grid was
showing; click one to open it. The right arrow, D or space opens the next,
and the left arrow or A the one before. The edit on screen is saved on the
way, so stepping along a shoot loses nothing.
White balance from the photograph
Press pick in the White Balance group, then click something neutral —
a white wall, a grey card, the air conditioner here. The picker sets the
sliders from the photograph, not from where they were: below, the frame
is dragged cold first and one click puts it right. A blown highlight is
refused, since a clipped pixel has no colour left to balance.

Composing
Crop by dragging the frame's corners, straighten with the slider, lock a
ratio from the chips. Done composing returns to the photograph.

Vertical and Horizontal, under Straighten, correct the lines that
converge when the camera is tilted up at a building; the crop refits to
what is left.

A crop that leaves a mask wholly outside the frame says so, and offers to take the crop back or keep it.

Local adjustments
Local in the rail turns the column into a mask stack. Find subjects runs a
segmentation model over the photograph; what it recognises appears as a list
of categories with how much of the frame each covers. Click one and it is a
mask — then every slider below edits only that region.


A mask is a stack of parts. Paint into it, subtract a gradient from it, grow
or shrink its edge, choose how it falls off. Show masks as draws the mask
tinted, as alpha, or as an outline; the eye on its row switches it off.

Linear and radial gradients, a tone range and a colour range are the other
ways to make one; each can be combined with any other. ∩ Intersect keeps
only where the new part and the mask agree: below, a gradient over the
lower half, then two strokes that survive only where the gradient is.

Repair
Repair in the rail: click a mark and it is covered from a source DarkRoom
chooses beside it. Drag either circle to move it; the size, feather and
opacity are in the panel. Heal blends; clone copies.

Film
The Film chooser at the head of Adjust applies a spectral simulation of a
named stock; below it, the print exposure and push controls a film has and a
sensor does not.

History, snapshots, presets
Every change is a step; Undo and the History panel walk them. Snapshot
keeps the current state under a name. Presets… saves the settings to
apply elsewhere, and imports .xmp from other applications.

Copying settings
Copy in the top bar, or Ctrl+C, takes this photograph's settings; Paste,
or Ctrl+V, puts them on another, and says what it would paste — how many
adjustments, and whether the crop comes too. In the grid, Paste to N on the
selection bar pastes onto every photograph selected. Which kinds of edit a
copy carries is chosen in Presets…, or with Ctrl+Shift+C: a look carried
across a shoot usually leaves each frame's own crop alone.
Merging a panorama
Select the frames, then Merge to panorama from the selection bar. The
frames are read, aligned, and drawn on the suggested projection with each one
outlined where it landed — twelve hand-held portrait frames across an alpine
valley, here. Change the projection (a 150° sweep on a flat perspective is
what the middle of the film shows, and why cylindrical is suggested), ask for
the border to be filled rather than cropped, then Merge. The composite is
written beside its sources as a DNG and appears in the grid with the merge
as the first step in its history.



Export
Export in the develop header, or Export N from a selection. Format,
size, colour space, sharpening, naming and where the file goes are in
Settings, and apply to every export until changed. An export with no folder
set is refused, and the header says so.

Settings

Background activity with progress, thumbnail and face indexing, storage on this device, display and colour, export defaults, what is written to XMP sidecars, and a diagnostics bundle for a bug report.
People
Face detection and identity run over the library and group faces by person;
the Identity page is where suggestions are confirmed, rejected and split,
and People on the filter bar narrows the grid to someone. Not pictured
here, for the obvious reason — faces.md has the design.
Where things are written down
| Feature | Design |
|---|---|
| Local masks and segmentation | segmentation.md, mask-editing.md |
| Repair | spot-removal.md |
| Panorama | panorama.md |
| Faces and identity | faces.md |
| Gestures, generated from the code | gestures.md |
| Navigation and layout | ui-navigation.md |
| Sync and storage | storage.md |
How this page is made
tools/manual/ drives the desktop build on
a private X server and records each scene; record.sh <library> re-makes
every picture here. Run it after a change to the interface and commit what
changed. The pictures are in LFS.
The application carries this page. cargo run -p traceability -- manual
renders it to index.html beside it, which the packages install with the
pictures and the app opens from Help, from Settings, and from the "See it"
link beside a gesture on the help sheet. CI fails when the two differ.
Making it the first time turned up nine faults, each fixed in its own commit before the pictures were taken: the folder picker could not choose the top level, month headings overprinted each other, a category mask widened the column off the window, the mask tint outlived its mode, the first sync uploaded an empty thumbnail shard, a merge that ran out of GPU memory left the page on Stop for ever, the export settings promised to ask for a folder and did not, the keyword sheet sent its keys to the grid, and an empty trash told you to check your library folder.