-
Notifications
You must be signed in to change notification settings - Fork 31
Add WebAssembly/js bindings (exrs-wasm) #263
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add WebAssembly/js bindings (exrs-wasm) #263
Conversation
Introduces exrs-wasm package with JavaScript-friendly APIs for creating multi-layer EXR files with AOVs (beauty, depth, normals) from WebGL/WebGPU render buffers. Includes GitHub Actions workflow for npm publishing. Co-Authored-By: Claude <[email protected]>
Interactive demo page showcasing: - ExrImage API for multi-layer EXR creation - All layer types: RGBA, RGB, depth, single-channel - Compression method and precision controls - ExrSequenceWriter for animation frames - Canvas previews and file downloads Co-Authored-By: Claude <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this pull requests! While I generally approve the idea to add js bindings, there are some big things I would want to discuss with you first.
The most important change would be to include reading of images in the first version, not as a later addition. This is important to make roundtrip tests and to avoid building an architecture which prevents adding the reading of images.
One example is compression being in the top level image structure in your PR, but it actually belongs into the layer. Or the ChannelType enum, which probably makes reading more complicated than it needs to be. This problem and other problems will surface when reading is added. That's why I propose we do deciding in the first version now.
Furthermore I would ask you to reduce the total amount of code needed. The html file contains an example for developers, yet I would think that the code should be easy to read and understand.
One last thing that I would prefer is to wrap the generated JavaScript in a clean handwritten API, which hides the details such as init() and free().
I know this is quite a lot of feedback. This is only because the exr library is already used in the public by some projects. Adding JavaScript bindings should be solid even in the first version if we do it in this repository.
You also have the choice to publish it independently of me, in your own account, then you can experiment more and I won't talk your ear off about architecture. I would think js bindings are a cool addition to the project, so adding it here would also be appreciated, if you want to address the feedback.
|
Great! That's all straightforward feedback and I'm happy to implement. Appreciate you taking a look and appreciate the initial code. Thanks for the pointers on the compression. I was thinking the html file was more about showing a quick demo to smoke test / help users understand more than it is about showcasing the API but I'm fine to remove. I'll have limited access to my computer this weekend but I'll try to get to this when I have some downtime. FWIW I created a js-native port at @akre54/exr-js which might be interesting to folks too. It has reading and writing. Let me know if there's anything from there that you'd like me to port here too. I see them as related but orthogonal projects. |
|
Ok updated based on your feedback. Mind taking another look? I left the example html file in there but if you would rather I remove it I'm fine with that too.
Done!
|
- Add EXR reading API (readExr, ExrReadResult) - Add U32 sample precision support - Move compression setting to per-layer level - Add convenience writing functions (writeExrRgba, writeExrRgb, writeExrSingleChannel) - Remove ExrSequenceWriter and addDepthLayer - Add JavaScript integration tests via wasm-bindgen-test - Update CI to run JavaScript tests - Update example HTML for new simplified API Co-Authored-By: Claude <[email protected]>
|
Awesome! I will have a look as soon as possible |
It's nice to have an example, but I would prefer to have it shorter. Or at least, another file with a short example. I see you added the convenience wrappers, but I had hoped to have a Javascript layer which we can write by hand. That way, we could easily build a pass-by-value single function for encoding images. I have tried this approach and it is not so easy, so I understand the hesitation. Perhaps using the serde typescript wrapper will make the API nice enough that we don't need an additional javascript layer? The tests could be in a separate (otherwise unused) npm package subfolder which is never published and uses the generated npm package as dependency. I want to propose that we work on an entirely different branch than master, such that we may collaborate and experiment on that branch with smaller PRs, instead of having one huge PR. What do you think? |
|
Yes totally fine by me. I'll cut it down and happy to work off of another
branch. Would you like to open PRs against my branch? Or start a different
one?
I'll look into the specifics of the other questions in the next couple of
days. Thanks for taking a look!
… Message ID: ***@***.***>
|
|
|
Yeah let's do it in this branch, I don't see any reason why not :) We can both open PRs against this branch to propose more complex ideas. Perhaps simple ideas we could do in this branch directly, but I have no strong opinion on that |
- Rename ExrImage → ExrEncoder, ExrReadResult → ExrDecoder for symmetry - Change f32 → f64 in JS API (JS operates in f64 natively) - Add optimized readExrRgba/readExrRgb using SpecificChannels - Add hand-written JS wrapper (js/index.js) hiding init()/free() - Add TypeScript definitions (js/index.d.ts) - Add README.md with documentation and examples Co-Authored-By: Claude <[email protected]>
- Remove ChannelType enum, store AnyChannels<FlatSamples> directly - Build channels at add_*_layer time instead of at encode time - Add tests-js/ subfolder as separate npm package for JS integration tests - Update native tests to use public API Co-Authored-By: Claude <[email protected]>
- Make encode/decode functions synchronous (require init() first) - getData() now auto-detects RGBA/RGB/single channel from layer contents - Add getChannel(name) for explicit channel access - Update TypeScript definitions, tests, and README Co-Authored-By: Claude <[email protected]>
…cribe all javascript content to typescript
|
I proposed changes in your fork at akre54#1. Mainly this PR aims to make the wrapper a proper npm package. This way, we can have a cleaner build and publishing process. |
…bgl-aov-export-jv make the wrapper a proper npm package
|
Merged akre54#1! |
M comment had a logic flaw. If you never had a number[], only FloatArrayBuffer, then it actually does save space at the cost of precision in JavaScript. So forcing f64 might be unnecessary memory usage. Instead we should probably revert to f32, or allow both variants side by side. |
|
The workflow fails because I didn't test it locally, so this was kind of expected, I will try to fix it directly in this branch |
Since the API uses Float32Array (from WebGL) rather than JS number[], using f64 wastes memory without benefit. Reverts the API to use f32 throughout for encoding/decoding pixel data. Co-Authored-By: Claude <[email protected]>
|
another big refactoring is over at your fork, akre54#2 (reduces code size by simplifying internal logic). i'd like to know what you think about this |
|
Thanks! Left a review |
|
once we merge the pr at your fork, I'd be happy with the API for V1, and we can try an initial release. Do you have a use case at hand to test whether this will work for you? |
…bgl-aov-export-jv2 generalize channel interleaving and deinterleaving
|
I added an npm based example which seems to work |
|
Okay, so there are some open points we can improve later as a follow-up:
I will create issues for those. I will now merge the PR and publish the NPM package. I would appreciate if you test the package when you have the time :) |
|
Thanks for your time and your contributions! It was a pleasure to work with you. |
|
For some reason you are not listed as collaborator on https://www.npmjs.com/package/exrs - but why...? you are recorded in the collaborators field in |
|
Likewise! And no worries. Thanks for all the work you put in. I'm just glad this is now available |
|
Thanks! I added a contributors section in the README :) |

