Skip to content

Media: Generate animated image sub-sizes by default. - #12572

Open
adamsilverstein wants to merge 14 commits into
WordPress:trunkfrom
adamsilverstein:add/animated-image-subsizes-filter
Open

adamsilverstein wants to merge 14 commits into
WordPress:trunkfrom
adamsilverstein:add/animated-image-subsizes-filter

Conversation

@adamsilverstein

@adamsilverstein adamsilverstein commented Jul 16, 2026 •

Copy link
Copy Markdown
Member

Test in WordPress Playground

Updated to match the change of direction in WordPress/gutenberg#80385 (comment): animated sub-sizes are now the default, and the filter is an opt-out.

Claude rewrote the description to follow along:

Core backport of the server-side changes in WordPress/gutenberg#80385 (design discussion in WordPress/gutenberg#80383).

Resized versions of animated GIFs have always lost their animation - the subject of the 12-year-old Trac #28474, which stalled server-side because GD cannot preserve frames and Imagick is only available on a subset of hosts. The client-side media processing path decodes and re-encodes every frame in the uploading user's browser, independent of host configuration, so sub-sizes can keep their animation.

This adds the wp_generate_animated_image_subsizes filter (boolean, default true) and exposes its value on the REST API index as generate_animated_image_subsizes, inside the existing client-side media processing block in WP_REST_Server::get_index() (alongside image_size_threshold, only for users who can upload_files). By default the client pipeline keeps the animation in every sub-size of an animated GIF or WebP, cropped sizes such as thumbnail included. Returning false generates static first-frame sub-sizes, matching the server-side path.

The field is also added to the block editor root-index preload field lists in wp-admin/edit-form-blocks.php and wp-admin/site-editor.php - these must match packages/core-data/src/entities.js exactly for preloading to work, and the matching entities.js change is part of the Gutenberg PR.

The client-side consumption (editor settings plumbing, @wordpress/upload-media, and the @wordpress/vips worker that loads every frame, crops each frame with one rectangle, and falls back to a static sub-size when an animation is too large to decode) ships via the Gutenberg package updates in WordPress/gutenberg#80385.

Trac ticket: https://core.trac.wordpress.org/ticket/65656
Trac ticket: https://core.trac.wordpress.org/ticket/28474

Proposed commit message

Media: Generate animated image sub-sizes by default.

Fix an issue where sub-sizes of animated image uploads are static first-frame images, as GD and Imagick flatten animated images when resizing. Generated image now retain animations, leveraging `libvips` and client-side media processing.

Add a `wp_generate_animated_image_subsizes` filter (boolean, default true) so the client pipeline can keep or drop the animation in sub-sizes. The client-side consumption ships via the Gutenberg package updates in https://github.com/WordPress/gutenberg/pull/80385.

Fixes #65656, #28474.

AI Use

The code and this description came out of a session with 🤖 Claude Code. I will review and test.

… index.

Sub-sizes of animated images are static first-frame images by default because re-encoding every frame per sub-size is very resource intensive. The client-side media processing path can preserve animation in the browser, so expose an opt-in flag on the REST API index (alongside image_size_threshold) that the client pipeline honors for uncropped sub-sizes.

See WordPress/gutenberg#80383 and WordPress/gutenberg#80385.
@github-actions

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@adamsilverstein
adamsilverstein marked this pull request as ready for review July 16, 2026 23:58
@github-actions

github-actions Bot commented Jul 16, 2026 •

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props adamsilverstein, wildworks, mukesh27, swissspidy, westonruter, mahdiali.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Comment thread tests/phpunit/tests/rest-api/rest-server.php
Comment thread src/wp-includes/rest-api/class-wp-rest-server.php Outdated
Comment thread src/wp-includes/rest-api/class-wp-rest-server.php
@adamsilverstein

Copy link
Copy Markdown
Member Author

Just noting we have decided to land this in 7.2.

…ubsizes-filter

