Skip to content

feat: resolve remote example assets into the WASM VFS #215

Closed

Description

@zackees

Summary

FastLED example assets are moving out of the FastLED source tree into FastLED/assets, referenced by URL and resolved at build time (FastLED/fbuild#1354). The WASM preview path needs to resolve those same remote URLs into its virtual filesystem, or sketches with remote assets will compile but show nothing in the browser preview.

Background

A sketch's data/ directory is currently bundled into the emscripten build and surfaced through the VFS (src/fastled/frontend/vfs_bridge.ts). That works because the files are physically present in the repo checkout.

Once assets are remote, data/ may contain only a reference (e.g. assets.json with {url, sha256, size_bytes} entries) rather than the payload. The preview must fetch and mount the real bytes.

Concrete first case — examples/Fx/FxLedmapper32x32:

  • video1.rgb — 6,051,840 bytes, 1,970 frames at 32x32 RGB888
  • screenmap.json — 13,790 bytes, loaded via filesystem.readScreenMap("data/screenmap.json", "strip1", ...)

The sketch opens both by path (data/video1.rgb, data/screenmap.json), so whatever the resolution strategy, those paths must still exist in the VFS at runtime.

Design questions

Two viable strategies, and this issue should pick one:

A — Build-time materialization. fbuild fetches assets before the emscripten build; the WASM side stays unchanged. Simplest, keeps one code path with hardware builds, but every preview build carries the full payload and the browser downloads it as part of the bundle.

B — Runtime VFS fetch. vfs_bridge.ts fetches the URL on demand and mounts it into the VFS at the expected path. Keeps the WASM bundle small and lets the CDN serve the payload with proper caching — a real advantage for a 6 MB video. But it introduces async loading before setup() can succeed, needs a loading state, and diverges from the hardware path.

Recommendation: B, with A as fallback. A 6 MB payload inlined into every preview bundle is exactly the cost a CDN exists to avoid. But B requires the sketch's filesystem.beginSd() / openVideo() sequence to tolerate assets that are not yet resolved — worth confirming against fl::FileSystem semantics before committing.

Requirements

  • Resolve remote asset references into the VFS at the sketch's expected paths
  • Verify sha256 from the manifest before mounting; fail visibly on mismatch
  • Serve from CDN (jsDelivr) with raw.githubusercontent.com as documented fallback
  • Surface a loading state for large assets rather than appearing to hang
  • Cache in the browser so a reload does not re-fetch 6 MB
  • Graceful, visible failure when an asset is unreachable — not a silent black canvas

Related

Activity

  1. zackees commented on Aug 22, 2026

    @zackees
    MemberAuthor

    Format decided: .lnk sidecar, key=value

    The remote-asset declaration is a sidecar named after the asset:

    data/video1.rgb.lnk   ->  materializes  data/video1.rgb
    
    # Remote asset descriptor.
    url=https://cdn.jsdelivr.net/gh/FastLED/assets@main/examples/FxLedmapper32x32/video1.rgb
    url=https://raw.githubusercontent.com/FastLED/assets/main/examples/FxLedmapper32x32/video1.rgb
    sha256=164804d94802dd96cd8cd37f7528ca6d30f8fd9396e812fe67ec6164bbdd2282
    size_bytes=6051840
    

    url may repeat (first primary, rest fallbacks). # starts a comment. Unknown keys are ignored so the format can grow. Full spec: FastLED/fbuild#1354.

    What this means for the VFS bridge

    vfs_bridge.ts will encounter video1.rgb.lnk in a sketch's data/ where the sketch expects video1.rgb. Under strategy B (runtime fetch), the bridge should:

    1. Detect <name>.lnk entries during VFS population.
    2. Parse the key=value set; fetch url entries in order (jsDelivr first — it is the CDN and sends proper cache headers).
    3. Verify sha256 before mounting; fail visibly on mismatch.
    4. Mount the bytes at <name> — not <name>.lnk — so filesystem.openVideo("data/video1.rgb", ...) resolves unchanged.
    5. Cache so reload does not re-fetch 6 MB.

    Under strategy A (build-time materialization) the bridge needs no change, because fbuild will have replaced the .lnk with the real file before the emscripten build. The .lnk format works for either strategy — this issue only needs to decide which.

    The live case is examples/Fx/FxLedmapper32x32, whose video1.rgb (6,051,840 bytes) is already hosted in FastLED/assets and removed from the FastLED repo.

  2. zackees commented on Aug 22, 2026

    @zackees
    MemberAuthor

    Tracked in the meta issue: FastLED/FastLED#3986 — remote assets: hosting, build-time resolution, and on-device filesystem deployment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions