8972565249 LR-PROF-IMPORT: import the user's own DNG camera profiles (.dcp) (#1063)
* LR-PROF-IMPORT: import the user's own DNG camera profiles (.dcp)

profile.import (File > Import Profiles & Presets, drag & drop) now also
reads DNG Camera Profiles: the user's own .dcp files, e.g. made with a
colour-checker tool. They appear in the profile browser and menu under
Camera Profiles with dcp:<camera>-<file> ids; profiles.camera lists them
and profile.deleteCamera / profile.deleteImported remove them. A library
on disk keeps a copy in Profiles/. No profile ships with the app and the
repository holds none: tests build theirs in memory (write_dcp).

lightcraft_raw::dcp parses the file per DNG spec ch. 6 (magic RC in
place of 42; IFD 0 read through the existing DNG colour builders) and
DcpProfile::applied_to puts its matrices, illuminants and look in place
of a raw file's, keeping the shot's white balance, analog balance and
baseline exposure.

Choosing one (develop.profile) replaces the raw file's colour model when
it is decoded, before the starting colour and the white-balance scale
(WbScaleOf) are worked out, so the matrix path takes over from the fitted
JPEG look (CR3/CR2/ARW...) and white balance follows the profile. A
profile without a tone curve of its own (most chart-made ones) keeps the
photo's contrast: the DNG's tone curve, else the tone of the camera-JPEG
fit. The profile travels with SourceRef::File into FileLoader /
PairLoader (new Option<&DcpProfile> argument); a decoded source records
the profile it was made with and is not reused for another. Smart
previews and the web render worker keep the file's own colour; rendered
(non-raw) photos ignore it.

This reverses the documented "Adobe profile formats deliberately
unsupported" stance for user-supplied files only; docs updated.

Split out of #1012.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-PROF-IMPORT: build against upstream main (kelvin_presets, 3-arg loaders)

DcpProfile::applied_to keeps the file's white-balance presets (new
ColorData::kelvin_presets upstream): they are camera facts like as-shot
neutral. Upstream's new FileLoader test closures take the profile argument.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-PROF-IMPORT: hover previews and browser thumbnails develop the hovered camera profile

The loupe's hover preview (preview_job) and the profile browser's thumbnails (variant_job) took
the photo's own decoded source, so hovering an imported camera profile showed the built-in colour
(and hovering a built-in profile while one was applied kept the camera profile's). Both now decode
as the hovered settings develop: the source follows the settings' camera profile, as the main
render does, and a variant's cache key already follows its settings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-PROF-IMPORT: build against upstream main (face-tile test loader and render_bench take the camera profile)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* CI: the catalog's staging-link test module is Unix-only (unused import broke clippy on Windows)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* CI: same-folder export test expects the '/'-joined subfolder (failed on Windows)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* CI: folder-drop tests build widget ids with the Folders tree's '/' paths (failed on Windows)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-PROF-IMPORT: rustfmt after merging main

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-PROF-IMPORT: a real D750 camera profile as a pinned test corpus, checked against Lightroom

dieli's CC0 Nikon D750 ColorChecker set (Calibrite PROFILER dual-illuminant DCP,
matrix-only; the two chart NEFs; Lightroom Classic 15.6 renders with the profile)
is fetched by cargo xtask corpus --download into corpus/d750-colorchecker/, pinned
to commit f37f4054 and by sha256; nothing is committed.

Tests (skip without the corpus): the profile parses as matrix-only with
Cool White Fluorescent + D55; the matrices blend by inverse temperature; the
matrix-only path inverts the colour matrix and adapts to D50; it takes over the
chart NEFs' colour; imported and applied end to end, the charts develop within
mean dE2000 3.84 (flash) / 3.35 (indoor) of Lightroom's renders.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-PROF-IMPORT: a single .dcp file is a camera profile only, not also an unreadable LUT

