Two Rust photo apps on the Mac
The same test as last time, on the desktop. On the Mac, LightPhotos lets Apple decode the photos. LightCraft decodes every photo with its own Rust code.
The main difference: who decodes the pixels
On the desktop, both apps are usual Rust programs. Both use all CPU
cores, draw with wgpu on Metal and read your folders in
place. The important difference is where the pixels come from.
LightPhotos: Apple decodes every JPEG, HEIC and RAW file. The decoders add 0 MB to the 15 MB app.
LightCraft also has many more finished features. It is a full Lightroom-style editor, with masks, healing, lens corrections, presets, history, HDR and panorama merges, and export to six formats. LightPhotos has a smaller job: you go through a folder fast, keep the good photos and make quick edits. Remember this for each number below.
| LightPhotos | LightCraft | |
|---|---|---|
| Decoding | Apple's ImageIO, and CoreImage's CIRAWFilter for full-size RAW | Its own, in Rust, for every format |
| HEIC | Yes | Not yet |
| Canon CR3 | Yes | Embedded preview only |
| Your photos | Folders, read in place. Each folder keeps its own part of the catalog. | One central catalog. Files stay in place unless you copy them. |
| License | MIT or Apache-2.0, with no copyleft libraries in the Mac app | MIT or Apache-2.0 |
One catalog or one in each folder
LightCraft keeps one central library, as Lightroom does. Its catalog records
every photo, so searches, albums and stacks can include all of them.
Import can add a photo in place, or copy or move it into
Originals/YYYY/YYYY-MM-DD/. Browse
Folder… also records what it sees. It removes those records
later if you did not change the photos.
LightPhotos does it differently. The app can open many folders, and
each folder keeps its own small cache and index in the
.lightphotos folder inside it. The catalog for a folder
moves with the folder. You can move, copy, rename or delete a folder
in Finder, and the app does not need to know, other than opening it again.
How I measured
I built both apps with their own release scripts. I ran them on one M4 Pro Mac with 48 GB of memory and macOS 26.3. For each run, I copied the same 20 Sony A7 II RAW files to a new folder, so no cache had seen them. Each app started with a new home folder and default settings.
Each window was 3840 × 2000 px on a 4K display. In a smaller window, the camera's embedded JPEG is large enough. Then neither app must develop the RAW data.
A script, tools/desktop-bench, starts the app,
double-clicks the first photo and pushes the right arrow eight times.
It captures the window every 150 ms. It records the first change,
and the time when nothing changed for 3 s. Each number is the
median of five runs. One capture takes about 0.34 s, so no
result can be less than about 0.5 s.
The numbers
| LightPhotos | LightCraft | |
|---|---|---|
| Download (.dmg) | 7.4 MB | 15.2 MB |
| App, installed | 15 MB | 43 MB |
| Launch to window | 0.27 s | 0.43 s |
| Launch to finished grid, 20 RAWs | 0.85 s | 1.37 s |
| Open the first photo: first change / screen settled | 0.57 / 1.46 s | 0.67 / 0.67 s |
| Next photo, each (median of 8): first change / screen settled | 0.55 / 0.55 s | 0.60 / 0.60 s |
| Memory afterwards | 1.2 GB | 1.4 GB |
The runs agreed to within a few percent, with three exceptions. The LightPhotos first photo took 1.0 to 1.5 s. One LightCraft grid took 2.3 s. LightCraft's memory was 1.3 to 1.7 GB.
What the measurement sees
Open first photo
Nothing prepared the first photo. The script records the first change and the time when the whole window stops changing. It cannot see when the photo becomes sharp.
- First visible change
- 0.57 s
- Whole screen settled
- 1.46 s
- First visible change
- 0.67 s
- Whole screen settled
- 0.67 s
Next photo
Both apps prepare the photos on each side. The 0.55 s and 0.60 s results are at the lowest value that the script can measure. The difference is too small to see.
- First visible change
- 0.55 s
- Whole screen settled
- 0.55 s
- First visible change
- 0.60 s
- Whole screen settled
- 0.60 s
Replay runs in real time. Drag the slider to inspect a moment.
Why the next photo is fast
Both apps prepare the photos on each side of the current photo. When you push the arrow key, the next photo is already developed. The app only has to show it.
Photo 1 is open. Nothing prepared it, so the app develops it now.
LightPhotos develops the neighbours with ImageIO, at the window size, up to 4096 px. LightCraft uses its own code, up to 2560 px. Both times are at the limit of the measurement, so I call them equal. Both apps pay the same cost: CPU time and battery for photos that you possibly do not look at.
Nothing prepares the first photo. At this window size, the embedded JPEG (1616 px wide) is too small. LightPhotos shows the grid thumbnail first, then develops the RAW data in about 0.6 s. LightCraft shows the first photo in one step.
My script waits a few seconds on each photo, as a person does who looks at each one. If you hold the arrow key down, you can go faster than either app can prepare. I did not measure that.
For 100% zoom and export, LightPhotos uses CoreImage's
CIRAWFilter. It uses the same Apple RAW engine, so the
pixels agree to within 8-bit rounding. But it runs on the GPU and
demosaics only at the size that you ask for. A full 24-megapixel
decode took 185 ms, against 590 ms through ImageIO. (Median
of the same 20 files, one at a time.)
Why the grid is faster and memory is lower
For a grid thumbnail, LightPhotos asks ImageIO for a small image. ImageIO reads the embedded JPEG in 12 to 22 ms per file. LightCraft also shows the embedded JPEGs first. Then it replaces them with thumbnails that it develops from the RAW data. Its grid is not finished until those thumbnails arrive.
For memory, each app keeps three photos ready: the current photo and its two neighbours. But the two apps keep different pixels.
LightCraft: 32-bit floating-point RGB. 2560 × 1707 × 12 bytes ≈ 52 MB a photo, 157 MB for three.
With 12 bytes for each pixel, LightCraft's sliders can use the full range of the sensor: highlights above white, and exposure changes without banding. LightPhotos gets finished 8-bit pixels from ImageIO. The totals are almost the same, and on screen the photos look the same.
The app size also comes from ImageIO. LightCraft includes its own decoders for RAW, JPEG, JPEG XL and more, and its own colour management. Its release build also skips link-time optimisation and keeps some debug information. The macOS decoders add nothing to LightPhotos.
What each choice costs
- LightPhotos looks as Apple develops a photo. I calibrated LightPhotos on Linux, Windows and the browser to match ImageIO, so a photo looks very similar on each platform.
- LightPhotos on the Mac supports only the cameras that
Apple supports. A new camera works when Apple adds it. If ImageIO
decodes a file incorrectly, I cannot fix it.
CIRAWFilteruses the same engine, so it does not change this. LightPhotos on other platforms uses rawler, so it supports every camera that rawler supports. - LightCraft has one decoder everywhere. A photo looks the same on each platform, and LightCraft can fix any bug in its decoder. The cost is a larger app, and formats that it does not have yet. HEIC needs an HEVC decoder, and no pure-Rust one with a permissive license exists. CR3 files show only their embedded preview for now.
Wrapping up
On the Mac, LightPhotos relies on the operating system, and LightCraft relies on itself. LightPhotos is smaller, starts faster, fills its grid faster and uses a little less memory. Apple did that work, and a smaller feature set fits in a smaller app. Both apps prepare the photos on each side, so both are equally fast through a shoot. LightCraft looks the same everywhere and does much more when you start to edit.