Skip to content

Client-side media: process image edits (crop, rotate, flip) with wasm-vips instead of the server /edit endpoint #82358

Description

@adamsilverstein

What problem does this address?

Image edits made in the editor (crop, rotate, flip) are still processed on the server. Both the block editor's image editor and the new media editor modal build a modifiers array and POST it to the media/<id>/edit REST endpoint, where PHP applies the edits with GD or ImageMagick:

Meanwhile, uploads already go through client-side media processing (wasm-vips), which handles the same operations and preserves things the server pipeline drops. That mismatch causes real bugs: cropping or flipping an HDR JPEG strips its gain map and the result is SDR, see #82295. As noted in that thread, GD and ImageMagick are gaining gain map support but it is behind build-time flags and will take a long time to reach most hosts. libvips ships with WordPress today via client-side media, so moving the edit step to the client is the practical path.

What is your proposed solution?

Move crop / rotate / flip processing to the client when client-side media processing is available, and upload the result through the existing upload pipeline instead of calling the /edit endpoint.

Rough sketch:

  1. When the user saves an edit, fetch the original (full size) file for the attachment rather than the size shown in the editor.
  2. Apply the modifiers with wasm-vips. The @wordpress/vips package already has the building blocks: crop with gain map handling (https://github.com/WordPress/gutenberg/blob/03bab588063/packages/vips/src/index.ts#L369-L400), and rotateImage covers rotation and horizontal / vertical flips (https://github.com/WordPress/gutenberg/blob/03bab588063/packages/vips/src/index.ts#L803).
  3. Hand the resulting file to the upload-media store so sub-sizes are generated client-side and uploaded the same way a fresh upload is, then swap the block's id / url to the new attachment.
  4. Fall back to the current /edit request when client-side processing is unavailable or fails for the file type.

Design Decisions:

  • When creating the cropped images match how the Gutenberg image editor creates them, creating a new media item
  • Always use/fetch the original image to apply crops.
  • A single helper that turns modifiers into vips operations
  • Undo behaves as currently - it points the block back at the old id / url
  • E2E tests confirm the cropping tool (+rotate/flop) work exactly as expected and that gain maps are preserved

Fixes #82295
Related: #72734

cc: @andrewserong @ramonjd @swissspidy @gregbenz

AI Use

Description by myself and Claude Code, from the discussion on #82295 and a read through the current edit and vips code paths.

Activity

  1. moved this to 🏗️ In Progress in WordPress 7.2 Editor Taskson Sep 2, 2026
  2. swissspidy commented on Sep 2, 2026

    @swissspidy
    Member

    This makes a lot of sense!

  3. andrewserong commented on Sep 2, 2026

    @andrewserong
    Contributor

    This makes a lot of sense!

    +1 thanks for capturing this and digging in! It'll also be a good thing to pursue for the future extensibility of the media editor, too, so touching on some of the ideas raised in #79175. TL;DR: to support things like color filters, AI tools and whatnot that will output an image client-side and require uploading to the server, instead of using the /edit endpoint.

    Also, I've already commented on the PRs but just crossing linking another PR that relates to tracking the original crop, as we look at ensuring the REST API supports both the client-side approach, and future "restore to original" / browsing behaviour for crops of a particular attachment:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

[Feature] Client Side MediaMedia processing in the browser with WASM[Feature] MediaAnything that impacts the experience of managing media[Status] In ProgressTracking issues with work in progress[Type] EnhancementA suggestion for improvement.

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions