Photoshop is the most-cloned application in creative software, and almost every clone has failed the same way: they rebuild the toolbar but not the object model, so layers stop behaving the moment you push past trivial edits. PhotoCraft, from the ArtCraft family of apps on getartcraft.com, takes the opposite route. It is a clean-room reimplementation of Photoshop’s engine semantics, written entirely in Rust with unsafe_code = "forbid" across the workspace, licensed MIT OR Apache-2.0, and shipped as version 0.6.0 of the storytold/photocraft repository. The workspace builds three frontends from one core: a native egui desktop app, a headless CLI, and a WebAssembly build that runs the same editor in the browser at 18.8 MiB, tuned by a dedicated wasm-release profile that keeps pixel crates at opt-level 3 while shrinking everything else.
What makes this repository unusual is not the feature list, although 16 adjustment layer types, layer styles, smart selections that run locally, and real PSD round-tripping are all present. It is the architectural discipline. Every user-visible action is a command with a stable, Photoshop-style identifier such as layer.newAdjustmentLayer.invert plus JSON parameters, and every frontend goes through one entry point: Session::execute. The egui UI, the CLI, a loopback JSON control channel, and an MCP server all drive the identical code path, so anything you can do with a mouse you can do from a script, and the README screenshots are rendered offscreen through that control channel rather than staged by hand.
The overview: three frontends converge on one engine facade, which owns the document model and drives two pixel backends that share the same tile storage. Automation reaches the engine from the outside without touching the UI.
Reading the overview from left to right:
- The three app frontends live in apps/photocraft/src, apps/photocraft-cli/src, and apps/photocraft-web, and the desktop shell wires GPU startup, tablet input, and monitor color profiling before the first window appears.
- The UI layer in crates/ui-egui/src holds panels, dialogs, and the canvas, but it holds no editing logic; it only issues commands.
- The engine facade in crates/engine/src is the single command registry every frontend calls, and it owns history, jobs, and document lifecycles.
- The document model in crates/doc/src is pure data: a layer tree where
children[0]is the bottom layer, matching PSD file order and compositing order, with adjustment, fill, text, shape, and smart-object kinds modeled from day one. - Two pixel backends, the CPU reference compositor in crates/compose/src and the wgpu compositor in crates/gpu/src, both read and write the sparse copy-on-write tile surfaces in crates/raster/src.
- The brush engine in crates/paint/src treats strokes as replayable data, and the automation layer in crates/automation/src wraps the engine as an MCP server, a JSON-lines RPC server, and a bridge into a running desktop app.
Why You Need This
First, you get your muscle memory back without giving up your files. The menus, shortcuts, panels, and tools sit where your hands expect them, from Ctrl-J to Shift-Ctrl-D, so the retraining cost is near zero. More importantly, your layered documents stay layered: the PSD codec in crates/psd/src/lib.rs was implemented clean-room from Adobe’s public file format specification, keeps unknown tagged blocks and image resources raw, and round-trips unmodified files byte for byte. The project reports that re-saving keeps the render of 307 of the 309 files in the psd-tools test suite, because a PSD that changes when you open and save it is a PSD you can no longer trust.
Second, your edits compound instead of burning in. Adjustment layers in PhotoCraft are real model objects, not a filter dialog with a preview: crates/doc/src/adjust.rs defines Curves with per-channel editing, Levels, Hue/Saturation, Black and White, Channel Mixer, Gradient Map, and more, and the compositor in crates/compose/src/adjust.rs applies them to the composite below the layer, masked and reorderable. The storage underneath makes this cheap: crates/raster/src/lib.rs builds an infinite plane of 256-by-256 tiles where tiles are Arc-shared and mutation copies only the touched tiles, so undo snapshots are pointer swaps and a brush stroke dirties only the tiles it touched. That is why crates/engine/src/history_cmds.rs can offer real undo without freezing the app.
Third, the whole editor is automatable from day one, not bolted on later. Because every action flows through Session::execute, the automation crate in crates/automation/src/lib.rs exposes the same editing surface three ways: PhotocraftMcp in crates/automation/src/server.rs runs an MCP server on the official rmcp SDK with session, document, and command tools; crates/automation/src/rpc.rs speaks a JSON-lines protocol over stdio or a loopback TCP port with methods like doc.open, doc.render, and batch that runs a list of steps with stopOnError; and crates/automation/src/bridge.rs forwards to a live desktop app so an agent can inspect, screenshot, and click the actual UI. The running app also hosts its own token-authed, loopback-only control server in apps/photocraft/src/control_server.rs, with connection limits and request size caps enforced in code.
The detail view: command modules dispatch by stable id, the document model stays free of rendering code, two compositors share tile storage, and the automation stack wraps the same session the UI uses.
The detail diagram repays a slow walk. Start at the engine: crates/engine/src/lib.rs states the contract in its first comment, that every frontend goes through Session::execute, and the registry in crates/engine/src/commands.rs dispatches domain modules like crates/engine/src/file_cmds.rs and crates/engine/src/layer_menu_cmds.rs by id. Those modules mutate the document model but never touch pixels directly; rendering is delegated. On the pixel side, the CPU compositor in crates/compose/src/lib.rs encodes Photoshop layer semantics in one place: blend modes with opacity times fill opacity, layer masks with density, clipping groups that composite atop their base, pass-through versus isolated groups, adjustment layers, and fill layers. It is labeled the reference implementation, and the GPU compositor must match it within 1/255 per channel.
The GPU side is the most impressive part of the codebase. crates/gpu/src/lib.rs keeps layer and mask surfaces resident in square pages, uploaded only when a chunk inside them is drawn, evicted least-recently-used when a byte budget is exceeded. crates/gpu/src/plan.rs compiles the layer tree into a linear list of passes over chunk-sized accumulators, and crates/gpu/src/fx.rs builds the maps for drop shadows, glows, satin, bevel, and stroke as fragment passes cached per layer state, rebuilding only tiles damaged by a brush stroke grown by the effect’s reach. Execution walks the canvas in 1024-square chunks handed straight to a display texture, so there is no readback and documents of any size composite within bounded memory. The brush engine feeding those tiles is equally principled: crates/paint/src/lib.rs models strokes as data, and crates/paint/src/dynamics.rs turns input points into dabs where every jitter is a hash of the brush seed and dab index rather than a random number, making strokes deterministic, replayable, and testable.
File I/O keeps the same standard. The PSD crate handles both PSD and PSB with all four compression methods, decodes channel data lazily, preserves padding, and parses and writes ActionDescriptors, the structured payloads Photoshop uses for automation. Standard formats go through crates/codecs/src/lib.rs and crates/io/src/lib.rs, camera RAW decoding lives in crates/raw/src/lib.rs to feed a Camera Raw style workflow, and layer styles from PSDs open as live, editable effects via the model in crates/doc/src/effects.rs.
From Install to First Composite
Building from source needs a recent Rust toolchain: the workspace pins edition 2024 and requires Rust 1.95 or newer. Clone the repository, run cargo run -p photocraft for the desktop app, or use the CLI for headless work: photocraft-cli starts from apps/photocraft-cli/src/lib.rs, and photocraft-cli serve runs the JSON-lines server on stdio or a loopback port. A productive first session: open a layered PSD, call doc.inspect to dump the layer tree, run a batch of commands that adds a Curves adjustment layer masked to an elliptical selection, then call doc.render for the composite as a base64 PNG. Agents get the same flow through MCP tools, and the bridge route can even screenshot and drive the live UI. Non-developers can grab installers from the ArtCraft site, and the web build runs in any modern browser via WebGPU.
Honest limits: this is an early alpha, and the project is candid about it. Compositing currently happens in display RGB with CMYK read through the document’s ICC profile, while mode-native CMYK and Lab compositing await the ICC milestone; the document model includes color modes and non-destructive kinds from day one even where rendering support lands later. The GPU compositor targets Metal, Vulkan, DX12, and WebGPU, so older machines fall back to software rendering. Feature parity is a moving target the project tracks openly, and the web build is size-capped by hosting limits. None of that changes the core evaluation: the command facade, the reference-versus-GPU compositor discipline, and the byte-faithful PSD codec are the hard parts done first, which is exactly the order a serious Photoshop replacement should choose. Enjoyed this post? Never miss out on future posts by following us