Checklist
out of scope:
This PR adds
exrs-wasm, an npm package that provides JavaScript/TypeScript bindings for writing EXR files from the browser. This enables web-based rendering tools to export multi-layer EXR files with AOVs directly from WebGL/WebGPU contexts.Related issues:
default-features = falseto disable rayon, enabling WASM compilationMotivation
The
exrscrate already compiles to WASM (as noted by the "Wasm Ready" badge in the README), but there was no JavaScript API to actually use it from the browser. This PR bridges that gap by providing ergonomic JS bindings.Use cases:
Changes
New files
exrs-wasm/- New WASM bindings crate:Cargo.toml- Crate config usingexras a path dependency withdefault-features = falsesrc/lib.rs- Full implementation (~600 lines).github/workflows/wasm-publish.yml- CI workflow to publish to npm onwasm-v*tagsModified files
.gitignore- Addedexrs-wasm/target/andexrs-wasm/pkg/build artifactsAPI
Features
console_error_panic_hookFor maintainers
This PR is designed to be reviewed and potentially modified before merging. Some decisions for maintainers to consider:
exrs-wasm- you may want to change to@exrs/wasmor similar for npm namespace0.1.0- you may want to sync with main crate versionsecrets.NPM_TOKEN- you'll need to set this upjohannesvollmer/exrs- correct for your repo0.2.100for compatibility - may need updatingTest plan
cargo testinexrs-wasm/)wasm-pack build --target web)🤖 Generated with Claude Code