Skip to content

Client-side media processing: animated GIF sub-size generation takes 15-45s per size and produces files larger than the original #80266

Closed

Description

@adamsilverstein

Description

With client-side media processing enabled, uploading an animated GIF in the editor can take well over a minute even on fast hardware, with the upload spinner blocking the whole time. Testing with a real-world screen-capture GIF (1517×855, 769 frames, 2.2 MB), the full upload pipeline took 91.5 seconds locally in wp-env.

Instrumenting every worker RPC call shows the time is NOT going where you'd expect:

Phase Duration
hasTransparency (vips; the time is dominated by one-time lazy wasm-vips instantiation in the worker's first call, not the check itself) 1.2s
Upload original GIF (REST) 1.3s
thumbnail 150×150 (cropped → static) 0.04s
GIF→video conversion (mediabunny, 769 frames) 3.6s
medium 300×169 animated resize (vips) ~16.5s
medium_large 766×432 animated resize (vips) ~27.6s
large 1024×577 animated resize (vips) ~44s

The three uncropped sub-sizes are re-encoded as full 769-frame animated GIFs ([n=-1] in packages/vips resizeImage()), serialized on the single vips worker: ~88s of the 91.5s total. Harness profiling shows ~85% of each resize is the GIF re-encode (per-frame palette quantization in gifsave), not decoding or resizing.

On top of the speed cost, the animated sub-sizes are larger than the original file: the 300×169 medium came out at 5.5 MB from a 2.2 MB source, because downscaling destroys the GIF's inter-frame optimization and every frame is re-quantized.

Cropped sizes (thumbnail) already silently flatten to the first frame (the [n=-1] branch is skipped when cropping), so current behavior is internally inconsistent as well.

What WordPress core does server-side

Core creates sub-sizes for animated GIFs, but always static, from the first frame only:

  • wp_generate_attachment_metadata() → wp_create_image_subsizes() has no animated special-casing.
  • GD flattens inherently (imagecreatefromgif() reads only the first frame).
  • Imagick explicitly selects frame 1 on load: "Select the first frame to handle animated images properly" (WP_Image_Editor_Imagick::load()).
  • At render time, wp_calculate_image_srcset() keeps flattened sub-sizes and the animated full-size image from ever mixing in a srcset ("WordPress flattens animated GIFs into one frame when generating intermediate sizes").

This also means uploads through the wp-admin Media Library (which still take the server path even when client-side processing is active) produce static sub-sizes today, while the editor's client-side path produces animated ones - the same GIF yields structurally different attachments depending on the upload surface.

Proposed resolution

Match core: generate sub-sizes of animated images from the first frame only in packages/vips resizeImage() (drop the [n=-1] uncropped branch). Animation is preserved where it matters - the original full-size GIF is untouched, and the GIF→video flow attaches an animated_video companion.

Measured result with this change (same GIF, same environment): upload drops from 91.5s to 6.9s (13.3×), sub-size generation from ~88s to 0.17s, and sub-sizes shrink from megabytes to kilobytes. Core's existing srcset guards apply to the client-generated static sub-sizes automatically since they key off the GIF mime type at render time.

Alternatives considered and measured, for the record:

  • Tuning gifsave (effort: 2, interframe_maxerror: 8, interpalette_maxerror: 16): 4-8× faster and fixes the size bloat, but still ~35-50s total for three sizes - and still diverges from core.
  • Skipping sub-sizes for animated GIFs entirely: equally fast, but diverges from core and makes the media library grid fall back to the full-size animated GIF (2.2 MB instead of a 6 KB thumbnail per grid item).

Environment

Activity

  1. moved this to 📋 Iteration/Tracking Issues in WordPress 7.1 Editor Taskson Jul 14, 2026
  2. moved this from 📋 Iteration/Tracking Issues to 🏗️ In Progress in WordPress 7.1 Editor Taskson Jul 14, 2026
  3. moved this from 🏗️ In Progress to 🔎 Needs Review in WordPress 7.1 Editor Taskson Jul 16, 2026
  4. moved this from 🔎 Needs Review to ✅ Done in WordPress 7.1 Editor Taskson Jul 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions