# WebAssembly
Ely builds for `wasm32-unknown-unknown` through `gpui_web`, Zed's browser platform for GPUI. The components page runs Ely's own gallery that way, in a canvas inside the page.
## Check the build
```sh
rustup target add wasm32-unknown-unknown
RUSTC_BOOTSTRAP=1 cargo check --lib --target wasm32-unknown-unknown
```
`gpui_web` uses an unstable feature for its threads, as Zed's own web examples do, so the check needs `RUSTC_BOOTSTRAP=1`.
## Start an app in the browser
The gallery's [web entry](https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components/src/branch/main/examples/gallery/web.rs) shows the steps:
| Step | Why |
| --- | --- |
| `#[wasm_bindgen(start)]` on the entry | The browser runs it when the module loads. |
| `gpui_platform::web_init()` | Sends panics and logs to the browser console. |
| `WebPlatform::new_with_backend_and_font_fallback(false, WebBackendPreference::Auto, CanvasFontFallback::EmojiAndCjk)` | One thread, since a static site gets no cross-origin isolation; WebGPU where the browser has it, WebGL2 where it does not; the browser lends fonts for emoji and CJK alone. |
| `Application::with_platform(platform)` | Then `with_http_client(Arc::new(platform.fetch_http_client()))`, which fetches pictures by address, and `with_assets(Assets)` and `run`, as on the desktop. |
## Build the gallery
`ELY_GALLERY_ASSETS=
/ scripts/web.sh ` builds the gallery with the `web` profile, runs `wasm-bindgen` and `wasm-opt -Oz`, and copies its page beside it and its pictures into `/assets/`. It needs the `wasm-bindgen` CLI at the version in `Cargo.lock` and binaryen's `wasm-opt`, and it fails when the wasm passes Cloudflare's 25 MiB. `ELY_GALLERY_ASSETS` is the address `/assets/` is served from, ending in `/`: GPUI reads no files in a browser.
The gallery opens at `?page=&story=&theme=light|dark`. A story needs its page.
## Embed it in a page
| Message | Direction |
| --- | --- |
| `gallery.setTheme(mode)`, or `postMessage({ ely: "theme", theme })` | From the host, before the wasm runs or after. |
| `{ ely: "ready" }` | To the parent, once the gallery runs. |
| `{ ely: "failed", error }` | To the parent, after a start that failed three times. |
Until a press or a key reaches it, the embedded gallery keeps GPUI's hidden text field from taking focus, so a starting frame leaves the host page's focus alone. Tab inside GPUI cycles within its window; a host gives a way past the frame, as the components page does.
## What differs on the web
| Area | On the web |
| --- | --- |
| Threads | One in the gallery: more need a page served with cross-origin isolation. |
| Pictures | Fetched by address, never read from files. |
| Time | `web_time::Instant`; std's panics in a browser. |
| Paste | Arrives as the browser's paste event. Code that reads the clipboard itself awaits `read_from_clipboard_async`. |
| Accessibility | GPUI builds no accessibility tree in the browser. |
| Native only | The live terminal and the gallery's capture tool do not build for WebAssembly. |
| macOS only | The tray icon, the Dock badge and the web view return an error or say so, as on Windows and Linux. |