Brightroom: a composable photo editor built on Core Image and Metal
đź“· A composable image editor using Core Image and Metal.
At a glance
- What is it?
- Brightroom is a Swift package that splits a photo editor into three products: a Photos-style UI, an editing session layer, and a parametric rendering engine. It suits iOS 17+ apps that need non-destructive edits and Metal-backed export.
- Who is it for?
- Adopt Brightroom if you are building an iOS 17+ photo editor and want non-destructive, parametric edits with a Metal-backed renderer, or if you only need the engine on macOS 14+ through BrightroomParametric. Skip it if you need a cross-platform editor, a server-side pipeline, or a Swift toolchain older than 6.3.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly Swift, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Brightroom solves, and who it is for
Most photo editors bake adjustments into pixels as the user drags a slider. Revisiting a crop or changing an exposure value then means re-editing a degraded image. Brightroom takes the opposite route: the README states that edits are kept as parameters, so you can change adjustments, revisit crops, and render again from the original image. The source image stays separate from the document.
The package is aimed at Swift developers building iOS photo apps. The README lists three products with different scopes. BrightroomUI provides a Photos-style editor and reusable SwiftUI editing components for iOS 17+. BrightroomEngine covers image loading, editing state and history, and asynchronous rendering, also iOS 17+. BrightroomParametric holds typed features, masks, document persistence, and Core Image rendering with no third-party dependencies, on iOS 17+ and macOS 14+. That split matters: an app that only needs to apply a stored recipe to images can take BrightroomParametric alone and skip UIKit entirely.
How the parametric engine evaluates a document
The engine models an edit as a Swift value tree. According to the README, features are evaluated in order, and each feature receives the preceding feature's output. The source image stays separate from the document, so the tree is data, not pixels.
The rendering call returns a lazy CIImage recipe rather than a bitmap. The README's example builds an EditingDocument with a MainTree containing a single ExposureFeature, then passes it to ParametricImageRenderer().makeImage(from:document:). To get pixels you need a CIContext, or you can use ParametricExportRenderer to render a document to an image or a file. Custom effects are added by conforming to ImageEffectFeatureType, and selective edits combine an EffectPipeline with a MaskTree inside a LocalAdjustmentFeature. Persistence goes through ParametricDocumentCodec.
That laziness is the interesting design decision. It keeps intermediate results out of memory until you ask for them, but it also means the returned CIImage is a description of work, not work already done. Code that expects a materialized image at that point will be wrong.
Installing Brightroom and wiring the Photos-style editor
Brightroom ships as a Swift package. The README gives two ways in: add https://github.com/FluidGroup/Brightroom.git in Xcode's package dependencies, or declare it in Package.swift. The example uses a minimum version of 5.0.0.
dependencies: [
.package(url: "https://github.com/FluidGroup/Brightroom.git", from: "5.0.0")
]For a photo editor, add BrightroomUI and BrightroomEngine to your app target. In a Swift package target the README shows product dependencies by name.
.target(
name: "MyApp",
dependencies: [
.product(name: "BrightroomUI", package: "Brightroom"),
.product(name: "BrightroomEngine", package: "Brightroom"),
]
)For processing without the editor, add BrightroomParametric instead. The package requires Swift 6.3 or later and uses Swift 6 language mode, so the Xcode toolchain has to include Swift 6.3 or later. The built-in UI uses UIKit internally, and the README notes that UIKit apps can present it through UIHostingController.
A first real use is the editing session. EditingStack owns the session, PhotosCropEditingModel connects it to the built-in crop, filter, adjustment, and blur controls, and the README says to keep both instances alive for the lifetime of the editor. The view starts image preparation automatically.
let stack = EditingStack(imageProvider: .init(image: image))
let model = PhotosCropEditingModel(editingStack: stack)Exporting goes through the stack's renderer. The README's sample calls editingStack.makeRenderer().render() and reads rendered.uiImage from the result. To export large images without a full-resolution bitmap in memory, pass a file output instead.
let rendered = try await editingStack.makeRenderer().render(
options: .init(
output: .file(url: outputURL, fileType: .heif(quality: 0.9))
)
)The file-backed result exposes fileURL and thumbnail(maxPixelSize:). Asking for cgImage or uiImage from it decodes the full image back into memory, which defeats the point of the file output.
The lifetime rule that will bite you first
The README is explicit that EditingStack and PhotosCropEditingModel must stay alive for the lifetime of the editor. That is a real constraint, not a footnote. In SwiftUI, holding them as local values inside a view builder or recreating them on every body evaluation breaks the session. The documented pattern is to store both in @State and initialize them once in the view's init, as the PhotoEditor example does.
The same section notes that you create a new editor session when selecting a different source image. There is no documented mechanism for swapping the image inside an existing stack. If your UI lets the user move between photos without dismissing the editor, you are writing that lifecycle yourself.
A second boundary is the file-backed export path. The README warns that requesting cgImage or uiImage from a file-backed result decodes the full image back into memory. File export only saves memory if the rest of your code respects it and works from fileURL or thumbnail(maxPixelSize:).
Where Brightroom is the wrong tool
Brightroom is an Apple-platform package. The product table lists iOS 17+ for BrightroomUI and BrightroomEngine, and iOS 17+ plus macOS 14+ for BrightroomParametric. There is no Android, web, or server target in the README, and no headless Linux story. A team that needs one editing pipeline across mobile and backend cannot use this as the shared layer.
The Swift 6.3 requirement is a second hard edge. An app pinned to an older Xcode toolchain cannot adopt the current release without moving its toolchain first, and the package uses Swift 6 language mode, so strict concurrency applies to how you call into it.
Third, the README does not document rollback, undo semantics, or migration of persisted documents between major versions. ParametricDocumentCodec is named as the way to save edit parameters, but the README does not describe a versioning or compatibility policy for those documents. If you store editing documents on a server and upgrade the package, check the release notes for 5.0.0 and 5.1.0 before assuming old documents still load. The README is silent on that point.
BrightroomParametric against a plain CIFilter chain
The obvious alternative is to skip the package and chain CIFilters directly. The difference is state and persistence. A hand-rolled filter chain applies operations in code order and produces an image; nothing in it survives as data unless you write that serialization yourself. Brightroom's document is a value tree of typed features with masks, evaluated in order, and ParametricDocumentCodec exists to save and reload it.
The trade-off runs the other way for small jobs. If you need one fixed filter applied once at import time, the document model, the feature protocol, and the renderer are overhead you will not use. Conforming to ImageEffectFeatureType buys you composability you may not need. For a single unconditional color transform, a CIFilter chain is less machinery.
The other comparison is inside the package. BrightroomUI gives you a finished Photos-style editor with crop, rotate, straighten, filter presets, color adjustment, and blur masks. SwiftUICropView is the starting point when you want a custom layout and controls, with EditingStack still managing state, history, and rendering. Choosing the built-in editor is a commitment to its interaction model; choosing SwiftUICropView is a commitment to building the controls.
Maintenance, licence, and upgrade cost
The repository is not archived, and the last push was on 2026-09-23, the same day as the 5.1.0 release. The release history shows 5.1.0 and 5.0.0 one day apart, with 4.0.0-beta.1 in May 2026. Two major-version lines landing within a day suggests the 5.x series was still settling at the time of writing, so pinning an exact version rather than a range is the safer default for a shipping app.
Brightroom is MIT licensed. That is a permissive licence, and it permits use in closed-source products, but the repository's LICENSE file is the authoritative text and this article is not legal advice.
Upgrade cost concentrates in two places. First, the Swift 6.3 and Swift 6 language mode requirement means toolchain upgrades are coupled to package upgrades. Second, persisted edit parameters are the durable artifact: the README names ParametricDocumentCodec for saving them but does not document a document-format versioning policy, so a major-version bump is the moment to check the release notes before shipping a migration to existing users.
Editorial conclusion
Adopt Brightroom if you are building an iOS 17+ photo editor and want non-destructive, parametric edits with a Metal-backed renderer, or if you only need the engine on macOS 14+ through BrightroomParametric. Skip it if you need a cross-platform editor, a server-side pipeline, or a Swift toolchain older than 6.3. Before committing, verify that your Xcode toolchain ships Swift 6.3, that keeping EditingStack and PhotosCropEditingModel alive for the editor's lifetime fits your view lifecycle, and that file-backed export with .heif(quality:) is wired up if you handle large images.
Frequently asked questions
Which platforms does Brightroom support?
BrightroomUI and BrightroomEngine target iOS 17+, while BrightroomParametric targets iOS 17+ and macOS 14+. The built-in UI uses UIKit internally, so UIKit apps present it through UIHostingController.
What Swift version does Brightroom require?
The README states the Swift package requires Swift 6.3 or later and uses Swift 6 language mode, so the Xcode toolchain must include Swift 6.3 or later.
Can I use Brightroom without its built-in editor UI?
Yes. Adding BrightroomParametric gives you typed features, masks, document persistence, and Core Image rendering with no third-party dependencies, and the README notes the parametric engine can be used independently, including applying documents to video frames through AVFoundation compositions.
How do I export a large image without holding the full bitmap in memory?
Pass a file output to the renderer, for example .file(url: outputURL, fileType: .heif(quality: 0.9)), and work from fileURL or thumbnail(maxPixelSize:). The README warns that requesting cgImage or uiImage from a file-backed result decodes the full image back into memory.
What licence is Brightroom released under?
The repository lists the MIT licence, and the LICENSE file in the repository is the authoritative text.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/fluidgroup-brightroom)