Skip to content

Conversation

@ppmpreetham
Copy link

This pull request introduces WebAssembly (WASM) support for the project, allowing the library to be compiled and used in web environments. Key changes include the addition of a new wasm feature, configuration updates for WASM builds, documentation for building with WASM, and the implementation of a WASM-compatible API in Rust.

WASM Support and Configuration:

  • Added new dependencies (wasm-bindgen, web-sys, console_error_panic_hook) and a wasm feature to Cargo.toml to enable WASM compilation and browser-specific functionality.

  • Introduced WASM-specific build profiles and optimization flags in both Cargo.toml and the new wasm-pack.toml to improve performance and control build output.
    Codebase Enhancements:

  • Added a new wasm.rs module implementing the GifskiWasm struct and exposing a WASM-friendly API for creating GIFs, including methods for initialization, adding frames, and finalizing output.

  • Updated src/lib.rs to conditionally include and re-export the wasm module and GifskiWasm struct when the wasm feature is enabled.

Documentation:

  • Expanded README.md with instructions for building the library for WebAssembly using wasm-pack and the new wasm feature.

Copy link
Member

@kornelski kornelski left a comment

Choose a reason for hiding this comment

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

The API won't buffer unlimited number of frames in memory. It's designed for streaming: writing out frames while taking new ones. Both must happen in parallel.

You can't add frames first and then start writing — that will deadlock.

src/wasm.rs Outdated

let img = ImgVec::new(pixels, width as usize, height as usize);

self.collector.add_frame_rgba(0, img, timestamp)
Copy link
Member

Choose a reason for hiding this comment

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

This is incorrect usage of the API. After a few frames this call will start blocking until write() is called from another thread.

@ppmpreetham
Copy link
Author

@kornelski Thanks for the feedback.
For the WASM target, I'm using a hybrid approach:

Buffering phase: I buffer all frames in a Vec<(ImgVec<RGBA8>, f64)> during add_frame_rgba() calls - no encoder interaction yet

Encoding phase: In finish(), I create the collector/writer, spawn a writer thread, add all buffered frames, then drop the collector and wait for the writer to complete

This approach works because:

  • The collector only starts receiving frames when all frames are already in memory
  • The writer thread runs in parallel while frames are being added
  • No deadlock occurs since both operations happen simultaneously in finish(), not sequentially
pub fn finish(self) -> Result<Vec<u8>, JsValue> {
    let (collector, writer) = crate::new(self.settings)?;
    
    // writer thread starts immediately
    let writer_handle = std::thread::spawn(move || {
        writer.write(&mut output, &mut NoProgress {})
    });
    
    // adds all buffered frames while writer is running
    for (index, (img, timestamp)) in self.frames.into_iter().enumerate() {
        collector.add_frame_rgba(index, img, timestamp)?;
    }
    drop(collector);
    
    writer_handle.join()??;
    Ok(output)
}

This requires WASM threads (via wasm-bindgen-rayon with SharedArrayBuffer + COOP/COEP headers). For simpler WASM use without threading support, would you recommend any alternative approach, or is buffering + threading the only viable solution?

This implementation requires browser support for SharedArrayBuffer and proper COOP/COEP headers. For broader browser compatibility without these requirements, is there a synchronous/single-threaded encoding path I could use, accepting the memory trade-off of buffering all frames?

@ppmpreetham ppmpreetham requested a review from kornelski January 27, 2026 14:58
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