Scandium 1.0.2 · macOS · Windows · GPL-3.0

A free, open‑source image editor that stays out of your way.

Layers, masks and adjustment layers. Retouching, selections, filters. A headless engine you can script. Nothing leaves your machine — no network, no telemetry, no account.

macOS 15.4 or later · SHA-256 checksums on the release page

Step 1: the synthetic lake scene imported as a layer, with dust spots in its sky. Step 2: a layer of spheres pasted over the lake with the Overlay blend mode. Step 3: a mask made from a rectangular marquee hides the spheres outside the right half. Step 4: a sticker layer pasted and moved to the lower left. Step 5: Curves and Hue/Saturation adjustment layers brighten and saturate the scene. Step 6: a rectangle shape layer added at the top of the sky. Step 7: the dust spots are gone after fifteen Heal strokes. Step 8: the sticker layer after the Perspective transform; the History panel lists every step.
1Import lake.png

Seven layers, two of them adjustments, one masked, one in perspective — built in eight steps, each one a real screenshot, every one a row in the History panel on the right. Click a step, or let it play.

Built in the open

65 days. 1 high schooler. A fleet of agents.

Scandium went from its first commit to the 1.0 tag in 65 days, written by a fleet of Claude Code sessions with one human operator — a high-school student. Every figure on this page is measured from the repository and the session logs, not estimated.

65
days
11 Aug 2026 → 15 Oct 2026, first commit to 1.0.2
8,954
commits
on main at the 1.0.2 tree
245,709
lines of code
Rust 211,064 · C++ 13,008 · QML 8,183 · tooling 13,454
2,434
tests
plus 51 ctest suites and 102 command contracts
26.6 B
tokens
processed by the agent sessions — 42.9 M of them written
100 %
free and open source
GPL‑3.0 · no account, no telemetry, no network

Measured 15 Oct 2026 on the build machine. Days: first commit on main to the v1.0.2 commit. Lines: Rust, C++, QML and the Python/shell tooling in the tagged tree, with contracts, goldens, fixtures, i18n and reports excluded. Tokens: every input, output, cache-write and cache-read token in the agent sessions' own logs, de-duplicated by message; the Windows build sessions ran on another machine and are not counted, nor are the advisor's sessions or the 41 sessions started from the Projects root; the fleet's session logs begin on 26 Aug 2026.

The tour

Real screenshots. Real pixels.

Every picture here was taken in Scandium 1.0 on the project's own synthetic test images. Drag the sliders.

01

Retouch

Heal, fifteen strokes on a dusty sky. The spots are gone; the grain, the gradient and the meteor stay. Drag to compare.

The sky of a synthetic lake scene with black dust spots. The same sky after fifteen Heal strokes: the dust spots are gone.
BeforeHealed
02

Adjustment layers

Curves, Levels, Exposure, Hue/Saturation, Brightness/Contrast, Threshold, Invert — as layers you can hide, reorder, mask or delete later.

A synthetic lake scene before a Curves adjustment. The same scene after a Curves adjustment layer.
BeforeCurves
03

27 blend modes

One masked layer of spheres over the lake, cycling through six of them. Pick one, or hover to pause.

The composite with the spheres layer in Overlay. The same layer in Multiply. The same layer in Screen. The same layer in Difference. The same layer in Hue. The same layer in Luminosity.
1Overlay
04

Selections and masks

Marquee, lasso, wand. Any selection becomes a mask — feather it, lower its density, invert it, unlink it. The pixels stay. Click to compare.

The spheres layer pasted over the lake, covering the whole canvas. After a mask from a rectangular marquee: the spheres show only on the right; the Mask panel reads Enabled and Linked.
1The spheres layer, unmasked
05

Transform and Perspective

Drag the handles with a live preview, confirm with Return. The sticker in the composite above went through Perspective. Click to compare.

The composite with the sticker layer still a flat rectangle. The same composite after the Perspective transform: the sticker is a trapezoid, and History reads Perspective Transform.
1The sticker, as pasted
06

The dialogs

Every adjustment but Invert, and every filter, has its own. Values, then Apply — and the result is one undoable step.

The Curves dialog with a control point lifted.

Curves

Master, red, green and blue.

The Hue/Saturation dialog: hue, saturation, lightness, colorize.

Hue/Saturation

Master and six colour bands, with colorize.

The Unsharp Mask dialog: amount, radius, threshold.

Unsharp Mask

Amount, radius, threshold.

The Gaussian Blur dialog with a radius field.

Gaussian Blur

And Box Blur, Median, High Pass, Add Noise.

The Canvas Size dialog: width, height, relative, anchor, extension colour.

Canvas Size

Anchor, relative, extension colour.

The list of 27 layer blend modes, from Normal to Luminosity.

Blend modes

All 27, Normal to Luminosity.

Rows of the History panel: Layer Properties, fifteen Heal Strokes, a Perspective Transform.

History

Every edit a row; click one to jump there.

The export format list: PNG, JPEG, TIFF, WebP, GIF, BMP, OpenRaster.

Export

PNG, JPEG, TIFF, WebP, GIF, BMP, OpenRaster.

Everything in 1.0

The whole list, scrolling by.

Taken from the user guide, the CLI's documentation and the release notes; nothing added. Hover to pause; open the list to read it.

Eleven groups, one guide.

Tools, layers, adjustment layers, selections, masks, filters, the Image menu, history, files, the headless CLI and the platform — every line is taken from the 1.0.2 user guide, the CLI's documentation and the release notes.

What is known not to work yet is listed in KNOWN-ISSUES.md: thirty entries, among them that the Windows build is not yet verified on a clean machine.

Open the full list

Tools

  • Move
  • Rectangular marquee
  • Elliptical marquee
  • Lasso
  • Polygon lasso
  • Magic wand — tolerance, contiguous or every matching pixel
  • Crop
  • Brush — size, hardness, colour
  • Eraser
  • Heal
  • Clone stamp — sample all layers, Option-click source
  • Dodge
  • Burn
  • Sponge
  • Blur
  • Sharpen
  • Smudge
  • Pen
  • Rectangle shape
  • Ellipse shape
  • Free transform
  • Perspective transform
  • One-letter tool shortcuts, V to U — press again to cycle the group

Layers

  • Add, duplicate, delete, merge down, reorder, rename, hide
  • Opacity
  • 27 blend modes
  • Shape layers with stroke
  • Paste as a new layer
  • Paste Into — masked to the selection
  • Flatten with or without hidden layers

Adjustment layers

  • Brightness/Contrast — with a legacy switch
  • Curves — master, red, green, blue
  • Exposure — exposure, offset, gamma
  • Hue/Saturation — master plus six colour bands, colorize
  • Levels — composite and per channel
  • Threshold
  • Invert
  • Every adjustment lives on its own layer: hide, reorder, mask or delete it later

Selections

  • Marquee, lasso and wand selections
  • Select all · Deselect · Invert
  • Save and load selections
  • Load a layer's alpha as a selection
  • Modify: expand, contract, feather, border, smooth
  • Delete inside the selection
  • Right-click canvas menu with the whole Select menu

Masks

  • Reveal All · Hide All
  • From Selection · From Selection, Inverted
  • From Transparency
  • Enable or disable, feather, density, invert
  • Link or unlink from the layer
  • Every change undoable

Filters

  • Gaussian Blur
  • Box Blur
  • Median
  • High Pass
  • Add Noise
  • Unsharp Mask

Image

  • Auto tone, auto contrast, auto colour — one step each
  • Resize — by width, height or longest edge, PPI, constrain, resample
  • Canvas Size — anchor, relative, extension colour
  • Rotate — any angle, flips, expand canvas
  • Trim — by transparency or edge colour, per edge
  • Flatten Image
  • Convert Depth — 8-bit integer or 32-bit float
  • Assign Profile — sRGB, Display P3, ProPhoto RGB
  • Convert to Profile

History

  • Every edit is one row
  • Click any row to jump straight to it
  • Undo and redo from the keyboard
  • The History panel is the same thing shown by name

Files

  • .appname package — layers, masks, adjustment layers, history position
  • Import PNG, JPEG, TIFF, WebP, GIF, BMP as layers
  • Drag a file onto the window to import it
  • Export PNG, JPEG, TIFF, WebP, GIF, BMP and OpenRaster
  • Byte-identical exports on macOS and Windows

Headless CLI

  • new · import · apply · script · export
  • info --json · history · undo · redo
  • duplicate · recover — crash recovery from the autosave journal
  • selftest — the determinism proof
  • Transactional JSONL scripts: stop on error with the line number, nothing persisted on failure
  • Exit codes 0 / 1 / 2 and, with --json, one JSON object per call
  • English, Spanish and Japanese catalogs

Platform

  • macOS 15.4 or later on Apple silicon
  • Windows x64
  • GPU rendering measured against the CPU reference
  • No network, no telemetry, no account
  • GPL-3.0 · SDK interface Apache-2.0
  • Every third-party notice, copyright line and licence text ships inside the app
The scripting door

The same engine, headless.

Every edit — in the app or from a shell — is one JSON command through one entry. This transcript is an excerpt of a real run: nothing in it is edited.

appname — release build, 1.0.2
$ appname new --width 3000 --height 1875 --background white --out lake.appname
Created lake.appname — 3000 × 1875 px, document 947cae27-4006-5dfa-8514-f93971bba330, background layer 7caaf307-25d6-508c-9797-5ed1e3cc515c
$ appname import --doc lake.appname --image lake.png
Imported layer "Pasted" (d86255cb-f2e5-4508-9e7a-53b8bd7217ef) into lake.appname
$ appname apply --doc lake.appname --command '{"schema":"appname.command.v1","id":"00000000-000e-4000-8000-000000000001","kind":"adjustment.add","target":{"layerId":"d86255cb-f2e5-4508-9e7a-53b8bd7217ef"},"params":{"opKind":"adjustment.curves","newLayerId":"00000000-000e-4000-8000-000000000008","newOpId":"00000000-000e-4000-8000-000000000007","params":{"master":[[0,0],[0.25,0.18],[0.75,0.85],[1,1]]}}}'
Applied adjustment.add (envelope 00000000-000e-4000-8000-000000000001) — revision 3
$ appname history --doc lake.appname
History — entries: 3, cursor: 3
     0  *  doc.new             5ca60670-c64b-4db7-8a5d-20313eb23a15
     1  *  edit.paste          5af76399-6888-4ee9-b779-ed03b5557520
     2  *  adjustment.add      00000000-000e-4000-8000-000000000001
$ time appname export --doc lake.appname --format png --out lake-curves.png
Exported lake-curves.png (png, 8591881 B)
real	0m1.189s
user	0m1.126s
sys	0m0.057s
$ appname selftest
Scandium selftest — ten-feature headless proof
  save → reopen: state_hash identity           PASS  hash 71ef71521484
  reopen → replay journal: state_hash identity PASS  2 commands
  reopen → re-export: byte identity            PASS  1676 bytes
  save → load → save: package byte identity    PASS  527851 bytes
  independent second run: byte identity        PASS  package + export identical
Selftest PASS — 5/5 features, golden: self-contained
Transactional scriptsA JSONL script stops on the first error and persists nothing on failure.
Crash recoveryRun with an autosave journal; recover replays it on top of the base package.
Determinism, provedselftest saves, reopens, replays and re-exports — and checks every hash matches.
Get it

Scandium 1.0.2

Checksums, source archives and release notes on the release page.

macOS 15.4 or later. Not notarized yet: right-click → Open → Open, once.
Windows. Unsigned for now: SmartScreen → More info → Run anyway. Needs the Microsoft Visual C++ Redistributable (x64).