PhotoCraft 0.16.0

Design

PhotoCraft

v0.16.0, released this week

A free, open-source image editor laid out like Photoshop.

FreeOpen sourceFree, no paid tier or account. Early alpha (v0.2)macOSUpdated 2 days ago

Rate PhotoCraft
No ratings yet20 views in the last 30 days

About

PhotoCraft is a layer-based image editor whose panels, tools and shortcuts follow Photoshop closely, so years of muscle memory carry over. It has layer masks, adjustment layers, layer styles, smart objects, type, vectors and brushes, plus more than 70 filters with live preview and AI selection that runs on the Mac itself. It opens and saves layered PSD and PSB files and works in 8, 16 or 32 bits per channel across RGB, CMYK, Lab and grayscale with ICC color management. Written from scratch in Rust and drawn with Metal. Still an early alpha, so keep the originals of anything important.

Price
Free, Free, no paid tier or account. Early alpha (v0.2)
License
Apache-2.0
Version
0.16.0 · Oct 25, 2026
Platforms
macOS
Category
Design

Featured badge

Make PhotoCraft yours: show the badge on its site and the listing marks itself as linked back.

Featured on MacGrove badge for PhotoCraft
HTML
<a href="https://macgrove.app/apps/photocraft"> <img src="https://macgrove.app/badge/photocraft.svg" alt="Featured on MacGrove" width="269" height="32" /> </a>
Markdown
[![Featured on MacGrove](https://macgrove.app/badge/photocraft.svg)](https://macgrove.app/apps/photocraft)

What’s new

All 16 releases

0.16.0LatestBig update · 2 days ago

  • Reuse filled-shape effect maps while moving styled groups
  • fix(ui-egui): a click on the bottom layer's Smart Filters selects them again
  • security(paint): cap spaced steps and procedural primitives
  • i18n(zh-hans): translate status messages and preview labels
  • perf(liquify): reuse interpolation taps and bound render buffers
  • perf(mcp): move command list rows instead of cloning

140 more changes

Earlier

  • 0.15.1fix(psd): keep pattern budget charges on parse failure
    • fix(psd): keep pattern budget charges on parse failure
    • fix(algo): avoid repeated copies of duplicate EXIF text
    • fix(paint): validate gray tile dimensions safely
    • fix(text): bound untrusted font and PSD text processing
    • Select and Mask: tool edits as select.refineEdge params (engine, part 1 of 2)
    • fix(cms): confirm ICC profile identity by bytes, not the 64-bit hash …
    • fix(vector): bound hostile geometry and raster work
    • fix: restrict droplet shim permissions, explain test range (SonarCloud)
    • Linked files: resolve stored paths with craft-docpath's resolve_absolute
    • CI: no debug info for dependencies, nextest in xtask ci, long sweeps spread over days
    • security(affinity): bound shared-object expansion work and metadata
    • release: the git-sources gate moves into craft-common-actions (storytold/craft-common-actions#78)
    • packaging: use [[ for bash conditional tests (SonarCloud S7688)
    • CI: the Linux gate runs through the suite's shared gate workflow
    • security(raster,doc): tile counts, shared rebuilt tiles, slice merge index
    • security(ipp): total reply deadline, resumable chunk walk
    • security: bisect table/stop lookups; charge ICC curves; bound PSD/RAW buffers
    • security(algo): bound working sets sized by caller-controlled extents
    • security(compose): bound dense working sets and nested Blend If evaluation
    • Tests: missing translations don't fail; xtask ci lists them
    • security(codecs): bound truncated JPEG recovery; count WebP frame ICC copies
    • security(plugins): host keys, band count, start fuel, FIFO open
    • security(gpu,ui-egui): bound clipped group planning; pattern swatch sampling
    • CI: cargo xtask ci is the gate, and the workflow runs it
    • xtask: the panic hunt without nextest builds with the HEIF decoder
    • ci(sonar): stop cancelling main-branch runs so coverage is published
    • deps: roxmltree 0.21 in photocraft-io and photocraft-engine
    • packaging(macos): retry stapling when Apple's ticket lookup drops
  • 0.14.0> [!NOTE] > FreeBSD aarch64 is dropped from this release. We had to cut releases, and we cannot build this target within the 5-hour CI limit for any of the Craft apps, so we are dropping it for now. This is a known blocker, flagged for a future fix in storytold/craft-common-actions#39. FreeBSD x86_64 and every other platform are included.

    > [!NOTE] > FreeBSD aarch64 is dropped from this release. We had to cut releases, and we cannot build this target within the 5-hour CI limit for any of the Craft apps, so we are dropping it for now. This is a known blocker, flagged for a future fix in storytold/craft-common-actions#39. FreeBSD x86_64 and every other platform are included.

    • Automation: tighten workspace checks for export, linked files and dialogs
    • View › Extras (Cmd+H) hides the Move tool's transform controls
    • fix(raster): prevent overflow in strided row sampling
    • security: index slice IDs in PSD slices parser
    • security: preflight PCX and XPM allocations
    • security: harden native temporary and recovery files
    • security: admit embedded SVG raster images by header size
    • security: stop double decompression of gzip-compressed SVG
    • @mercuryVM made their first contribution in
  • 0.13.0> [!NOTE] > FreeBSD aarch64 is dropped from this release. We had to cut releases, and we cannot build this target within the 5-hour CI limit for any of the Craft apps, so we are dropping it for now. This is a known blocker, flagged for a future fix in storytold/craft-common-actions#39. FreeBSD x86_64 and every other platform are included.

    > [!NOTE] > FreeBSD aarch64 is dropped from this release. We had to cut releases, and we cannot build this target within the 5-hour CI limit for any of the Craft apps, so we are dropping it for now. This is a known blocker, flagged for a future fix in storytold/craft-common-actions#39. FreeBSD x86_64 and every other platform are included.

    • Fix lag when dragging a floating selection with the Move tool
    • Open GIMP XCF files: layers, groups, masks, blend modes, channels and selection
    • Layers Panel: flyout, options and thumbnail sizes (part 1/3)
    • Layers Panel: Smart Filters mask follows thumbnail size, sub-rows stay compact (part 2/3)
    • Fill layers: match masks in creation and Pattern Fill preview (part 3/3)
    • Integration batch: #8229, David's follow-ups and 85 FredApps-approved PRs on main
    • Vanishing Point: perspective clone samples without allocating per pixel
    • Actions: a background filter's step is recorded only by the recording it started in
    • Camera raw: a developed raw keeps its EXIF (RW2 included), so File Info shows its Camera Data
    • Displace on a Smart Object: the map is embedded in the smart filter, so it displaces
    • feat(ui): search font input field sticky on scroll
    • Free Transform: a result over the pixel limit says so, not "not enough memory"
    • Free Transform: a result may take what Memory Usage allows, not 2^30 px
    • Scale layer-effect noise by coverage so it follows the glow
    • Shortcuts: Ctrl+F4 closes the document beside Ctrl+W (Windows/Linux)
    • Lock Slices: Slice Select can't select a locked slice, and both slice tools say why
    • Slice Select: the selected slice's handles resize it
    • Filter Gallery › Halftone Pattern: a tone-keeping screen at 45°
    • Smart Blur: 3.5× faster at radius 12 with identical output
    • Fixed Size marquee: Width and Height in any length unit
    • Light themes outline the slider knob (UI-217-11, #7927)
    • Note tool: a selected note shows its author and colour in the options bar
    • Actions: Emboss saves to and loads from .atn as Photoshop's Embs step
    • Fix File Info showing 1/1 s for exposures just under one second
    • Slice Select gets its own icon: the Slice knife with a pointer
    • perf(raster): tile content bounds compare rows in blocks (P32, #211)
    • Windows on ARM: auto picks DirectX 12 for Qualcomm graphics
    • Open: a file without type never waits for the font scan
    • automation: edit.installedProfiles is no host-path command
    • fix(liquify): keep frozen pixels stable near mask boundaries
    • Codecs: Fix corrupted CMYK JPEG exports
    • Pencil: align 1 px click snapping with the line walker
    • fix(psd): pad Gradient Map blocks
    • CodeRabbit follow-ups for the #8095 batch
    • PSD layer comps: an entry without enab inherits the previous visibility, as in Photoshop
    • Integration batch: 8 PRs on main
    • Content-Aware Move: live preview for large selections (TOOL-213-4, #6680)
    • Actions that start with Deselect play on when nothing is selected
    • A layer that doesn't fit in GPU memory no longer breaks GPU drawing: older pages are freed and the layer is retried
    • HEIF: honour the nclx colour description; HLG/PQ HEICs open as linear BT.2020 32-bit
    • test(ui): a toolbar click with no prior hover picks the tool (part of #2603)
    • Background redraw: keep the last finished composites, so a solo's restore uploads instead of recompositing (part of #6528)
    • Web: one press per tap, so menus open again on touch
    • Shortcuts: custom bindings on PageUp/PageDown (and other multi-word keys) fire again
    • Selections don't mark a document modified
    • Actions: record File › Export › Export As and replay it with its settings
    • Brushes: folders with search matches can be collapsed while searching
    • macOS: keep the bundle's Dock icon after the window opens
    • Keyboard Shortcuts: Shift is recorded when capturing a shortcut
    • Freeform Pen: ⇧ constrains freehand strokes to straight 45° lines
    • Color panel: RGB slider mode
    • test(engine): make the define-brush mask allocation fail on its own (fail-after-one)
    • Content-Aware Fill workspace: an erased sampling area previews as an error, and a failed OK keeps it open
    • fix(atn): a preset-only Photo Filter step exports its resolved colour
    • Auto-Blend Layers: turn the Background into a layer before masking it
    • Menu bar: Alt held through a shortcut no longer takes the keyboard, so Enter reaches the dialog
    • Presets: an Export As preset survives a damaged sibling set (test for #6906)
    • Preserve saved PCraft previews when manifest replacement fails
    • Actions: stop when a Save step's document is closed by a Save Document script
    • Toolbar: colour chips stay centred when the tools scroll
    • Studio: the floating Properties card has the Adjustments tab
    • Flatpak: a second launch hands its files to the running PhotoCraft over a Unix socket
    • Actions: Minimum / Maximum with Preserve: Roundness is no longer exported as Squareness
    • Show Technique and Range in the glow Layer Style pages
    • Open the selection menu on a Quick Selection right-click
    • Carry on an open path from its end with the Pen
    • Move typed text with a drag from just outside it
    • Move tool: a selection lifts up to a pixel's own coverage
    • perf(format): reuse .pcraft writers between document saves
    • fix(actions): preserve effective Stroke width in ATN export
    • fix(actions): refuse lossy fractional Ripple ATN export
    • Painting symmetry: canvas edits move the axis with the pixels
    • Actions: a Selective Color step keeps the values of its scalar form
    • Perspective Crop: Color Sampler points move with the image
    • Lasso: a short straight segment places a vertex instead of closing the outline
    • Save for Web: later slices cover earlier ones, covered slices split into pieces
    • Copy: text selected in an error notice is copied, not the layer
    • feat(toolbar): select grouped tools using press-drag-release
    • scorecard: five rows catch up with work already on main
    • .atn: a pinned corpus of real action sets, with byte-exact and load/save/reload tests
    • fix(screen-picker): cancel queued requests without stale replies
    • Fix mixed-order layered TIFF type geometry and unsafe text fallback
    • PSD Duotone: read and write the ink block (names, colours, curves)
    • Support .act color tables end to end (swatches + Color Table dialog)
    • IO: open .psd through the PSD reader, not codecs
    • Layers: Group Layers creates the group collapsed
    • Warp: apply a large layer's warp in the background, with progress and Esc to cancel
    • Move tool: ΔX/ΔY readout beside the pointer while dragging
    • Export As: Resample menu, incl. Nearest Neighbor
    • Shapes: Density and Feather on shape layers
    • Displace: OK asks for the displacement map, as in Photoshop
    • Image: Bitmap dialog with Output resolution and per-method settings
    • Text: apply anti-alias mode shaping when rasterizing type
    • BMP export: Indexed Color documents save as palette BMPs
    • Bevel & Emboss: keep the Gloss Contour's Anti-aliased (antialiasGloss)
    • Stroke: keep Overprint (overprint)
    • Zoom tool menu: Zoom In and Zoom Out last, as in Photoshop
    • refactor: share hsb conversions between photocraft-color and the color picker
    • feat(psd): editable Render smart filters (Clouds, Difference Clouds, Fibers, Lens Flare)
    • Performance: a cancelled Gaussian Blur job stops within budget
    • Preferences: Save in Background off saves in the foreground
    • Zoom tool: hold to zoom continuously with Animated Zoom on
    • Tools: each painting tool's brush persists across launches; restored tool options are clamped
    • GPU: pin the keep-overlap ordering with a tile-growth regression test
    • PSD: parameterless smart filters (Blur, Sharpen, Find Edges, Solarize…) open and save like Photoshop
    • PSD: Surface Blur, Smart Blur and Radial Blur smart filters open and save like Photoshop
    • PSD: Offset and Custom smart filters open and save like Photoshop
    • PSD: Mezzotint and Color Halftone smart filters open and save like Photoshop
    • New Document: Color Profile, the working space or another profile of the mode
    • PSD: Perspective Warp smart filters open and save like Photoshop
    • Artboards: layers outside the boards stay visible over the pasteboard
    • Survive a lost window GPU device: draw nothing, save unsaved work for recovery, close
    • fix(ui): preserve shape stroke settings at zero width
    • Type: commit the edit when another layer or document becomes active
    • Canvas overlays stay visible while panning
    • Tab in Full Screen Mode shows the menu bar and panels; leaving Full Screen restores them as before
    • Actions: .atn Select Subject, Select and Mask, Color Range, clipping and layer masks, colours map to commands (part of #6745)
    • Tests: generated large-file fixtures and a P20 number
    • security: prevent script injection in fuzz workflow (SonarCloud S7630)
    • test: non-blocking sleep in async test loop (SonarCloud rust:S7488)
    • security: enforce HTTPS on curl downloads (SonarCloud S6506)
    • ci: pin GitHub Actions to full commit SHA (SonarCloud S7637)
    • refactor(atn): build the clipping-mask and three adjustment events with macros (SonarCloud duplication gate)
    • Smart Filters: distinct editors, shared disclosure and stable alpha
    • README: the app icon above the title
    • security: cap uncached pattern conversion
    • security: bound dense mask working set
    • security: bound XCF copied layer metadata
    • security: bound PDN layer-name copies
    • security: bound cumulative retained RAW tile memory
    • security: cap lossless-JPEG Huffman table builds
    • security: apply caller limits to RAW preview decoding
    • security: interpolate RAW gain maps per pixel, not per full row
    • security: position RW2 bit cursor in constant time
    • security: reject tiled Sony cRAW and cap row references
    • security: bound TIFF structure-dump output
    • security: admit HEIF coded image size, not just transformed
    • security: admit deep-EXR output size and validate chunks before indexing
    • security: budget Cryptomatte per-pixel pair capacity
    • security: bound inverse-curve and CLUT interpolation work
    • security: compute auto-slice coverage per band, not per cell
    • security: check filter-effects padding length before scanning
    • security: bound Affinity raw child-slot traversal work
    • security: index Affinity live-corner radii by record
    • security: index Affinity object fields for O(1) lookup
    • security: validate corpus paths and bound symlink traversal in xtask
    • security: count GIF frame delays with a map
    • security: convert spelling offsets in a single pass
    • security: stop quadratic rescans in XMP value scanning
    • security: dedup Cryptomatte keys and cache stream lists
    • security: deduplicate ATN action names in near-linear time
    • security: index FITS header keywords for O(1) lookup
    • security: index XCF parasite names for duplicate replacement
    • security: index comp IDs in PSD layer-comp import
    • security: bound RAW metadata array reads
    • security: bound nested automation playback
    • security: encode printer names in IPP requests
    • ci(freebsd): run the photocraft-gpu tests one at a time
    • Fixes #8519 quick export honours extension
    • Format the quick export test
    • Backlog batch: 271 reviewed contributor PRs
    • Fix Fixed Size marquee drag positioning, building on #6026
    • fix(ui): pin the document tabs' » button to the strip's right end (part of #8290)
    • fix(actions): Hue/Saturation uses Photoshop's hue key H in .atn files
    • New Document: labels show the arrow, not a text cursor
    • Compose: effect maps that don't fit in the effect cache are built over the render rect, not rebuilt whole on every render
    • i18n(pt-br): translate 131 runtime labels that still showed in English
    • Save: a tab whose file another save writes over is detached and marked unsaved
    • Open/Save: refuse file names that aren't valid Unicode instead of rewriting them
    • Alt held during a drag is no menu-bar tap, so Enter commits Free Transform
    • Liquify: keep short strokes whole so undo removes only the last
    • Compose: tone and hue-range lookups truncate instead of calling floor
    • Fix tooltip text in scrub_label function
    • Panels: the dock can be dragged as wide as the window allows, not only 520 points
    • Quick Export: preserve floating-point EXR output
    • Compose: adjustment layers blend back and round without per-sample calls
    • fix(psd): an edited type layer updates the Photoshop-era Txt2 objects too
    • fix(ui): grey out Select and Mask in the options bar without a selection
    • fix(ui): Browse… buttons for dialog folder and plug-in path fields
    • fix(info): Info panel CMYK uses the Color Picker's colour-managed conversion
    • fix(swatches): a CMYK swatch keeps its inks as the foreground
    • fix(ui): menu titles switch on hover at the very top screen edge
    • feat(ui): Brushes tab preview-size slider for grid cells and list rows
    • Layers panel: mark adjustment layers kept as imported but not rendered
    • fix(actions): .atn export of Image Size with resample none no longer adds a pixel resize
    • fix(paths): the Work Path is drawn only while selected or a path tool is active
    • fix(ui): a unit after a range bound in a params doc no longer resets the dialog range (part of #7598)
    • docs(parity): Crop rows in ui-parity.md match the code and the scorecard
    • Preferences: the theme swatch star no longer grows the row on hover
    • Warp: a surface drag commits exactly as previewed, now under test
    • Preferences: list the languages alphabetically by their own name
    • Color Lookup lists the LUTs in a Preferences folder
    • UI: Control-click opens the right-click menus on macOS
    • Alt + wheel zooms while Alt is held, even when the modifier state misses it
    • Plug-ins: a reduced live preview scales the settings a manifest marks "unit": "px"
    • Add interactive drag positioning to print preview
    • Clipboard: a panic while reading the clipboard is reported, not a crash (refs #7582)
    • CPU canvas: a large document shows its own pixels at 100%
    • Keep Batch results when the action saves and then closes the document
    • Pen: ⌥/Alt-drag grabs the last anchor's incoming handle too
    • Object Selection: preserve Shift/Alt from mouse-down
    • fix(engine): Remove Background looks for the subject in the layer, not the canvas
    • docs(architecture): drop references to the removed rust-framework-options.md
    • test(codecs): cover exact TGA packet and encoder input boundaries
    • test(io): cover PSD palette boundaries and bitmap row packing
    • test(algo): cover deinterlace choices and NTSC signal limits
    • test(algo): cover Custom convolution bounds, padding and depth semantics
    • Dialogs: Preferences' title no longer sits against an inner outline
    • contributors: add adi-1108
    • docs(support): Canvas Size added a white border around my photo
    • test(ui): cover tool memory persistence
    • Keyboard Shortcuts: the dialog switches sets through the engine too
    • fix(ui): Free Transform corners show the diagonal resize arrow on wide or tall boxes
    • MCP stdio: a line that is not UTF-8 gets a parse error instead of ending the session
    • Layers: per-layer XMP metadata (layer.xmpMetadata), stored as Photoshop stores it
    • fix(atn): reject non-replace select.rect modes in .atn export
    • Fix: add missing Chinese translations for "Up" and "To"
    • Auto-Blend Layers: mask and blend the whole layers, past the canvas too
    • Camera Raw: choose the demosaic method when a raw opens
    • Camera Raw: the full-resolution preview of a raw open shows the raw-stage sliders
    • CLI convert: refuse to write over the input file unless --in-place
    • Refuse strict .atn export of Apply Image with a blending mask
    • Move tool: moving selected pixels shrinks the selection, transform controls and snapping to them
    • Refuse strict .atn export of a mask/channel-targeted Free Transform
    • fix(atn): keep a Transform Selection's interpolation; refuse what the transform descriptor can't carry
    • fix(atn): refuse strict export of canvas-bound selection modifies and placed pattern buckets
    • Refuse .atn export of crops with output size or resolution
    • Lasso tools: the outline is solid until the selection is made
    • Refuse strict ATN export of Select and Mask with brush strokes
    • Previous / Next / First / Last Brush commands (, . ⇧, ⇧.)
    • Fade the alpha channel or Quick Mask an edit changed
    • Record one action call when playback pauses for a step dialog
    • Map the bevel's shading through its gloss contour, not its light
    • Refuse .atn export of resolution-only Image Size without keepPrintSize
    • Preferences: Zoom Resizes Windows resizes floating document windows, and the Zoom tool shows it as Resize Windows to Fit
    • File Info: print a slow shutter speed as the fraction a camera writes…
    • Export As: show the size the file will have
    • fix(actions): refuse lossy fractional Crystallize ATN export
    • Actions: a setting Photoshop's event cannot hold refuses the step
    • fix(ui): guides draw over the Puppet Warp, Perspective Warp and Vanishing Point previews
    • Guides: show the X or Y position beside the pointer while dragging
    • Refuse strict .atn export of an in-place layer duplicate
    • Add a draggable center diagram for Radial Blur Zoom
    • Preferences › Raw Defaults › Resolution: a developed raw opens at it
    • PNG: correct sTER eye-order names without changing stored bytes
    • Don't paint from the sampled point when Alt is released mid-press
    • Print: an IPv6 print server is parsed as a host, not split on its last colon
    • Print: the no-listener test takes a port the OS says is free, not port 1
    • Brush: with airbrush build-up on, number keys set Flow and Shift+number sets Opacity
    • fix(touch) double tap jitter
    • Save As JPEG: JPEG Options dialog (quality 0–12, Baseline / Optimized / Progressive) (FILE-215-19)
    • Slice Select: Delete deletes the selected slice, not the layer
    • Liquify: Alt-drag brush resize keeps the brush in place and no longer paints a dab
    • Create Rectangle and the other shape dialogs: right-click Width or Height to pick their unit
    • perf(liquify): parallel dab nodes and source read, P30 −13–15 %, same pixels
    • Layers: the pointer over a thumbnail shows what ⌘-click loads, with the +/−/× badge
    • fix(transform): keep artboard paper below child previews
    • Type: hovering a served font fetches it after a short rest, not as the pointer passes
    • Align: a lone layer in an artboard aligns to that artboard
    • Fix dropdown menus opening behind modal dialogs on macOS trackpad tap
    • Decode numeric character references in File Info XMP text
    • Preferences: say Rendering Mode applies at next launch, next to it
    • fix(geom): reject extreme warp split indices without overflow
    • perf(raster): read 16- and 32-bit rows into RGBA with the colour model resolved per row
    • Keep previous crop length when normalizing rounds to zero
    • Keep '#' inside quoted .cube TITLE when parsing
    • Snapping: artboard edges and centres are snap targets
    • Layers panel: a group's layers indent past the group
    • fix(format): make Windows native-save publication durable
    • Page Up/Down, Home and End scroll the canvas, as in Photoshop
    • Write DOMAIN_MIN/DOMAIN_MAX in the .cube writer
    • Preferences › File Handling: Do not append "copy" to filename when saving a copy
    • fix(liquify): include cubic overshoot in source reads
    • fix(type): resolve exact PostScript names in type.create
    • Reject '+' signs in color picker hex field
    • Compose: Color Balance works out each channel's curve once, not per pixel
    • Layers panel: a click on the empty space below the layers deselects them
    • Find small objects in large documents with the Object Finder
    • macOS: Beep When Done plays the system alert sound (NSBeep) instead of a terminal bell
    • Puppet warp: Delete and Backspace remove the selected pin
    • Puppet warp: Fix discrepancy between preview and the final state
    • Intel Iris Xe on DX12: no more red blocks in brush previews and tool icons
    • Type: paragraph direction buttons in the Paragraph panel and options …
    • feat(ui): System Info shows the graphics card's own memory
    • docs(contributors): add Gotix-eg to people.toml
    • Actions: Shift-click layer select in .atn imports as range select and exports back
    • Title bar: only the primary button starts a window drag
    • Object Finder: hovering with an empty active layer shows the status-bar hint

    -…

Source
GitHub releases
Last checked