# Hosting Craft Anything for the web `craft-anything-web-.zip` (from the GitHub release, or `packaging/web/package.sh`) holds a static site in `craft-anything-web-/`: | File | What it is | |---|---| | `index.html` | The page. It loads everything through relative URLs. | | `craft-anything-web-.js` | wasm-bindgen glue (generated, ES module) | | `craft-anything-web-_bg.wasm` | The app | | `_headers`, `.htaccess` | Sample header rules for Netlify/Cloudflare Pages and Apache | There is no server-side code and no hand-written JavaScript: the glue is generated by wasm-bindgen from the Rust sources (`apps/craft-anything-web`). Upload the folder's contents anywhere that serves static files. The web viewer runs entirely on the visitor's device: a dropped or picked file is read in the browser and never uploaded. It uses the built-in, pure-Rust analyzers only (formats, disassembly, JavaScript bundles); running programs, external engines and AI backends need the desktop app. A browser with WebGPU or WebGL2 is required. ## Size `packaging/web/package.sh` fails the build if the wasm grows past 24 MiB: Cloudflare Pages and Workers static assets reject any file over **25 MiB**, so a release can't ship a file a common host refuses. ## Required server settings - **MIME type:** serve `.wasm` as `application/wasm`. Browsers refuse to stream-compile it under any other type, and the app then loads slowly or not at all. Serve `.js` as `text/javascript`. Most hosts already do both. For nginx, check that `mime.types` has `application/wasm wasm;`. - **Compression:** turn on gzip or Brotli for `.wasm`, `.js` and `.html`. Brotli usually shrinks the wasm to a quarter of its size. You can also precompress (`brotli -k *.wasm`) and let the server send `Content-Encoding: br`. - **Caching:** `Cache-Control: public, max-age=31536000, immutable` on the hashed `.wasm` and `.js` files, and `no-cache` on `index.html`. - **HTTPS:** WebGPU (and the clipboard) only work in a secure context, which means `https://` or `http://localhost`. Over plain HTTP elsewhere, the app falls back to WebGL2. - **No special isolation headers:** Craft Anything doesn't use `SharedArrayBuffer`, so it doesn't need `Cross-Origin-Opener-Policy` or `Cross-Origin-Embedder-Policy`. If your site already sends COEP `require-corp`, also send `Cross-Origin-Resource-Policy: same-origin` (or `cross-origin` when the files live on a CDN) on the app's files. nginx example: ```nginx location /craft-anything/ { types { application/wasm wasm; text/javascript js; text/html html; } gzip on; gzip_types application/wasm text/javascript text/html; location ~* \.(wasm|js)$ { add_header Cache-Control "public, max-age=31536000, immutable"; } location ~* index\.html$ { add_header Cache-Control "no-cache"; } } ``` Local test: `python3 -m http.server 8767` inside the folder, then open http://localhost:8767/. ## Embedding in a page (iframe) ```html ``` - The app fills the iframe and follows its size, so size the iframe and not the app. - Keyboard shortcuts go to the iframe after the user clicks into it, as with any embedded app. - **Sandboxed iframes** need at least `sandbox="allow-scripts allow-same-origin allow-popups"` (`allow-popups` lets Help links open in a new tab). - Don't send `X-Frame-Options: DENY` or a `frame-ancestors` CSP that excludes the embedding page. ## Renderer Craft Anything renders with wgpu: **WebGPU** when the browser has it, otherwise **WebGL2**. ## Smoke test (headless Chrome) Headless Chrome has no WebGPU adapter, so it runs on WebGL2 through SwiftShader: ```sh "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --headless=new \ --use-angle=swiftshader --enable-unsafe-swiftshader --window-size=1440,900 \ --timeout=30000 --screenshot=craft-anything-web.png "http://127.0.0.1:8767/" ```