Docs: the web hosting guide matches what the build ships (#164, #165, #174, #180)

- #174: the file names are fixed (effectcraft_web.js, effectcraft_web_bg.wasm, worker.js,
  sw.js…), not content-hashed, so the guide's year-long immutable caching kept returning
  visitors on the old release after an in-place upgrade. The guide, nginx example, _headers
  and .htaccess now revalidate (no-cache) the .wasm, .js and index.html, and suggest a
  versioned path for longer caching. The file table lists the real files.
- #180: the .wasm is about 58 MB (17 MB gzip, 10 MB Brotli) for v0.3.1, not 13 MB / 5 MB;
  docs/web.md's 38 MB figure is updated too.
- #165: preferences live in the origin's browser storage (OPFS, IndexedDB or memory), not
  localStorage.
- #164: relative URLs come from the page itself (and package.sh's check), not a Trunk.toml
  that doesn't exist.
The iframe example's title no longer says image editor.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
bflatasticandClaude Opus 5.5 committed 2026-10-07 13:56:23 +07:00
1 parent b09f06f417
commit 120c7ec4e1
4 files changed
+37 -26

No files matched your search

+2 -4
View File
@@ -7,10 +7,8 @@ AddType text/javascript .js
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(wasm|js)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
<FilesMatch "^index\.html$">
# The file names don't change between releases: revalidate them (HOSTING.md).
<FilesMatch "(\.(wasm|js)|^index\.html)$">
Header set Cache-Control "no-cache"
</FilesMatch>
</IfModule>
+27 -17
View File
@@ -6,29 +6,38 @@ static site in `effectcraft-web-<version>/`:
| File | What it is |
|---|---|
| `index.html` | The page. It loads everything through relative URLs. |
| `effectcraft-web-<hash>.js` | wasm-bindgen glue (generated, ES module) |
| `effectcraft-web-<hash>_bg.wasm` | The app, about 13 MB, or 5 MB with compression |
| `effectcraft_web.js` | wasm-bindgen glue (generated, ES module) |
| `effectcraft_web_bg.wasm` | The app: about 58 MB, about 17 MB with gzip and 10 MB with Brotli (v0.3.1) |
| `snippets/…/js/host.js` | Browser glue (storage, audio, workers) |
| `worker.js`, `audio-worklet.js` | Render and frame workers, and audio playback |
| `sw.js`, `manifest.webmanifest`, icons | Offline install: the service worker and the app manifest |
| `_headers`, `.htaccess` | Sample header rules for Netlify/Cloudflare Pages and Apache |
There is no server-side code. Upload the folder's contents anywhere that serves static files.
## Any path works
All URLs in `index.html` are relative (`public_url = "./"` in `apps/effectcraft-web/Trunk.toml`),
so the site works at a domain root (`https://example.com/`), under a prefix
(`https://example.com/tools/effectcraft/`) and from a CDN bucket. The asset names carry a content
hash, so they can be cached forever. Only `index.html` needs revalidation.
All URLs in `index.html` are relative: the page is written that way
(`apps/effectcraft-web/web/index.html`), and `package.sh` refuses a build with root-absolute
URLs. So the site works at a domain root (`https://example.com/`), under a prefix
(`https://example.com/tools/effectcraft/`) and from a CDN bucket.
The file names are the same in every release (`effectcraft_web_bg.wasm` and so on), so browsers
must revalidate them: a long-lived cache would keep returning visitors on the old version after
you upgrade in place. Revalidation is cheap (an unchanged file answers `304 Not Modified`), and
the service worker keeps the app for offline use after the first visit. To cache the files for
longer, serve each release from its own versioned path (`/effectcraft/0.4.0/`).
## 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`. That takes the
download from about 13 MB to about 5 MB. 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`.
- **Compression:** turn on gzip or Brotli for `.wasm`, `.js` and `.html`. For v0.3.1 that takes
the download from about 58 MB to about 17 MB (gzip) or 10 MB (Brotli). You can also
precompress (`brotli -k *.wasm`) and let the server send `Content-Encoding: br`.
- **Caching:** `Cache-Control: no-cache` (revalidate) on `index.html`, `sw.js` and the `.wasm`
and `.js` files, since their names don't change between releases (see above).
- **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:** EffectCraft doesn't use `SharedArrayBuffer`, so it doesn't
@@ -43,8 +52,7 @@ location /effectcraft/ {
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"; }
location ~* (\.(wasm|js)|index\.html)$ { add_header Cache-Control "no-cache"; }
}
```
@@ -55,7 +63,7 @@ Local test: `python3 -m http.server 8765` inside the folder, then open http://lo
```html
<iframe
src="https://example.com/effectcraft/"
title="EffectCraft image editor"
title="EffectCraft"
style="width: 100%; height: 720px; border: 0;"
allow="fullscreen; clipboard-read; clipboard-write"
allowfullscreen>
@@ -64,9 +72,11 @@ Local test: `python3 -m http.server 8765` inside the folder, then open http://lo
- 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.
- **Cross-origin embeds** work. Preferences are kept in the iframe's `localStorage`. Browsers
that partition or block third-party storage may forget them between visits, and the app
then starts with defaults.
- **Cross-origin embeds** work. Preferences, projects and imported media are kept in the
iframe origin's browser storage: the Origin Private File System, else IndexedDB, else memory
only (`effectcraft.info().storage.backend` says which; see `docs/web.md` › Persistence).
Browsers that partition or block third-party storage may forget them between visits, and the
app then starts with defaults.
- **Sandboxed iframes** need at least
`sandbox="allow-scripts allow-same-origin allow-downloads allow-popups"`. Without
`allow-same-origin` there's no storage. Without `allow-downloads`, Save and Export (browser
+3 -2
View File
@@ -2,12 +2,13 @@
/*
X-Content-Type-Options: nosniff
# The file names don't change between releases: revalidate them (HOSTING.md › Any path works).
/*.wasm
Content-Type: application/wasm
Cache-Control: public, max-age=31536000, immutable
Cache-Control: no-cache
/*.js
Cache-Control: public, max-age=31536000, immutable
Cache-Control: no-cache
/index.html
Cache-Control: no-cache