Questions about ESM + WASM in PlayCanvas published builds

Hi, I’m trying to load the wasm + esm library into a publish build. It works in the editor, but it doesn’t work in the build.

  1. ESM asset URL is not importable in published build
    In the Editor I can do import('/api/.../libktx.mjs') and it works. But in the published build the same asset is served from .../js/esm-scripts/.../libktx.mjs on S3 and that URL returns 403, so dynamic import() fails. Is this S3 ESM path intentionally non-public? Is there a supported way to make an ESM asset importable in a published build?

  2. Exposing ESM to globalThis
    Since the new ESM system removes hidden globals, is it acceptable/supported to add at the end of my own ESM module (for example libktx.mjs):

globalThis.createKtxModule = createKtxModule;

so that other scripts can use it in the published build (where import() is not possible)? Or is there a recommended / official way to expose functionality from an ESM script to the global scope?

  1. Custom WASM + JS glue flow
    Docs show how to use WASM Module assets, but how should we register a custom WASM + JS glue pair (like libktx.wasm + libktx.mjs) so that PlayCanvas loads the JS glue first, and then we can call the factory from our game scripts? Is there an example of doing this with a user-provided ESM glue file?

  2. Keep ESM in /files/assets/
    Is there any way (asset type or setting) to force an ESM script to stay under /files/assets/... on publish instead of being moved to js/esm-scripts/...? The goal is to fetch/import it like a normal file. If not, is hosting the ESM on an external CDN the recommended workaround?

  3. Load order for external ESM
    If we host libktx.mjs externally and do globalThis.createKtxModule = ... there, what is the supported way to make sure this script runs before the PlayCanvas scene ESM scripts in the published build?

@Mark_Lundin might know

1 Like

@SashaRX I’m not sure about the WASM, but you can use a relative path to import modules.

// file ./utils/dep.mjs
export const N = 10;
// file ./script.mjs
import { Script } from 'playcanvas'

export class Test extends Script {
  async initialize() {
    const { N } = await import('./utils/dep.mjs')
  }
}

This should work whether you import statically or dynamically. Let me know if there’s an issue

1 Like

I can add that you can expose your lib instance under globalThis. It is ok.

As for custom Wasm and glue - don’t use the engine one. It is only for Emscripten builds. If you use anything else, then just instantiate it normally, how you would do it in vanilla js. You can locate your Wasm and glue file in the asset registry of your app, if you are using Editor.

1 Like