Skip to content

feat: add animated WebP encoding and decoding support#103

Open
xfalcox wants to merge 1 commit into
jamsinclair:mainfrom
xfalcox:claude/add-animated-webp
Open

feat: add animated WebP encoding and decoding support#103
xfalcox wants to merge 1 commit into
jamsinclair:mainfrom
xfalcox:claude/add-animated-webp

Conversation

@xfalcox
Copy link
Copy Markdown

@xfalcox xfalcox commented Mar 30, 2026

Encoder:

  • encodeAnimated(frames, options) accepts array of {imageData, duration} and produces animated WebP with infinite loop via WebPAnimEncoder API
  • Links against libwebpmux for muxing frames into container

Decoder:

  • decode(buffer) now handles both static and animated WebP (falls back to demux API for animated files, returning first composited frame)
  • decodeAnimated(buffer) returns array of {imageData, duration} frames with proper alpha blending and disposal method handling
  • isAnimated(buffer) returns boolean
  • Links against libwebpdemux for demuxing animated WebP

Also upgrades EMSDK from 2.0.34 to 3.1.57 to fix terser minifier crash.

@xfalcox xfalcox force-pushed the claude/add-animated-webp branch from 32d64bd to cd7bf5c Compare March 30, 2026 20:27
Encoder:
- encodeAnimated(frames, options) accepts array of {imageData, duration}
  and produces animated WebP with infinite loop via WebPAnimEncoder API
- Links against libwebpmux for muxing frames into container

Decoder:
- decode(buffer) now handles both static and animated WebP (falls back
  to demux API for animated files, returning first composited frame)
- decodeAnimated(buffer) returns array of {imageData, duration} frames
  with proper alpha blending and disposal method handling
- isAnimated(buffer) returns boolean
- Links against libwebpdemux for demuxing animated WebP

Also upgrades EMSDK from 2.0.34 to 3.1.57 to fix terser minifier crash.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@xfalcox xfalcox force-pushed the claude/add-animated-webp branch from cd7bf5c to 060566b Compare March 30, 2026 20:29
@jamsinclair
Copy link
Copy Markdown
Owner

Thanks @xfalcox. This is awesome.

If it's ok, I'll take a look at these PRs over the weekend and get back to you with any comments 🙏

I can check, but did claude use the existing #75 as reference at all?

xfalcox added a commit to discourse/jSquash that referenced this pull request May 7, 2026
xfalcox added a commit to discourse/discourse that referenced this pull request May 11, 2026
## Summary

- Extends client-side image optimization to convert **JXL → JPEG**,
**HEIC → JPEG**, and **animated GIF → animated WEBP** using jSquash WASM
packages before upload
- Transparent JXL/HEIC images are converted to **WEBP** instead of JPEG,
so transparency is preserved (WEBP compresses much better than PNG for
this case)
- Gated as an **upcoming change** via
`composer_media_optimization_image_convert_enabled` (experimental,
`feature,all_members`, opt-in per group through the upcoming changes
admin UI)
- Sends raw file bytes to Web Worker for formats browsers can't decode
natively (JXL, HEIC), with new `"convert"` and `"convertAnimated"`
worker message types
- Adds JXL to `authorized_extensions` and `supported_images`; adds
HEIC/HEIF to `supported_images` for consistency
- Skips GIF→WEBP when output is larger than input
- Falls back to filename when MIME type is missing (browsers may not
recognize JXL/HEIC)

### Note on jSquash packages

This depends on Discourse-scoped forks of the jSquash packages
(`@discourse/jxl`, `@discourse/heic`, `@discourse/webp`,
`@discourse/gif`, `@discourse/jpeg`, `@discourse/resize`). The following
upstream PRs would let us move back to the canonical `@jsquash/*`
packages, but the upstream maintainer is currently unresponsive:

- jamsinclair/jSquash#101 (`@jsquash/heic`)
- jamsinclair/jSquash#103 (`@jsquash/webp`
animated support)
- jamsinclair/jSquash#104 (`@jsquash/gif`)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

2 participants