paged.image — raster editing on the GPU

A darkroom inside the layout

Photoshop-class image editing on WebGPU — layers, masks, adjustment layers, healing and content-aware fill — and a PSD that comes back byte for byte when you did not touch it.

AGPL-3.0 OR PMEL·canary — usable, moving fast·0.1.0-canary.18·github.com/paged-media/plugin-image ↗

131
image kernels
26
blend modes
80
Photoshop answers replayed in CI
Every pixel operation is a GPU kernel

Written in WGSL, run on WebGPU

paged.image is a Rust engine compiled to WebAssembly that dispatches its work as WGSL compute through WebGPU. A kernel is one definition — an id, a parameter block, WGSL source and a tolerance — registered from a single table, and every kernel binds the same interface: inputs, parameters, a selection mask and a half-float output.

The registry spans adjustments, convolutions, the compositing modes, geometry and resampling, morphology, rank filters, generators and a gallery of effects. Sliders preview live on a resampled proxy; full resolution follows the moment they rest.

The kernel contact sheet: one photograph through exposure, brightness and contrast, saturation, hue rotation, black and white and posterize, each committed back into the document — with the roster of kernel ids by family underneath.
The kernel contact sheet: one photograph through exposure, brightness and contrast, saturation, hue rotation, black and white and posterize, each committed back into the document — with the roster of kernel ids by family underneath.
Adjustments
Exposure, levels and curves per channel, Hue/Saturation with six ranges, vibrance, colour balance, black and white, channel mixer, photo filter and more — as a re-runnable chain.
Filters
Lens, motion and radial blur, smart sharpen, reduce noise, dust and scratches, median, emboss, find edges, twelve gallery effects, colour lookup from a .cube file.
Fills
Gradient, noise, solid, pattern and content-aware fills, and a paint bucket.
Selection
Marquees, lasso, polygonal lasso, magic wand and quick selection; add, subtract, intersect; feather, expand, contract, border, smooth; selection to path and back.
Non-destructive by construction

Layers, masks, adjustment layers, smart objects

A layer stack with visibility, lock, opacity and 26 blend modes; masks from a selection or painted directly; groups and clipping; editable adjustment layers; smart objects that re-render at a new scale. One undo list covers pixel edits and every change to the stack.

Retouching is here too: brush, pencil and eraser with pressure and .abrpresets, clone stamp, healing brush, spot healing, the patch tool, dodge, burn and sponge, blur and sharpen brushes, red-eye removal, and a type tool that paints shaped text with the document’s own fonts.

The Darkroom opener. Its loop — place, ingest, adjust, composite, commit — is the plugin's whole life cycle: four stations touch only the session; commit writes the result into the document.
The Darkroom opener. Its loop — place, ingest, adjust, composite, commit — is the plugin's whole life cycle: four stations touch only the session; commit writes the result into the document.
PSD fidelity

Paged never destroys a PSD

Open a Photoshop file you do not change and the exporter hands back the original bytes, byte for byte. Layers of the retained file can be renamed, removed or given another opacity before export. A session with several layers exports as a layered PSD — pixel layers, masks, groups, opacity, blend, visibility and clipping.

Layered files open as layers when the model reproduces them: pixel layers, groups, user and vector masks drawn the way Photoshop draws them, clipping, Curves, Levels, Exposure, Invert and Hue/Saturation adjustment layers, smart objects as their stored render. Of a 157-file corpus of real PSDs, 76 open as layers today; the rest open flattened and say why.

[ 0 ]bytes changed when an untouched PSD is exported
[ 80 ]Photoshop answers for blend, adjustment and filter cases, replayed in CI
[ 76 / 157 ]real-world PSDs that open as editable layers
Every case agrees, differs by a stated convention, or is declined on a measured disagreement.

Beside the 80 cases, seven layered RGB PSDs and six CMYK PSDs with Photoshop’s own sRGB conversions are replayed in CI. GPU tests run on a software Vulkan adapter for every pull request and on Apple silicon after merges.

Compositing on the page

Pixels that sit in a layout

An adjusted image is shown inside its frame as a scene layer while you work, and composites with whatever the page puts around it. The engine itself decodes the formats a layout places, and composites frames with InDesign’s sixteen blend modes; paged.image adds its own 26 inside an image.

The Paged Annual — photo-codecs
The Paged Annual — blend-modes
The Paged Annual — luminosity
One photograph through several codecs, decoded by the engine; then one motif under the sixteen frame blend modes the engine composites, Normal to Luminosity. These pages show the engine beneath the plugin, not the plugin's own 26 layer modes.
Live — paged.image as its own application

The photo editor, in this tab

Solo mode boots the real editor as paged.image, with only the tools and panels a photo editor needs in the cockpit. Your image is decoded and edited on your own GPU; nothing is uploaded.