# Conflicts:
#	src/wp-includes/rest-api/class-wp-rest-server.php
#	tests/phpunit/tests/rest-api/rest-server.php
#	tests/qunit/fixtures/wp-api-generated.js
adamsilverstein and others added 3 commits September 30, 2026 15:39
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxbbMKz3sa741SjHuE1ZLS
A bare `animated_image_subsizes` reads like an array of sizes rather
than a boolean. Prefix it with `generate_` so the field name mirrors
the filter it exposes, and keep the preload field lists alphabetical so
they still match the editor's entities.js list exactly.

Claude-Session: https://claude.ai/code/session_018uRsBsDb9t3piAjyWaycCo

@t-hamano t-hamano left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-approving this prior to WordPress/gutenberg#80385.

Just in case, it might be better to wait to commit this PR until WordPress/gutenberg#80385 is merged and confirmed to be shipped in 7.2. Otherwise, a mismatch may occur between the preload list and entities.js.

Comment thread tests/phpunit/tests/rest-api/rest-server.php
@mahdialikhanusiya

Copy link
Copy Markdown

Looks good. Clean opt-in approach for preserving animated GIF sub-sizes via the client-side pipeline.

Happy to test once both sides are ready.

@adamsilverstein

Copy link
Copy Markdown
Member Author

Looks good. Clean opt-in approach for preserving animated GIF sub-sizes via the client-side pipeline.

Happy to test once both sides are ready.

This is ready for testing @mahdialikhanusiya

Missing animation in sub-sizes has been reported as a bug since #28474, so keep it by default, cropped sizes included, and let sites opt out with the `wp_generate_animated_image_subsizes` filter instead. Mirrors WordPress/gutenberg#80385.

Claude-Session: https://claude.ai/code/session_01NyQHifJCe7BYvWvpPDtwXG
@adamsilverstein adamsilverstein changed the title Media: Add an opt-in filter to generate animated image sub-sizes Media: Generate animated image sub-sizes by default. Oct 16, 2026
@mahdialikhanusiya

Copy link
Copy Markdown

@adamsilverstein Thanks I will test it soon. Hopefully by tomorrow.

@adamsilverstein

Copy link
Copy Markdown
Member Author

Pre-approving this prior to WordPress/gutenberg#80385.

Just in case, it might be better to wait to commit this PR until WordPress/gutenberg#80385 is merged and confirmed to be shipped in 7.2. Otherwise, a mismatch may occur between the preload list and entities.js.

WordPress/gutenberg#80385 is merged and I plan to merge this PR ahead of beta 1.

@mahdialikhanusiya

Copy link
Copy Markdown

Tested on the Playground.

Uploaded an animated GIF via the block editor.

  • Full-size: animates ✅
  • Medium (300×169) and thumbnail (150×150): static (first frame only) ❌

Also tested after changing the resolution in the block editor — sub-sizes still remained static.

Looks like the client-side path may not be generating animated sub-sizes in this environment. Let me know if additional testing steps are needed.

@adamsilverstein

Copy link
Copy Markdown
Member Author

@mahdialikhanusiya thanks for testing!

Claude dug into why the sub-sizes came out static, here is what it found:

That result is expected on this Playground build. This PR is only the server half: it adds the wp_generate_animated_image_subsizes filter and exposes it on the REST index. The part that actually keeps the frames lives in the editor packages from WordPress/gutenberg#80385, which merged on October 16.

wordpress-develop builds the editor from a pinned Gutenberg commit (gutenberg.sha in package.json), currently 954fe7f from September 30, two weeks before that merge. So the Playground for this PR runs the old upload pipeline, which ignores the new field and generates static sub-sizes the same way trunk does.

Animated sub-sizes will show up end to end once the Gutenberg pin in core is bumped past the #80385 merge. Until then, a way to test the full behavior is the Gutenberg plugin from trunk (or a recent nightly) on top of this branch: upload an animated GIF in the block editor, then open the medium and thumbnail files directly, and they should animate.

@mahdialikhanusiya

Copy link
Copy Markdown

@adamsilverstein Thanks for the clear explanation! 🙌

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants