Pewarta Proxy

📋 Server Request & Proxy Logs

Loading active proxy settings...
Developed by Khairul Adha (adhexa.id)
Mengambil log server...

⚙️ Pengaturan Proxy Target

Skip to content

Vips: use binary (UTF-8) encoding instead of base64 when inlining WASM to shrink the worker bundle #79187

Closed

Description

@adamsilverstein

What

Switch the WASM inline encoding in our build from base64 data URLs to Emscripten's compact UTF-8 binary encoding to reduce the compressed size of the @wordpress/vips worker bundle.

Background

@wordpress/vips inlines wasm-vips' .wasm binaries directly into build/modules/vips/worker.min.js so there are no separate WASM downloads and no host MIME-type issues. The inlining is done by the wasmInlinePlugin in packages/wp-build/lib/build.mjs, which currently emits data:application/wasm;base64,… data URLs.

base64 inflates binary by ~33% and — more importantly — misaligns byte patterns so it compresses poorly. As @kleisauke pointed out, Emscripten's SINGLE_FILE_BINARY_ENCODE uses a custom UTF-8 embedding instead, which compresses much better with both gzip and brotli.

Measured impact

Measured on the 0.0.18 vips.wasm + vips-heif.wasm through a faithful reproduction of the full build pipeline (inline → JSON.stringify worker-code wrapper → minified bundle):

worker.min.js gzip brotli
base64 (current) ~4.20 MB ~3.31 MB
binary (UTF-8) ~3.65 MB ~2.79 MB
change −13.2% −16%

The bundle-size check uses gzip, so this is roughly −550 kB off the compressed worker.min.js. (An earlier comment cited ~18% from a standalone payload measurement; the full-pipeline figure above is the accurate one, since the real worker.min.js wraps the worker in a JSON.stringify layer.)

Tradeoff: the uncompressed worker.min.js grows ~16% (~1.8 MB), since UTF-8 stores high byte values as two bytes vs base64's one ASCII char. The worker is lazily loaded and served compressed, so the compressed transfer win benefits users while the uncompressed cost is transient.

Proposed implementation

  1. Update wasmInlinePlugin in packages/wp-build/lib/build.mjs to emit the binary-encoded string (Emscripten's scheme: write each byte as UTF-8, escaping \r, \n, \, and the chosen quote char) instead of a base64 data URL.
  2. Update consumption in packages/vips/src/index.ts: the binary-encoded string is not a fetchable data URL, so locateFile must decode it to bytes and pass wasm-vips either wasmBinary (main vips.wasm module) or a Blob URL via URL.createObjectURL (the vips-heif.wasm dynamic library, which is loaded by URL fetch).
  3. Verify the worker still instantiates and the existing @wordpress/vips tests pass, and confirm the bundle-size reduction in CI.

Notes

Implemented in #79188.

Activity

  1. added
    [Feature] MediaAnything that impacts the experience of managing media
    and removed
    [Feature] MediaAnything that impacts the experience of managing media
    on Jun 15, 2026
  2. kleisauke commented on Jun 15, 2026

    @kleisauke
    Contributor

    For context, the comment at lovell/sharp-libvips#247 (comment) may also be relevant here, as server-side Brotli compression could further reduce the size of these Wasm binaries. AFAIK, using a CDN such as jsDelivr is probably out of scope for Gutenberg or WordPress.

  3. adamsilverstein commented on Jun 15, 2026

    @adamsilverstein
    MemberAuthor

    For context, the comment at lovell/sharp-libvips#247 (comment) may also be relevant here, as server-side Brotli compression could further reduce the size of these Wasm binaries. AFAIK, using a CDN such as jsDelivr is probably out of scope for Gutenberg or WordPress.

    Thanks, I'll give that a try. Right jsDelivr is out of scope, although we do load some 3p libraries from our own CDN, the build process and direct inclusion are a better chould for this feature.

  4. adamsilverstein commented on Jun 15, 2026

    @adamsilverstein
    MemberAuthor

    Thanks @kleisauke, that's a helpful reference. I measured all three options for the two inlined binaries (vips.wasm + vips-heif.wasm, 0.0.18), through a faithful reproduction of our build pipeline:

    Approach gzip brotli
    base64 inlined (current) ~4.20 MB ~3.31 MB
    binary inlined (#79188) ~3.65 MB ~2.79 MB
    raw .wasm, not inlined (floor) ~3.23 MB ~2.57 MB

    Two takeaways:

    1. Brotli is the dominant lever, but it's a serving-layer concern. It's not something the build controls — it depends on the web server (mod_brotli / ngx_brotli) or CDN, which most production WordPress hosts already provide. jsDelivr is out of scope as you noted. So there's no additional build-side change to make here from the Brotli point.

    2. Binary encoding already captures nearly all of the achievable benefit under Brotli. The inlined binary-encoded artifact (~2.79 MB brotli) is within ~220 kB of the raw-.wasm brotli floor (~2.57 MB). Getting fully to the floor would mean serving the .wasm un-inlined, which we deliberately avoid (separate downloads + host MIME-type issues).

    So #79188 (binary encoding) is the build-side improvement, and serving Brotli realizes the rest at the hosting layer. Opened the PR with the full numbers and a note on the one tradeoff (uncompressed size grows ~16%).

  5. moved this to 📋 Iteration/Tracking Issues in WordPress 7.1 Editor Taskson Jun 19, 2026
  6. moved this from 📋 Iteration/Tracking Issues to 🏗️ In Progress in WordPress 7.1 Editor Taskson Jun 19, 2026
  7. moved this from 🏗️ In Progress to ✅ Done in WordPress 7.1 Editor Taskson Jun 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Type

Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions