An on-device photo editor for Mac, coming to iPhone.
Apple's Vision framework reads your photograph. Deterministic code writes the edit.
Everything runs on your machine — your photographs never leave it.
Signed and notarised · macOS 14+ · Apple silicon · about 5 MB
Drop in a photo. Kelvin reads the scene and hands you three or four finished edits to choose between. Pick one, tune it, apply it across the shoot.
One frame, four candidate edits. Not four filters over the same numbers — four readings of the photograph, each computed from its own histogram and EXIF.
Most tools give you one answer, or ask you to teach them your style with thousands of past edits first, or repaint your pixels with a generative model.
Kelvin reads one photograph and offers a few defensible interpretations of it. You pick. That choice is the point of the whole app.
The idea underneath: whatever reads the scene never chooses numbers. It answers categorical
questions — is there a subject, is it a person, an animal, a sea stack. Every actual value is then
computed by ordinary, tested code from the histogram, the EXIF and the masks. Ask a model for
exposure: +0.37 and it will invent one, confidently, and you will never know which answers were
guesses.
That reader used to be a 1.7 GB vision-language model. Measured against real before-and-after edits, Apple's own Vision framework does the job better — on both of the project's test corpora — in a tenth of a second instead of five, with nothing to download (D27). So that is what reads your photograph now.
Works today
- The full editor: the adjustment set, colour mixer and geometry, with a mask kit — subject, sky, radial, graduated, brush, colour range, luminance, skin, per-person
- On-device scene reading, shown in the panel so you can see what it made of the photograph
- Candidate generation: three or four finished edits per photograph — and C hands the canvas over to them, two up or four up at full size, because "this one or that one" is a question anyone can answer and squinting at thumbnails is not
- Culling: keep/reject, a filmstrip that groups a shoot by burst, day, place or near-duplicate, and a scan that marks the sharpest frame of each burst
- Batch apply across a folder, and exporting every photo you edited in one go — JPEG, HEIC, PNG or 16-bit TIFF, at full size or a chosen long edge, in sRGB, Display P3 or Adobe RGB
- Sharing a finished frame to Messages or AirDrop through the same render the export path uses, with location off by default and offered only when the frame actually carries it
- Non-destructive throughout: your originals are never written to
Not yet
- Kelvin does not learn from which candidate you pick. The pick is recorded; nothing reads
it (see
docs/DECISIONS.mdD18 for why the learner was dropped rather than finished) - Auto-masks can be refined and inverted, but not brushed by hand
- Nothing generative, on purpose
The core and the app each have their own test suite, and CI runs them on every pull request.
The scene reading is shown rather than hidden — "landscape · golden hour · object", and a sentence explaining what it saw. If it read the photograph wrongly, you can tell at a glance.
A shoot grouped by capture day. It also groups by burst, by place, or by near-duplicate — and under burst or near-duplicate it marks the sharpest frame of each run.
Download the latest release — a signed, notarised disk image. Drag it to Applications and open it; there is no right-click-Open dance and no Gatekeeper warning.
It is about 5 MB. Scenes are read by Apple's Vision framework, which is part of macOS, so there is no model to download — not on first run, not ever — and no photograph you open is sent anywhere to be read. (Until 0.9 the app carried a 1.7 GB model inside it; see D27 for why it no longer does.)
The link above always resolves to the newest release. Nothing on this page names a version, so nothing here can quietly go stale the way a pinned download link does.
macOS 14+, Xcode 16.3+.
make build && make test
make appThe iPhone app is generated with XcodeGen:
scripts/make-iphone-project.sh, then open Apps/KelvinPhone/KelvinPhone.xcodeproj.
CONTRIBUTING.md has the details and the gotchas.
Opens RAW, JPEG, HEIC, PNG and TIFF.
The editing and the scene reading both happen locally. There's no account, no telemetry, and your photographs are never uploaded. Two small things do use the network — an update check, and place names for geotagged photos — and both are listed below and switchable off. If your library already lives in iCloud then it lives in iCloud — that's your setup, and Kelvin neither adds to it nor takes it away.
Scene reading uses Apple's Vision framework, which is part of macOS, so reading a photograph needs no network and nothing is ever downloaded to do it. A release asks the network two things, both of them switchable off in Settings. The first is whether an update exists. It does that on its own, because an alpha whose fixes only reach the people who agreed to a dialog is an alpha that stays broken for everyone else — the check sends no account, no identifier and nothing about your photographs, and both switches are in Settings ▸ General if you would rather it did not.
The second is place names. If a photograph carries GPS coordinates, Kelvin asks Apple what that place is called, so the filmstrip can group your shoot under “Sunriver, Oregon” rather than under a pair of numbers. The coordinate goes; the photograph does not — not the pixels, not the filename, not an identifier. It is rounded to about 110 m before it is sent, each place is looked up once and remembered, and the switch is in Settings ▸ Scene reading under Network. Turn it off and no coordinate ever leaves your Mac.
That is the whole list. Everything that reads or edits a photograph happens on your machine, which is the promise this app is actually making — your pictures are processed here, not uploaded to be processed somewhere else.
Your originals are never modified: edits are kept in Kelvin's own folder, never beside your files. The one thing that touches them is culling — you can move rejected frames to the Trash from the filmstrip, which asks first and puts them in the Finder's Trash so you can get them back. Exports carry the original metadata by default, including location — there's a switch in the export panel to strip it, with tests that read the file back to check.
Swift 6 and SwiftUI. Core Image for RAW decoding and rendering, so camera profiles come from Apple. Apple's Vision framework for reading the scene and finding subjects, skies and faces. Edits are stored as small JSON recipes.
Apple platforms only, by choice — the Mac app today and an iPhone app in progress, sharing one engine.
See docs/DECISIONS.md.
| CONTRIBUTING | Before your first build or patch |
| docs/ARCHITECTURE | How the pipeline fits together |
| docs/RECIPE-SCHEMA | The data model |
| docs/DECISIONS | Why things are the way they are — read before proposing changes |
| docs/EVALUATION | How edit quality is measured |
| docs/RELEASING | Building a signed, notarised release |
Bug reports and reproductions are welcome and carry no licensing question. For patches, see CONTRIBUTING.md.
If Kelvin earns a place in your workflow, you can sponsor its development. Sponsors are thanked by name inside the app — the list ships in each release, because Kelvin doesn't phone home, not even for a thank-you.
Kelvin is written by its owner working with Claude, and the commit history says so. That history is kept rather than squashed — it records what was measured and why decisions went the way they did.
AGPL-3.0-only. If you build on this and ship it — as an app or as a service — your source has to be open too.
Contributions are covered by a contributor licence agreement: you keep your copyright, and
the maintainer keeps the ability to relicense. Reasoning in docs/DECISIONS.md (D8).
Copyright © 2026 William Wallace.