The paged editor booted as paged.image
Needs a browser with WebGPU (Chrome, Edge, Safari 26).open full screen ↗
How it lives in a document

Layers in the container, the composite in the frame

Commit stores the layer stack in the document’s own parts inside the .paged container and puts the composite into the frame as one document undo step. Reopen the frame and the layers come back. Because the frame holds a plain placed image, the IDML InDesign opens shows exactly what you see — and Apply to file or the exporters write the result as PSD, PNG or JPEG.

The full list of what ships and what does not is in the repository’s status record ↗.

Spec sheet — from the registry, not from marketing

paged.image features

Plugin · image · 85
A sampled .abr tip paints through the existing stroke accumulator
image.abr.engine-bridge
experimental· untested
.abr brush-preset reader over a new PSD descriptor reader
image.abr.reader
experimental· untested
The .abr regression gate, split into the two lanes its roles can run
image.abr.regression-gate
experimental· untested
Per-channel statistics, from the same buffer as the histogram
image.channels.readout
experimental· 2 tests
Load a channel as the selection (luminosity masking)
image.channels.to-selection
stable· 2 tests
CMS display lane — qcms RGB↔RGB (the exact build core ships, page/editor agree)
image.cms.display
experimental· 3 tests
CMS behind the swappable CmsEngine seam (D-11: hybrid)
image.cms.engine
experimental· 14 tests
CMS rung 1 — the RGB display transform at decode
image.cms.ingest-transform
experimental· untested
CMS print lane — moxcms per-intent RGB↔RGB + CMYK8→RGBA8 ingest
image.cms.print
experimental· 10 tests
AVIF / JXL / camera-RAW / HEIC — codec verdicts (deferred + explicit non-goals)
image.codec.avif
planned· untested
EXIF/TIFF read — orientation + DPI + color-space tag, auto-orient at ingest
image.codec.exif
stable· 10 tests
Codec adapter contract (ImageSource/ImageTarget over sans-IO ByteSource) + PNG + raw
image.codec.traits
stable· 17 tests
GPU tests that cannot pass silently
image.conformance.gpu-lanes
experimental· untested
Conformance harness — scalar references, gpu↔ref parity, fixture corpus
image.conformance.harness
experimental· 64 tests
Panel + chain integration of the new adjustments and generators
image.editor.adjust-breadth
stable· untested
Auto-enhance — gray-world WB + percentile auto-levels params from the panel histogram
image.editor.auto-enhance
stable· 8 tests
Blur and sharpen brushes
image.editor.blur-sharpen-brush
experimental· untested
Photoshop .abr brush libraries load and set the brush
image.editor.brush-presets
experimental· 2 tests
Rotate, flip and Canvas Size over all layers
image.editor.canvas-ops
experimental· untested
Clone stamp — the brush with a sampled paint layer
image.editor.clone-stamp
stable· 4 tests
Foreground/background colour, picker and eyedropper
image.editor.color-fg-bg
experimental· untested
Content-aware fill — coarse-to-fine exemplar inpainting
image.editor.content-aware-fill
stable· untested
Editor crop + straighten tool — host-agnostic crop machine over the geom.crop window
image.editor.crop
stable· 2 tests
Crop straighten commits (arbitrary-angle rotation resample)
image.editor.crop-straighten
stable· untested
Editor levels/curves/white-balance panel — histogram view + adjust kernels
image.editor.curves
experimental· 4 tests
K-3 decode worker pool — off-main-thread ingest decode (host.workers consumer)
image.editor.decode-pool
experimental· 2 tests
Dodge, burn and sponge
image.editor.dodge-burn
experimental· untested
Filters — blur / unsharp / hue-rotate / invert in the adjustments panel
image.editor.filters
stable· 25 tests
Gradient tool (drag the line)
image.editor.gradient-tool
experimental· untested
Healing brush — a clone tone-matched in the gradient domain
image.editor.healing-brush
stable· 2 tests
History panel over the tile journal
image.editor.history-panel
stable· untested
Host Cmd+Z inside an image
image.editor.host-undo
experimental· untested
Hue/Saturation — master, six colour ranges, colorize
image.editor.hue-saturation
experimental· untested
M4 editor enablement — C-5 placed bytes → decode → adjust → C-1 Stage-A in-frame composite
image.editor.ingest
experimental· 20 tests
Pixel-layer stack with a compose.* fold
image.editor.layers
experimental· 6 tests
Live adjustment preview on a resampled proxy
image.editor.live-preview
experimental· untested
Paint on layer masks (Pixels/Mask edit target)
image.editor.mask-painting
experimental· untested
Move tool for pixels and layers
image.editor.move-tool
experimental· untested
Brush / Pencil / Eraser — the raster paint lane
image.editor.paint
stable· 10 tests
Paint bucket
image.editor.paint-bucket
experimental· untested
Patch tool
image.editor.patch
experimental· untested
Define pattern, pattern fill and shape blur
image.editor.pattern-fill
experimental· untested
Median, Maximum, Minimum and Color Lookup
image.editor.rank-filters
experimental· untested
Raster type — shaped glyphs painted into a layer
image.editor.raster-type
experimental· 4 tests
Red eye
image.editor.red-eye
experimental· untested
Resample — resize the working image through the registered kernels
image.editor.resample
stable· 3 tests
Spot healing brush
image.editor.spot-heal
experimental· untested
C-6 tile provider for placed images (host.images.claimImageResource, v44; honest level-0 subset)
image.editor.tile-provider
experimental· 4 tests
Raster tool options in the host's tool-options bar
image.editor.tool-options
experimental· untested
Bounded COW tile journal — undo/redo for pixel edits
image.editor.undo-journal
experimental· 2 tests
Engine B — persistent buffer graph (invalidation, mip-aware eval, Gestures/Operations)
image.graph.engine-b
experimental· 5 tests
16-bit PSD sources open, reduced to 8 and reported
image.io.depth-16bit-ingest
experimental· untested
Layered PSD export
image.io.layered-psd
experimental· untested
Commit image edits to the document and reopen them
image.io.persistence
experimental· untested
Save-back — adjusted composite to PSD / PNG / JPEG
image.io.save-back
stable· 4 tests
Kernel breadth — 24 new point/compose/gen kernels (73 → 97)
image.kernel.breadth-2026-08
stable· untested
T0 generated kernel families (arith/relational/boolean/cast/band/minmax/linear)
image.kernel.family-t0
experimental· 151 tests
T1 crown-jewel kernels (conv/resample/compose — handwritten WGSL modules, ABI v1.1)
image.kernel.family-t1
experimental· 41 tests
T2 editor-bearing kernels (adjust/geom/generators)
image.kernel.family-t2
experimental· 67 tests
T3 breadth kernels (morphology, rank/median, distance transform)
image.kernel.family-t3
experimental· 14 tests
1D and 3D LUT sampler classes
image.kernel.lut-samplers
experimental· untested
General backward-map warp
image.kernel.warp-backward
experimental· untested
Frozen WGSL kernel ABI v1 (4 bind groups, 16×16, mask-mixed pointwise)
image.kernel.wgsl-abi
stable· 5 tests
Clipping layers — an adjustment confined to the layer beneath
image.layers.clipping
stable· untested
Editable adjustment layers
image.layers.editable-adjustments
experimental· untested
The layer fold keeps adjustment layers in f16
image.layers.f16-fold
stable· untested
Layer groups — pass-through and isolated, nesting up to eight
image.layers.groups
stable· untested
Smart objects
image.layers.smart-objects
experimental· untested
A host path becomes the selection
image.paths.path-to-selection
stable· 2 tests
A raster selection becomes a real vector path
image.paths.selection-to-path
experimental· 2 tests
Work counters and count budgets
image.perf.budgets
experimental· untested
Engine A async sinks — to_buffer_async/to_encoder_async (the wasm32/WebGPU readback lane)
image.pipeline.async-sink
stable· 6 tests
Engine A — demand-driven streaming pipeline skeleton
image.pipeline.engine-a-skeleton
stable· 8 tests
paged.image bundle (media.paged.image) — manifest + activate + panel
image.plugin.bundle-manifest
experimental· 4 tests
PSD structural session — layer list/edit/save reach the editor
image.psd.editor-doors
stable· untested
PSD import populates the layer stack (declines rather than approximates)
image.psd.layer-import
experimental· untested
PSD mutatable — edits re-encode through the preservation writer
image.psd.mutatable
experimental· 8 tests
PSD rendered tier — channel decode + flatten render oracle
image.psd.rendered
experimental· 31 tests
PSD/PSB structural parse + preservation-invariant writer
image.psd.roundtrip
experimental· 171 tests
Reductions — histogram + min/max/mean statistics
image.reduce.statistics
experimental· 4 tests
Residency Tier 0/1 hardening under working-set pressure
image.residency.tier01
stable· 3 tests
Selection-mask plumbing — the §6.1 mask surfaced end-to-end
image.selection.mask
stable· 4 tests
Selection system — marquee/ellipse/lasso/wand over the mask ABI
image.selection.mask-tools
stable· 6 tests
Selection coverage threaded through the GPU adjust chain
image.selection.masked-pipeline
stable· 4 tests
Select ▸ Modify and wand options
image.selection.modify
experimental· untested

85 features · commit-pinned snapshot · the same rows the team plans by