Skip to content

Cold-start progress, early WebGPU bail, and Hugging Face Space packaging - #5

Merged
abgnydn merged 1 commit into
mainfrom
feat/hf-space-ready
Aug 3, 2026
Merged

Cold-start progress, early WebGPU bail, and Hugging Face Space packaging#5
abgnydn merged 1 commit into
mainfrom
feat/hf-space-ready

Conversation

@abgnydn

@abgnydn abgnydn commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Preparing to publish as a Hugging Face Space. Three UX fixes the Space audience makes urgent, plus the packaging itself.

Cold start now shows progress

~314 MB comes from R2 and nothing indicated it was happening — getOrFetch buffered the whole body, so the boot overlay sat on a static string for minutes. It now streams: connectome — 78.4 / 125.7 MB (62%).

Measured on a cold IndexedDB: 119 updates for 125.7 MB, 33 for 42.8, ~97 for 140.3 — about one per megabyte. The throttle is load-bearing rather than cosmetic; per-chunk emission would fire thousands of DOM writes a second and measurably slow the download. Streaming is opt-in via an optional callback, so the four call sites passing none keep the original arrayBuffer() path unchanged.

The WebGPU bail moved to the top of main()

This wasn't on the plan and turned out to matter most. The "gpu" in navigator check ran after loadBrain, so a Safari or Firefox visitor downloaded 125 MB and only then learned their browser can't run it. Verified after the change: zero .bin requests on that path, and the dead end links to the landing page, which needs no WebGPU.

On an HF audience — browsed casually, often on mobile, often not in Chrome — that's the most common failure mode, and the one that generates "doesn't work" comments.

Space packaging (space/, kept out of the GitHub README)

  • space/README.md — the YAML frontmatter HF requires as the literal first bytes. app_file: index.html, deliberately not app.html: the Space page frames app_file, and a WebGPU-less visitor landing straight in the simulator sees only a black overlay. index.html explains the project and forwards deep links to the simulator before paint.
  • space/.gitattributes — Hub default minus *.wasm and *.bin. LFS-backed files are served as a 302 to a per-request signed URL with cache-control: no-store, so the 8.6 MB mujoco wasm would re-download on every page load. Plain, it returns inline with an ETag and revalidates as 304.
  • deploy:hf — builds locally, uploads dist/. The nine VITE_* R2 URLs bake in at build time from .env.production, which is gitignored; HF's build job forwards no user variables and Space Variables are runtime-only for static Spaces. Building on HF would bake same-origin defaults that build:slim then deletes — every binary 404s. Building locally makes that structurally impossible. Verified: all nine R2 URLs in the built bundle, no same-origin fallback.

Two more found on the way

  • index.html's pre-paint forwarder matched mode=science but not mode=game, so a shared ?mode=game link stopped at the landing page.
  • Embedded visitors now get a "open in its own tab" link. Inside an iframe the storage bucket is partitioned per-embedder (Safari may block it outright), so the 314 MB IndexedDB cache can silently fail to persist — and src/cache.ts swallows that failure by design.

Assets stay on R2

R2 egress is free. A front-page day at 10k cold visitors is ~3.1 TB; HF publishes no per-Space bandwidth quota, and undocumented limits are the kind you discover mid-spike. HF also sends no cache-control at all on Space assets, so caching would be strictly worse.

Still to verify before announcing

Ten minutes, and nobody has done it: open the Space in Chrome with third-party storage blocked and confirm navigator.gpu and indexedDB are actually available inside the frame. Evidence is strong — WebGPU isn't Permissions-Policy gated, and abgunaydin/draw-instant is already a running WebGPU static Space — but it was never instrumented in an iframe.

tsc clean · unit 5/5 · e2e 34/34.

🤖 Generated with Claude Code

…packaging

Cold start pulls ~314 MB from R2 and showed nothing while it happened —
getOrFetch buffered the whole body, so the boot overlay sat on a static
string for minutes. It now streams and reports per megabyte:
"connectome — 78.4 / 125.7 MB (62%)". Measured on a cold IndexedDB: 119
updates for 125.7 MB, 33 for 42.8, ~97 for 140.3 — about one per MB. The
throttle is load-bearing, not cosmetic; emitting per chunk would fire
thousands of DOM writes a second and measurably slow the download itself.
Streaming is opt-in via an optional callback, so the four call sites that
pass none keep the original arrayBuffer path byte for byte.

The bigger fix was not planned. The `"gpu" in navigator` check ran AFTER
loadBrain, so a Safari or Firefox visitor downloaded 125 MB and only then
learned their browser cannot run this. It is now the first statement of
main(): verified zero .bin requests on that path, and the dead end links to
the plain-language landing page, which needs no WebGPU. On a Hugging Face
audience that is the single most common failure mode.

Hugging Face Space packaging, as a separate space/ directory so none of it
touches the GitHub README:

- space/README.md carries the YAML frontmatter HF requires as the literal
  first bytes of the file. app_file is index.html, not app.html: the Space
  page frames app_file, and a WebGPU-less visitor landing directly in the
  simulator would get nothing but a black overlay. index.html explains the
  project in ~49 KB of plain HTML and forwards deep links to the simulator
  before paint.
- space/.gitattributes is the Hub default minus *.wasm and *.bin. LFS-backed
  files are served as a 302 to a per-request signed URL with no-store, so
  the 8.6 MB mujoco wasm would re-download on every single page load. As a
  plain file it returns inline with an ETag and revalidates as 304.
- deploy:hf builds locally and uploads dist/. The nine VITE_* R2 URLs are
  baked at build time from .env.production, which is gitignored; HF's build
  job forwards no user variables and Space Variables are runtime-only for
  static Spaces, so building on HF would bake same-origin defaults that
  build:slim then deletes — every binary would 404. Building locally makes
  that structurally impossible. Verified: all nine R2 URLs present in the
  built bundle, no same-origin fallback.

Two smaller things found on the way:

- index.html's pre-paint forwarder matched mode=science but not mode=game,
  so a shared ?mode=game link stopped at the landing page.
- The app now tells an embedded visitor to open it in its own tab. Inside
  an iframe the storage bucket is partitioned per-embedder (and Safari may
  block it), so the 314 MB IndexedDB cache can silently fail to persist and
  every visit pays full price. src/cache.ts swallows that failure by design.

The assets stay on R2 rather than moving into the Space repo. R2 egress is
free; a front-page day at 10k cold visitors is ~3.1 TB, HF publishes no
per-Space bandwidth quota, and undocumented limits are the kind you discover
mid-spike. HF also sends no cache-control at all on Space assets.

e2e 34/34, unit 5/5, tsc clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@abgnydn
abgnydn merged commit e859d81 into main Aug 3, 2026
1 check passed
@abgnydn
abgnydn deleted the feat/hf-space-ready branch August 3, 2026 07:35
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.

1 participant