profile.import read every plain file that was neither a folder nor a .zip as a .cube LUT, so importing
one .dcp imported the camera profile and also reported it as failed ("stream did not contain valid
UTF-8"). Only .cube files are read as LUTs now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-PROF-IMPORT: review fixes for imported camera profiles

- A library moved or restored elsewhere finds its camera profiles in its own Profiles/ folder.
- A grid thumbnail is cut from the decoded preview only when that preview used the same camera profile.
- Render and source cache keys carry a fingerprint of the profile file, so a deleted or replaced profile never
  has its old renders stand in.
- Camera profiles can be favourites, and favourites/recent entries survive reopening the library.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-PROF-IMPORT: smart previews without the camera profile, current-profile source facts, unsupported files reported

CodeRabbit review of 4ef025d9:
- Build Smart Previews develops the proxy without the photo's imported camera profile and never takes a cached
  source decoded with it (the denoised picture is kept); before, only the stale-proxy refresh left it out.
- Session::source_info reports decoder facts only from a cached source developed with the camera profile the
  photo uses now, else the catalog's.
- profile.import reports a file that is neither .cube, .dcp nor .zip as failed instead of passing over it.

Each with a regression test in tests_dcp.rs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-PROF-IMPORT: the Before side decodes with its own camera profile; a failed profile copy fails that file only

- build_job (and region_job_before through it) picks the Before render's source with the Before
  settings, so it never reuses or decodes a source developed with the edit's camera profile.
- profile.import: a .dcp whose copy into the library fails is reported in `failed`; the profiles
  imported with it stay imported and are saved, and nothing half-written is left.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-PROF-IMPORT: a cached smart preview stands in while the original is offline

A smart preview carries no camera profile, so with an imported profile the profile check
rejected the cached proxy and every render decoded it again (it gives the same picture). It
now stands in, for its own level and for the thumbnail cut from it, while the original is
known to be offline; otherwise the profile check is as before, so once the original is back
the photo is decoded from it with its profile. Photos without an imported profile keep main's
behaviour. Test `tests_dcp::a_cached_smart_preview_stands_in_while_the_original_is_offline`
(fails without the fix).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-PROF-IMPORT: deleting a camera profile removes the library's copy before forgetting it

profile.deleteCamera now removes the library's copy of the profile file
first. A copy that is already gone is fine; any other error (file in use,
no access) fails the command and keeps the profile listed, so the library
never holds a profile file it no longer lists. Test:
a_profile_whose_copy_cannot_be_deleted_stays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-PROF-IMPORT: deleting a camera profile never follows `..` out of the profiles folder

A listed path with a `..` component can start with the profiles folder and
still lead out of it; such a path is not treated as the library's copy, so
deleting the profile leaves that file alone. Test:
deleting_a_profile_never_follows_dot_dot_out_of_the_profiles_folder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-PROF-IMPORT: both tone fits of a DNG see its lens opcodes; no side effect in the profile-tag comparison

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-PROF-IMPORT: a hover preview's key carries the camera profile file's fingerprint

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-PROF-IMPORT: unusable camera profiles not offered; file's WB scale and the profile's tone kept

Review of #1063 (CodeRabbit, outside-diff findings):
- a camera profile whose file is missing or damaged when the library opens
  is no longer offered in the profile browser or accepted by develop.profile
  (it silently developed with the raw's own colour); the entry stays, so it is
  offered again once the file is back.
- a raw on the relative white-balance scale stays relative with a camera
  profile (its matrices turned it to Kelvin, so existing edits were read as
  absolute temperatures and the catalog and quick previews disagreed).
- with a camera profile its tone curve, then the tone kept for a curve-less
  profile, come before the fitted camera look's tone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-PROF-IMPORT: one Camera Profiles group; the web loader keeps a raw's own colour

Review of #1063 (CodeRabbit):
- a LUT group named "Camera Profiles" that didn't sort last made the profile
  menu list that group twice; it is now one group, last, with both kinds.
- without web workers a render ran inline and the web loader applied the
  selected camera profile, unlike the worker (the documented web behaviour
  keeps the raw's own colour); the loader now leaves it out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-PROF-IMPORT: camera profile files are read at most to the 64 MiB limit

A .dcp larger than the parser accepts was read whole into memory before
being refused. One bounded read (lightcraft_raw::dcp::read_dcp) stops one
byte past the limit and is used for import, reopening a library and the
duplicate check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-PROF-IMPORT: a test that a hostile hue/saturation map header in a camera profile drops the map (FredApps, #1063)

Dimensions far past the data, past u32 products or zero, an entry count of 4 billion values and a
data offset past the end each drop the map while the profile's matrix still reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Docs: parity checklist keeps both sides of the merge

* LR-PROF-IMPORT: tests added on main take the file loader's camera-profile argument

* Merge main; a thumbnail test takes the file loader's camera-profile argument

* Docs: one camera colour row, with main's notes and the import note

* Process key tests take the profile salt argument

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-05 07:09:30 +01:00
2026-11-02 20:41:34 -05:00
2026-11-02 20:41:34 -05:00
2026-11-02 20:41:34 -05:00
2026-10-31 02:30:12 +00:00
2026-11-02 20:41:34 -05:00
2026-10-12 23:50:09 -07:00

ArtCraft

LightCraft icon: an engraved lynx on an amber tile

LightCraft

Your photos. Your pixels. Your machine.

English · 简体中文 · 繁體中文

Photo library and raw development; an open-source, non-destructive photo library and raw developer, built in pure Rust.
Native on macOS, Windows and Linux. In the browser via WebAssembly. Drivable end to end by AI agents over MCP.

Pure Rust macOS, Windows, Linux and Web MCP server included License: MIT OR Apache-2.0 Status: young and moving fast
Scanned by SonarCloud Reviewed by CodeRabbit

Join the ArtCraft community on Discord

LightCraft on getartcraft.com · ArtCraft · All Crafting Apps


LightCraft's Edit view with Ansel Adams' The Tetons and the Snake River in the loupe, the Light and Effects panels open on the right, and the four showcase photos in the filmstrip
Ansel Adams, "The Tetons and the Snake River" (1942). Public domain, U.S. National Archives. Developed in LightCraft.

Note

ArtCraft is a community of artists from all walks of life. Digital, generative, music, games — if you make things, you're one of us. Come say hi on Discord.

Editing · Color grading · Before & after · Masking · Presets · Library · Agents & MCP · Performance · Status · Quick start · Roadmap · Downloads · Crafting Apps


Edit like you mean it

LightCraft is a complete darkroom in a single native app. Every adjustment is non-destructive, so your originals are never touched. Every slider renders through a scene-referred, wide-gamut, 32-bit float pipeline: highlights roll off like film, shadows open up without halos, and colour stays clean from capture to export.

☀️ Light

Exposure, Contrast, Highlights, Shadows, Whites, Blacks, with edge-aware local tone mapping (a guided filter on log-luminance). Pulling −100 Highlights recovers a blown sky without the grey halos you'd get from a naive curve.

🎨 Color

White balance by temperature and tint (Kelvin for raw, relative for JPEG) with presets, Auto and a click-to-neutralise eyedropper. Vibrance that protects skin tones, Saturation, an 8-band Color Mixer (hue / saturation / luminance) and 3-way Color Grading wheels with blending and balance. All of it is computed in OkLCh, a modern perceptual colour space.

✨ Effects

Texture for fine detail, Clarity for mid-tone punch, Dehaze (dark-channel prior with guided refinement; push it negative to add atmosphere), post-crop Vignette with highlight priority, roundness and feather, and resolution-independent film Grain with size and roughness.

📈 Tone Curve

Parametric region curve with movable splits plus point curves for RGB, Red, Green and Blue. Curves are monotone by construction, so you never get an accidental tone inversion.

The Tone Curve open under the Light panel, with a gentle S-curve on the RGB point curve applied to The Tetons and the Snake River
A gentle S on the RGB point curve, right under the Light sliders. Ansel Adams, 1942 (public domain).


Color grading, the cinematic way

Split-tone shadows, midtones and highlights independently with drag-anywhere colour wheels. Below, Dorothea Lange's Migrant Mother gets a warm, print-like tone (highlights at 42°, shadows at 28°) in two drags.

Color Grading wheels for midtones, shadows and highlights next to Dorothea Lange's Migrant Mother, toned warm like a print
Shadows, midtones and highlights wheels in the Color panel. Dorothea Lange, "Migrant Mother" (1936). Public domain, Library of Congress.


Before & after

Press \ to toggle the Before view, Y for side by side, or Shift+Y for a split view. Every image below is a real screenshot of LightCraft, captured automatically by an agent through the control channel.

Side-by-side before and after of The Tetons and the Snake River: the after has deeper clouds and more open shadows along the river
The Tetons and the Snake River. Highlights −45, Shadows +38, Clarity +28, Dehaze +18. Ansel Adams, 1942 (public domain).
Side-by-side before and after of Migrant Mother: the after is warmer, with lifted shadows
Migrant Mother. Shadows +42, Texture +18, split-toned grade, vignette. Dorothea Lange, 1936 (public domain).
Side-by-side before and after of Earthrise: the Earth over the lunar horizon, the after slightly warmer and richer
Earthrise. Dehaze +22, Highlights −30, warmer white balance, Vibrance +22. NASA / Bill Anders, Apollo 8, 1968 (public domain).
Side-by-side before and after of The Blue Marble: the after has deeper blacks and firmer cloud detail
The Blue Marble. Highlights −38, Blacks −20, Dehaze +15, Vibrance +30. NASA, Apollo 17, 1972 (public domain).

Masking that goes where you point

Paint with a Brush (size, feather, flow, density, erase), drop Linear and Radial Gradients with draggable pins, or select by Luminance Range, Color Range, Sky, Subject and Background. Combine components with Add / Subtract / Intersect, invert any of them, and dial in 17 local adjustments per mask (Temp, Tint, Exposure, Contrast, Highlights, Shadows, Whites, Blacks, Texture, Clarity, Dehaze, Hue, Saturation, Sharpness, Noise, Moiré, Defringe), plus an overall Amount.

Desktop Object and Describe masks use SAM 3 for click-based and text-prompt selection, with a separately installed model; see model setup and terms. Subject, Sky and Background selections still use classical heuristics. Existing model-generated masks render and export without the model installed.

Masking panel with a linear Sky mask and a radial Sun glow mask; the radial gradient is drawn as a red overlay around the sun on a lake scene
A radial "Sun glow" mask (Temp +40, Exposure +0.50) layered over a linear "Sky" mask. Photo from LightCraft's procedurally generated demo library.


Presets, profiles & the Color Mixer

Forty-one hand-built presets ship in the box (Golden Hour, Teal & Orange, Faded Matte, Selenium Tone, Crisp Landscape and more), each with an Amount slider from 0 to 200 %. Save your own from any group of settings, mark favourites, and copy, paste or sync edits across a whole selection with exactly the groups you choose.

Presets column grouped into B&W, Color, Film, Landscape, Portrait and Style, beside the Color panel with the 8-band Color Mixer open on purple
The Presets column next to the 8-band Color Mixer. Photo from the demo library.

✂️ Crop, straighten & geometry

Free or locked aspect ratios (1:1, 4:5, 5:7, 2:3, 4:3, 16:9, 16:10, original), drag-to-rotate straightening that always keeps the largest crop inside the image, thirds / grid / golden-ratio overlays, flips and 90° rotations, plus manual Vertical, Horizontal, Rotate, Aspect, Scale and Offset transforms.

⚫️ Black & White

One click (V) to monochrome, with an 8-band B&W Mix that lets you darken skies or make foliage glow. Then tone it with Color Grading for selenium, sepia or split-tone prints.

Crop tool on The Tetons and the Snake River: a 16 by 9 crop rotated slightly, with a rule-of-thirds overlay and the Geometry sliders on the right
A 16:9 crop straightened +2.5° with the thirds overlay. Ansel Adams, 1942 (public domain).
Split before and after view of sand dunes: color on the left, black and white with grain and vignette on the right
Split view: colour original on the left, B&W with Clarity, Vignette and Grain on the right. Demo library.

Organize everything

A library that stays out of your way: All Photos, Recently Added, Picks, By Date, Albums nested in Folders, and Recently Deleted. Rate with 0–5, flag with P / X / U, colour-label with 6–9. Search understands fields: rating:>3 flag:pick iso:>800 camera:x2 date:2026-04 keyword:mountains person:"Jane Doe"; plain words also match the people named on faces, and quotes keep several words together. Every view sorts by capture date, import date, edit date, name, rating, size or at random (a stable shuffle; View → Sort → Reshuffle for a new one). The justified Photo Grid and Square Grid views are virtualized, so they stay smooth whether you have forty photos or forty thousand.

The People view groups named faces, including names read from XMP; face boxes can be edited in the loupe. On desktop, automatic detection and recognition use optional models installed through Settings → Faces; no weights are bundled. See face models and setup.

Justified Photo Grid of 24 demo photos with ratings and flags, and a sidebar of nested albums under a Travel 2026 folder
Photo Grid with albums nested in a folder, ratings and flags. Demo library.
Square Grid showing the four public-domain showcase photos with star ratings and pick flags
Square Grid of the four public-domain showcase photos.
Info panel for Earthrise showing file name, 2400 by 2400 JPEG dimensions, rating, title field and camera metadata
The Info panel: file, dimensions, rating, title, caption, copyright and camera metadata. NASA / Bill Anders, "Earthrise", Apollo 8, 1968 (public domain).

Built for agents

Every menu item, slider, brush stroke, crop handle and keystroke in LightCraft is a command with a stable id and JSON parameters. The UI, the keyboard, the CLI, a JSON-lines control channel and an MCP server all dispatch through the same entry point. An agent can cull a shoot, develop it, mask a sky and export it, and see the result.

lightcraft --control 7980 ~/Pictures/trip
{"method": "engine.execute", "params": {"command": "photo.flag",  "params": {"flag": "pick"}}}
{"method": "engine.execute", "params": {"command": "develop.set", "params": {"values": {"light.highlights": -45, "light.shadows": 38}}}}
{"method": "engine.execute", "params": {"command": "mask.add",    "params": {"kind": "radial", "center": [0.62, 0.4], "rx": 0.2, "ry": 0.14}}}
{"method": "ui.clickWidget",   "params": {"id": "slider:effects.clarity"}}       // drive any widget by name
{"method": "ui.pointer",       "params": {"events": [{"kind":"down","x":0.2,"y":0.3}, {"kind":"up","x":0.4,"y":0.3}]}}
{"method": "ui.screenshot",    "params": {"path": "after.png"}}
  • Command registry. engine.commands lists the available commands; develop.controls lists every slider's range, default and current value.

  • MCP server. lightcraft-cli mcp exposes the command registry to Claude (or any MCP client), alongside helpers for import, query, develop, render (returned as an image) and export. Masks have no helper of their own: create and edit them through run_command (e.g. mask.add). It runs headless, or attached to the running app with screenshots, clicks and gestures. See docs/mcp.md.

    cargo build --release -p lightcraft-cli
    claude mcp add lightcraft -- "$PWD/target/release/lightcraft-cli" mcp ~/Pictures/shoot          # headless
    claude mcp add lightcraft-app -- "$PWD/target/release/lightcraft-cli" mcp --connect 127.0.0.1:7980  # live app
    
  • Scriptable CLI: lightcraft-cli run --import in.dng develop.set control=light.exposure value=0.7 app.export path=out.jpg longEdge=2048 runs any chain of commands (headless, on a saved library, or against the running app) and prints one JSON result per command; lightcraft-cli render in.dng -o out.jpg --set light.exposure=0.7 --preset ….

  • Undo for everything, including agent actions: a slider drag (or a scripted burst of updates) is one undo step.

  • Contact sheets (native app / CLI): select photos, then File → Contact Sheet PDF…. Choose A4 or Letter, landscape, rows/columns and filename captions; save a paginated PDF and print it from your PDF viewer. Exports use the current edits, fit each photo without further cropping, and render at 150 dpi in sRGB. The background export supports cancellation and protects originals and XMP sidecars. From the CLI:

    lightcraft-cli run --import ~/Pictures/shoot library.selectAll export.contactSheet path=Contact.pdf paper=a4 columns=3 rows=4 captions=true
    

    export.contactSheet also accepts explicit ids, landscape=true, and paper=letter. It replaces an existing output only after the entire PDF succeeds. Limits: 1,000 photos, 100 pages, 256 MiB; captions are rasterized with the same font coverage as watermarks (CJK requires craft-fonts), with long names shortened in the middle. This is a contact-sheet workflow, not a printer-driver or custom picture-package layout editor.

  • Print layouts (native app / CLI): File → Print… opens the Print module's layout editor: Single Image, Contact Sheet, Picture Package (Auto Layout packs cells onto pages) and Custom Package (drag and resize cells, stacking order, snap to cells or a grid), Zoom to Fill / Rotate to Fit, Photo Border and Inner Stroke, paper sizes from 4 × 6 to 13 × 19 and A3 to A5 or custom, margins, rulers in in/cm/mm/pt, page background, 72–720 ppi, and saved templates. Print to PDF… renders in the background. Everything is a print.* command (print.get, print.set, print.cell.add, print.layout.autoLayout, print.exportPdf, print.template.save…), so the CLI and MCP drive it too:

    lightcraft-cli run --import ~/Pictures/shoot print.set 'layout={"style":"picturePackage","page":{"paper":"8x10"}}' \
        print.cell.add width=5 height=7 print.cell.add width=2.5 height=3.5 print.layout.autoLayout print.exportPdf path=Package.pdf
    

    The Print Job panel supports printer/paper ICC profiles, rendering intent, black point compensation, print adjustment, sharpening and 8/16-bit PDF output. Direct printing through an OS print dialog and page overlays (identity plate, watermark, page numbers) are not there yet.

  • Slideshows: View → Slideshow plays the photos in view full screen, with fades, repeat and random order. Settings → Slideshow configures layouts, templates, backgrounds, overlays and title screens. Native exports support PDF, JPEG sequences and AV1 video in MP4 or WebM; music and a dedicated Slideshow module editor remain missing. See docs/slideshow.md.

  • Every widget is addressable (ui.widgets) and clickable by name, so agents operate the real UI, not a side door.

  • The screenshots in this README were produced end to end by the docs/showcase/ scripts. Protocol reference: docs/control-protocol.md.


Fast, native, private

  • Pure Rust, no C. Our own RAW decoders (DNG, Canon CR2/CR3, Sony ARW, Nikon NEF, Fujifilm RAF incl. X-Trans, Panasonic RW2 / Leica RWL, Pentax PEF, Olympus ORF), our own colour science, our own pipeline. JPEG, PNG, TIFF, WebP, PSD composites, JPEG XL, GIF (first frame) and BMP open today; HEIC/HEIF photos too with --features heif (HEVC is a build-time choice, as in PhotoCraft).
  • Scene-referred & wide-gamut. Linear Rec.2020 float internally, Bradford-adapted white balance, gamut mapping instead of clipping, a filmic shoulder for raw and pixel-exact pass-through for JPEGs you haven't touched.
  • Resolution-independent edits. Radii and brush sizes are relative to the image, so a 400 px preview, your 5K display and a 60 MP export look the same.
  • GPU-accelerated, CPU-exact. The whole develop pipeline runs as wgpu compute kernels (Metal / Vulkan / DX12), checked against the CPU pipeline to within 1/255. On a 24 MP raw (Apple M4 Pro): a slider update re-renders in ~4 ms, a cold 2.5 MP loupe in ~30 ms, and a full-size export in ~0.3 s including a parallel JPEG encode. Without a GPU the same pipeline runs on all CPU cores, redoing only the stages a slider affects. Batch exports render several photos side by side, so one's decode and encode overlap another's render.
  • Instant culling. Opening a raw shows its embedded camera preview or cached render within ~0.1 s while the full render follows (~0.2–0.5 s for 24 MP). The next and previous photos are prepared in the background, so stepping through a shoot takes ~50 ms per photo.
  • Background rendering. A worker pool renders the loupe, before/after and every visible thumbnail off the UI thread: drafts during drags, full quality on release.
  • Local-first. No account, no cloud, no telemetry, no subscription. Your catalog is an append-only log of human-readable operations you can diff, back up or replay.

Feature status

LightCraft is young and moving fast: alpha. Where we honestly stand (details in the roadmap and docs/target-app-parity.md):

  • By feature count we're at ~82% (core features 98%), tracked row by row in docs/parity-checklist.md.
  • As a day-to-day tool we're nearer 61% for the whole product, ~51% for a typical working photographer and ~65% for casual use. It's great for JPEG/DNG and most Sony / Panasonic / Fujifilm / Nikon / older-Canon raws on one machine; see docs/raw-parity.md for your camera.
  • The biggest gaps:
    • camera colour calibration: Sony, Nikon, Panasonic, Fujifilm and Canon CR3 and CRW raws have guarded estimates from their camera JPEGs, with built-in Canon EOS D60, ILCA-99M2, ILCE-7CR, ILCE-7M4, ILCE-7RM5, X-H2S, X-T4, Nikon Z 6II, Z 8 and D5300 profiles; measured calibration covers one model so far (chart-measured matrices for the Canon EOS D60), and other raws or rejected fits retain a neutral matrix;
    • compressed Olympus raws and unsupported CR3 variants: these use embedded JPEG previews when present. Fujifilm lossless/lossy compressed RAF now decodes sensor data; verification and existing-library reload instructions;
    • AI quality and model setup: Subject and Sky selection are classical heuristics; Object/Describe use an optional SAM 3 model. AI Denoise supports Bayer RAW with separately installed weights, while X-Trans and linear-RGB denoise and Super Resolution remain missing; see AI masks and AI Denoise;
    • network libraries: a preview reads the raw's header — or, for a CR3 or a TIFF raw whose preview tag sits past it, exactly the ranges the sparse planner names — and its embedded JPEG only (56.5 MB → 5.9 MB, 10 s → 1 s on the test share), but the developed picture still needs the sensor data over the wire; see docs/raw-preview-reads.md;
    • on-screen HDR display, video import/editing, and the Book / Map module views. Print layouts and colour-managed PDF output work; direct printer output and page overlays remain missing. Slideshow playback and export work, with a dedicated module editor and music still missing.
  • What's next: see the ranked gaps and the roadmap.
Area Status
Library: albums, folders, smart albums, stacks (incl. auto-stack), virtual copies, ratings, flags, labels, filter bar, search, sort, grids, filmstrip ✅
Culling: Compare (synced zoom) and Survey views, auto-advance, instant previews ✅
Light, Color, Effects (vignette styles), Tone Curve (+ refine saturation, targeted adjustment), Color Mixer (+ targeted), Point Color, Color Grading, Calibration, B&W ✅
Masking: brush, linear, radial, luminance/colour range, add/subtract/intersect; Object and Describe selections ✅ · Object/Describe require optional SAM 3; Subject/Sky remain heuristic (details)
Crop, straighten tool + auto straighten, flip, rotate, aspect ratios, overlays ✅
Profiles (Color, Neutral, Vivid, Landscape, Portrait, Monochrome: our own looks), presets, versions, history, copy/paste/sync settings ✅
Camera colour: DNG matrices, guarded camera-JPEG look fits and built-in profiles for selected cameras 🚧 measured calibration and per-model coverage remain incomplete (raw parity)
Native macOS menu bar (generated from the command registry), control channel + every widget addressable, headless UI snapshots ✅
RAW: DNG, CR2, CR3 (lossless CRX Bayer and version 0x100/0x200 C-RAW), ARW, NEF (uncompressed + lossless/lossy compressed, incl. lossy after split), Fujifilm RAF (uncompressed + lossless/lossy compressed, Bayer + X-Trans), Panasonic RW2 / Leica RWL / Panasonic RAW (every raw format, DMC-LX1 to DC-S1RM2), Pentax PEF, Olympus ORF (uncompressed, 12.8 bpp), Samsung SRW (uncompressed); embedded previews for every format incl. CR3 🚧 · other CR3 variants, compressed ORF / SRW and Nikon HE NEF decode ⬜ (raw parity)
Detail: sharpening, luminance + colour noise reduction ✅ · AI Denoise 🚧 (Bayer, user-installed weights; setup), Super Resolution ⬜
Remove / Heal / Clone spots (auto source), non-generative Fill, editable spot pins, Visualize Spots, Red Eye and Pet Eye ✅ · Fill uses PatchMatch; no generative fill
Export: JPEG / PNG / TIFF / flat PSD / WebP / AVIF / JPEG XL / raw or rendered DNG / original, sizing, file-size limit, output sharpening, naming templates, batch, metadata policy, text or image watermark ✅
HDR: HDR editing with a headroom limit, SDR rendition, Visualize HDR; gain map JPEG, PQ/HLG AVIF, PQ PNG/JPEG XL and float TIFF export ✅ · HDR display ⬜
Library persistence (crash-safe op log + snapshots, background compaction, failed saves reported), disk thumbnail cache ✅
Import: Add in place / Copy / Move, rename and folder templates, devices, duplicate detection, watched folders; Local folder browsing ✅
MCP server (headless or live app, persistent libraries), CLI, control channel ✅
XMP sidecars (read/write, auto-write), reading crs: develop settings, preset files (.lcpreset, XMP presets) ✅
Optics (distortion, vignetting, auto + manual CA, defringe, lens corrections embedded in DNG files, Panasonic / Leica RW2 / RWL distortion data and Nikon Z 6II / Z 8 / Z f distortion data), Geometry (transforms, Constrain Crop), Upright (Auto/Level/Vertical/Full/Guided) ✅ · camera lens profiles (our own) ⬜
Photo Merge: HDR (auto-align, deghost), Panorama (spherical/cylindrical/perspective, boundary warp, auto crop), HDR Panorama → DNG ✅
GPU pipeline (wgpu compute, CPU-exact within 1/255), CPU fallback on device limits / errors ✅ · WebGPU in the browser 🚧
People view, XMP face names and editable face boxes; automatic face detection and recognition ✅ · detection/recognition require optional models (setup)
GPX tracklog auto-tagging and offline reverse geocoding ✅ · place names require the GeoNames dataset; Map view ⬜
Print layouts, picture packages, templates and colour-managed PDF export ✅ layouts/PDF · 🚧 direct printer output and page overlays
Slideshow playback, layouts, overlays, templates and PDF/JPEG/AV1 export ✅ playback · 🚧 dedicated module editor and music (details)
Video import/editing and Book module ⬜
Web build (same UI in the browser via WASM): persistent library in OPFS/IndexedDB, Web Worker rendering, export downloads ✅ · WebGPU, Safari/Firefox testing 🚧

✅ works today · 🚧 in progress · ⬜ not started


Quick start

Workspace builds, including the desktop, web and CLI applications, require Rust 1.95 or newer.

git clone https://github.com/storytold/lightcraft && cd lightcraft
cargo run --release -p lightcraft                       # opens your library (~/Pictures/LightCraft Library; a new one starts with demo photos)
cargo run --release -p lightcraft -- ~/Pictures/trip    # import your photos (folders are scanned, duplicates skipped)
cargo run --release -p lightcraft -- --memory           # a throwaway in-memory demo session (writes nothing)
cargo run --release -p lightcraft -- --control 7980     # with the automation channel
cargo xtask web --serve                                 # the same app in the browser: http://127.0.0.1:8080/
cargo run --release -p lightcraft-cli -- render photo.jpg -o out.jpg --set light.exposure=0.5
cargo xtask ci                                          # fmt, clippy, HEIF, tests, parity, layers, assets, WASM
cargo xtask ci --quick                                  # only what your branch's changes can break (minutes)

cargo xtask ci --quick compares with the merge base of origin/main (--base REF for another) and runs fmt, clippy, the tests of the changed packages, layers and assets, plus HEIF, parity and the wasm check when the changes touch them; a change to Cargo.toml, Cargo.lock or the lint config tests everything. GitHub runs the full cargo xtask ci on every pull request (.github/workflows/ci.yml). Only one ci runs at a time per machine: a second one, from another worktree say, waits for the first and names it, since several at once take longer in total than in turn. LIGHTCRAFT_CI_SLOTS=2 allows two; LIGHTCRAFT_CI_LOCK=off skips the lock.

CI defaults to line-table debug information and one build job per 1.5 GB of RAM available when it starts (at most one per CPU; 4 if memory can't be read), so an 8 GB machine with 6 GB free builds with 4, and a busy machine with little free builds with fewer. Test threads follow, capped at 4. Full-debuginfo linkers, one per CPU, otherwise exhaust RAM before any test runs. GPU coverage is unchanged. Explicit CARGO_PROFILE_DEV_DEBUG, CARGO_BUILD_JOBS, RUST_TEST_THREADS and NEXTEST_TEST_THREADS settings override these defaults. The tests run under cargo test; LIGHTCRAFT_NEXTEST=1 runs them under cargo-nextest instead (one process per test, slower here because each regenerates the demo scenes). Ordinary development commands keep their own settings.

Release builds on memory-constrained machines: #1196 reports Arch/AUR release-link OOM kills on a 16 GB machine when the desktop and CLI build concurrently with full debug information, thin LTO and one codegen unit. The CI defaults above do not apply to ordinary cargo build --release. Start with one job; two may be suitable if enough memory is available:

CARGO_BUILD_JOBS=1 cargo build --release -p lightcraft -p lightcraft-cli
# Alternatively: CARGO_BUILD_JOBS=2 cargo build --release -p lightcraft -p lightcraft-cli

If a packaging environment enables full debug information or memory-heavy LTO settings, use command-local overrides for a lower-memory source build:

CARGO_BUILD_JOBS=1 CARGO_PROFILE_RELEASE_DEBUG=0 CARGO_PROFILE_RELEASE_LTO=false CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16 cargo build --release -p lightcraft -p lightcraft-cli

These POSIX-shell assignments affect only this command, not global Cargo configuration or the repository's release profiles. Disabling LTO may change performance and binary size; disabling debug information reduces debugging detail. This is build guidance, not a guarantee that any machine has enough memory. Downstream packagers should serialize large binary builds and avoid full debug information combined with LTO and one codegen unit on low-memory builders. A PKGBUILD that exports its own profile settings can override inherited variables; adjust the packaging recipe locally or contact its maintainer rather than assuming an environment prefix on makepkg will take precedence.

Linux/Arch build troubleshooting: rustc SIGILL with ThinLTO. In #2811, an Arch Linux x86_64 build on an AMD Ryzen 9 7900X3D with 64 GB RAM failed with rustc 1.99.0 / LLVM 23.1.1, AUR lightcraft 0.7.1-1 and 0.13.0-1, RUSTFLAGS="-C opt-level=3 -C target-cpu=native", and release settings opt-level=3, lto=thin, codegen-units=1. cargo build --frozen --release reached the executable crates before rustc crashed with signal 4 (SIGILL / ILL_ILLOPN). The core dumps point into LLVM's SelectionDAG / vector-combine optimization path; the exact compiler root cause has not been isolated. This does not mean every machine with that hardware or toolchain will fail. It is a different observed failure from the 16 GB OOM kills in #1196 above; there is no evidence establishing a shared root cause.

For a direct Cargo source build, the equivalent command-local LTO override is:

CARGO_PROFILE_RELEASE_LTO=off cargo build --frozen --release

Reporter @rwarmstr verified the workaround by exporting CARGO_PROFILE_RELEASE_LTO=off inside the AUR PKGBUILD's _srcenv() function and rebuilding both package versions, without changing RUSTFLAGS. The reporter tested the AUR recipe, not the direct Cargo invocation above. A variable prefixed to makepkg alone may be overwritten by the PKGBUILD's own exports; the recipe must honor or set the override. The AUR !lto opt-out behavior is tracked separately in #2813.

Cargo's LTO documentation distinguishes false from off: false can still perform thin local LTO across codegen units (unless codegen-units=1 or opt-level=0); off disables LTO explicitly. The low-memory recipe above is unchanged. This workaround is for affected builds, not a new default; disabling LTO may change performance, binary size and build behavior.

Tests that need a GPU, the corpus, the craft-fonts faces or a model pass without checking anything when those are missing (libtest has no "skipped" status), so cargo xtask ci lists them: tests record a skip with lightcraft_testkit::skip!, and the run ends with what the machine offered (OS, CPUs, RAM, GPU adapters per backend, corpus, fonts) and every skip by reason. It writes target/ci/report.json and target/ci/summary.md, a block to paste into a PR (--summary-md prints it); --hardware also runs the GPU tests on every backend that has a hardware adapter. Nothing is uploaded.

Chinese and Japanese text need the shared font repo, an optional build input (official releases always include it):

git clone https://github.com/storytold/craft-fonts ../craft-fonts
CRAFT_FONTS_DIR=../craft-fonts cargo run --release -p lightcraft

Without it LightCraft builds and runs the same, but Chinese and Japanese text have no glyphs. Fonts are never committed to this repo — what goes there is described in the craft-fonts README.

Interface language: Edit → Language (English, 简体中文, 繁體中文(台灣), 日本語, Português (Brasil), Español, Deutsch, Русский, Français, Українська, Polski, Čeština, Italiano, ქართული, Svenska, Suomi, Bahasa Indonesia, Tiếng Việt) or Settings → General → Language; the choice applies immediately and persists. See docs/localization.md.

Logs: the desktop app writes its log to standard error and to logs/lightcraft.log in its settings folder (Linux $XDG_CONFIG_HOME/lightcraft/logs/, by default ~/.config/lightcraft/logs/; macOS ~/Library/Application Support/LightCraft/logs/; Windows %APPDATA%\LightCraft\logs\), never in the library. Portable mode: a file named portable.txt (or LightCraft.portable) next to the executable moves the settings folder to LightCraftData beside it. A launch from a desktop menu or the Dock has no terminal, so attach this file to a bug report (Help ▸ Open Log Folder shows it in the file manager). Each start moves the previous log to lightcraft.1.log and that one to lightcraft.2.log, so the log of a run that crashed survives the next start; the file stops growing at 16 MiB, --version and --help write none, and runs with LIGHTCRAFT_NO_PREFS log to standard error only. By default LightCraft's own crates log at info and everything else at warn. LIGHTCRAFT_LOG=info or debug works as before (that level for LightCraft's own crates, warnings and errors from the rest; any other value: warnings and errors only) and wins over RUST_LOG, which otherwise replaces the default with env_logger-style directives such as RUST_LOG=debug or RUST_LOG=warn,lightcraft_pipeline=trace (a directive ending in * covers every target starting with it, as in lightcraft*=debug). Panics are recorded there too, and still in lightcraft-panics.log in the temp folder. The logger is apps/lightcraft/src/logging.rs.

The web build needs the wasm32-unknown-unknown target and the matching wasm-bindgen CLI (cargo xtask web prints the exact install command); see docs/web.md.

Nix builds the desktop app and lightcraft-cli (the Nix build always includes the craft-fonts input, so Japanese text has glyphs):

nix run github:storytold/lightcraft                    # the desktop app
nix build github:storytold/lightcraft                  # → ./result/bin/{lightcraft,lightcraft-cli}
nix develop github:storytold/lightcraft                # rust toolchain + native deps + fonts

On NixOS, run a cargo build from nix develop: outside it the app can't find libvulkan.so.1, finds no GPU and renders on the CPU, much more slowly (it says so once at start-up). nix run and nix build set the path themselves.

In a flake configuration (NixOS, home-manager, nix-darwin):

# flake.nix
inputs.lightcraft.url = "github:storytold/lightcraft";
# optional: build against your own nixpkgs instead of the one LightCraft pins
# inputs.lightcraft.inputs.nixpkgs.follows = "nixpkgs";

# then, in a NixOS or home-manager module (where `inputs` is in scope):
nixpkgs.overlays = [ inputs.lightcraft.overlays.default ];   # makes `pkgs.lightcraft` available
environment.systemPackages = [ pkgs.lightcraft ];           # home-manager: home.packages = [ pkgs.lightcraft ];

nix build installs the same desktop file, hicolor icons and AppStream metadata as the .deb/.rpm, and runs cargo test --workspace as its check phase (skip it with pkgs.lightcraft.overrideAttrs { doCheck = false; }).

Keyboard: G grid · D detail · E edit · C crop · M masking · Shift+P presets · \ original · Y before/after · Z zoom · J clipping · ⌘Z undo · ⌘/ all shortcuts.

How it's built

An engine-first Cargo workspace of small, tested crates with enforced layering (cargo xtask layers): geom, color, raster, tiff → raw, codecs, meta, develop → pipeline → catalog → engine → ui-egui. The egui frontend is one swappable crate; nothing below it knows a UI exists. mcp and the apps (lightcraft, lightcraft-cli) sit on top of engine.

Contributing

Humans and agents follow the same rules, so read AGENTS.md first. The short version:

  • Clean-room. Never read proprietary binaries or GPL raw/photo code (darktable, RawTherapee, LibRaw, rawspeed, dcraw…); work from public specs and black-box observation.
  • No proprietary assets, ever: no icons, screenshots, presets, profiles, LUTs or fonts from commercial products. Every image, icon and font in the repo is original, public domain, Creative Commons, OFL or permissively licensed, and has an entry in assets/ATTRIBUTION.md added in the same commit. New fonts go to storytold/craft-fonts, not here.
  • Pure Rust, enforced crate layering, everything is a command, and cargo xtask ci green before every commit (one task id per commit).
  • Never crash. Non-test code returns errors instead of panicking: no unwrap(), expect(), panic! or unsafe, checked indexing on anything derived from input, and a regression test with every crash fix. Details in AGENTS.md.

Questions, ideas or a bug you'd like to talk through first? Bring them to Discord.


Downloads

New to LightCraft? Download it from the LightCraft page on getartcraft.com. That's the easiest way to install it.

Want a specific build or format? On GitHub, the latest release has every build listed below, and all releases has earlier versions and their notes. <ver> in the file names is the version number, and SHA256SUMS.txt lists a checksum for every file.

Windows

Build Installer Portable
x64 (64-bit Intel/AMD) lightcraft-<ver>-windows-x64.msi lightcraft-<ver>-windows-x64-portable.zip
arm64 (Snapdragon and other ARM PCs) lightcraft-<ver>-windows-arm64.msi lightcraft-<ver>-windows-arm64-portable.zip
x86 (32-bit) lightcraft-<ver>-windows-x86.msi lightcraft-<ver>-windows-x86-portable.zip

Installers and executables are code-signed. The installer asks who to install for and where (for your account only, in %LOCALAPPDATA%\Programs\ArtCraft\LightCraft, by default; "Everyone on this computer" uses C:\Program Files\ArtCraft\LightCraft (Program Files (x86) for the 32-bit build); upgrades stay in the folder you chose) and ends on a page confirming LightCraft was installed, with an option to start it. For unattended installs: msiexec /i lightcraft-<ver>-windows-x64.msi /qn INSTALLFOLDER="D:\Apps\LightCraft\". A silent install is per-user by default. INSTALLFOLDER chooses the folder, not who it is installed for; add ALLUSERS=1, from an elevated terminal or deployment tool, for an all-users install.

Update or uninstall asks for an old .msi, or stops with error 1612 or 1714? Windows removes the installed version with its own cached copy of that version's installer; if the copy is gone, it asks for the original file and stops (an update then ends with error 1603). Download the release MSI of the version and build you have installed from all releases and run msiexec /fv "<path to that .msi>" /qb+ /norestart, approving the permission prompt if Windows shows one. That restores the cached copy; then retry the update or uninstall. Opening the MSI, or choosing Repair or Uninstall on it, may still fail. Error 1603 has other causes too; if this doesn't help, open an issue with the error message.

macOS

Build File Notes
App, universal (Apple silicon + Intel) lightcraft-<ver>-macos-universal.dmg Signed and notarized
Command-line tool, universal lightcraft-cli-<ver>-macos-universal.zip Signed and notarized

Linux

Format x86_64 aarch64 (ARM64) Notes
AppImage lightcraft-<ver>-linux-x86_64.AppImage lightcraft-<ver>-linux-aarch64.AppImage Portable; updates with AppImageUpdate (.zsync files)
Flatpak lightcraft-<ver>-linux-x86_64.flatpak lightcraft-<ver>-linux-aarch64.flatpak Sandboxed; flatpak install --user <file>
Debian/Ubuntu lightcraft-<ver>-linux-x86_64.deb lightcraft-<ver>-linux-aarch64.deb
Fedora/RHEL/openSUSE lightcraft-<ver>-linux-x86_64.rpm lightcraft-<ver>-linux-aarch64.rpm
Tarball lightcraft-<ver>-linux-x86_64.tar.gz lightcraft-<ver>-linux-aarch64.tar.gz Unpack anywhere

RISC-V (riscv64): lightcraft-<ver>-linux-riscv64.tar.gz, a tarball only (glibc 2.39 or newer, e.g. Ubuntu 24.04+).

On a Wayland desktop LightCraft runs through XWayland when it is available, because only X11 gives windows file drag-and-drop; set LIGHTCRAFT_WAYLAND=1 to use native Wayland (sharper fractional scaling, no drop).

FreeBSD

Build File
x86_64 lightcraft-<ver>-freebsd-x86_64.tar.gz

Web (WebAssembly)

Build File Notes
Static site lightcraft-web-<ver>.zip Runs in a modern browser; host it on any static server

The Crafting Apps

LightCraft is one of the Crafting Apps: free, open-source creative tools from the ArtCraft team, each written from scratch in Rust and each able to stand on its own.

App What it's for Code Learn more
PhotoCraft Image editing: layers, masks, type and real PSD files GitHub Website
VectorCraft Vector illustration GitHub Website
FilmCraft Video editing, color and sound GitHub Website
LightCraft Photo library and raw development GitHub Website
PdfCraft Reading, organizing and protecting PDFs GitHub Website
EffectCraft Motion graphics and visual effects GitHub Website
DesignCraft Page layout and publishing GitHub Website
FigCraft Interface design and prototyping GitHub Website
WordCraft Word processing and documents GitHub Website
SoundCraft Audio recording and editing GitHub Website
DeckCraft Presentations and slide decks GitHub Website
CADCraft Computer-aided design GitHub Website
GridCraft Spreadsheets and data GitHub Website
TextureCraft 3D texture painting GitHub Website
Craft Anything See how any software works, then build your own GitHub Website
SolidCraft Parametric 3D CAD by Tangency Labs (community app) GitHub

And ArtCraft itself, our AI image and video studio for artists who want real control.


Join the ArtCraft community on Discord

Come make things with us

Our Discord is where artists of every kind hang out: people who paint, shoot, draw, cut film, set type, and people still figuring out what they like to make. Share what you're working on, ask for help, tell us what's broken, or tell us what you wish these tools could do. Whatever your medium and however long you've been at it, you're welcome here.

discord.gg/artcraft · getartcraft.com · The Crafting Apps · LightCraft


License and credits

LightCraft is dual-licensed under MIT or Apache-2.0, at your option. Copyright (c) 2026 ArtCraft Team and the LightCraft contributors. Required notices are in NOTICE.

Bundled fonts, icons, images and other assets keep their own open licenses; each one is listed with its author, source and license in assets/ATTRIBUTION.md.

Showcase photographs are public-domain works, used via Wikimedia Commons: Ansel Adams, The Tetons and the Snake River (1942, U.S. National Archives); Dorothea Lange, Migrant Mother (1936, Library of Congress); Bill Anders / NASA, Earthrise (1968); NASA, The Blue Marble (1972). The demo library is procedurally generated by LightCraft. UI font: Inter (SIL OFL 1.1). Builds made with craft-fonts (all official releases) also embed its Chinese and Japanese fonts (Noto Sans CJK SC, BIZ UDPGothic, BIZ UDMincho, Shippori Mincho; SIL OFL 1.1), listed in its ATTRIBUTION.md. All icons are original.

The ArtCraft name, wordmark and logos in docs/brand/ are trademarks of the ArtCraft Team and are not covered by this license. They may be used only unmodified, and only as part of this repository and LightCraft, under docs/brand/LICENSE-brand.txt. Forks and modified versions must remove them.

ArtCraft
Made by the ArtCraft team and community.

Star history

Star History Chart

Library, Tools, and Presets panels can be regrouped, split, floated within the app window, closed, and reopened. Tools keeps the current Edit/Crop/Masking mode; the photo view and filmstrip stay together. Drag a panel header to a target edge or another header. Its context menu offers Float panel, Move to group, and Close panel. Existing panel toggles reopen panels at their saved location; Reset Panel Layout restores the app layout. Floating panels remain inside the current app window.

S
Description
No description provided
Readme
234 MiB
Languages
Rust 98.2%
WGSL 0.6%
Python 0.6%
PowerShell 0.3%
Shell 0.2%