or import it dynamically"}}]}]}

BabylonJS/Babylon.js · error

twgsl is not available.

Error message

twgsl is not available.

What it means

WebGPUTintWASM relies on the external twgsl library (loaded from twgsl.js, which wraps the Tint WASM SPIR-V to WGSL translator) to convert SPIR-V shaders. initTwgsl throws when (self).twgsl is undefined, meaning the twgsl script was not loaded into the page before initialization. Without it, WGSL shaders cannot be produced from SPIR-V in the WebGPU engine.

Source

Thrown at packages/dev/core/src/Engines/WebGPU/webgpuTintWASM.ts:64

            ...twgslOptions,
        };

        if (twgslOptions.twgsl) {
            WebGPUTintWASM._Twgsl = twgslOptions.twgsl;
            return;
        }

        if (twgslOptions.jsPath && twgslOptions.wasmPath) {
            await Tools.LoadBabylonScriptAsync(twgslOptions.jsPath);
        }

        if ((self as any).twgsl) {
            // eslint-disable-next-line require-atomic-updates
            WebGPUTintWASM._Twgsl = await (self as any).twgsl(Tools.GetBabylonScriptURL(twgslOptions.wasmPath!));
            return;
        }

        throw new Error("twgsl is not available.");
    }

    public convertSpirV2WGSL(code: Uint32Array, disableUniformityAnalysis = false): string {
        const ccode = WebGPUTintWASM._Twgsl.convertSpirV2WGSL(code, WebGPUTintWASM.DisableUniformityAnalysis || disableUniformityAnalysis);
        if (WebGPUTintWASM.ShowWGSLShaderCode) {
            Logger.Log(ccode);
            Logger.Log("***********************************************");
        }
        return WebGPUTintWASM.DisableUniformityAnalysis || disableUniformityAnalysis ? "diagnostic(off, derivative_uniformity);\n" + ccode : ccode;
    }
}

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Load twgsl.js before init: add <script src="https://cdn.babylonjs.com/twgsl/twgsl.js"></script> or import it dynamically
  2. Pass correct twgslOptions: { twgslJsPath, twgslWasmPath } pointing to valid, same-origin (or CORS-enabled) URLs
  3. Verify the global is present before init: check window.twgsl / self.twgsl exists
  4. Ensure the .wasm file is served with correct MIME type (application/wasm) and is reachable

Example fix

// before
await WebGPUTintWASM.InitTwgsl(); // twgsl.js never loaded -> throws
// after
await WebGPUFrameGraphRunnerScriptLoader... // or simply:
await ThinEngine._LoadScriptAsync('https://cdn.babylonjs.com/twgsl/twgsl.js');
await WebGPUTintWASM.InitTwgsl({ twgslWasmPath: 'https://cdn.babylonjs.com/twgsl/twgsl.wasm' });
Defensive patterns

Strategy: fallback

Validate before calling

if (typeof (self as any).twgsl !== 'function') {
  await ThinEngine._LoadScriptAsync('https://cdn.babylonjs.com/twgsl/twgsl.js');
}
await WebGPUTintWASM.InitTwgsl({ twgslWasmPath: 'https://cdn.babylonjs.com/twgsl/twgsl.wasm' });

Type guard

function isTwgslAvailable(w: unknown): w is { twgsl: (url: string) => Promise<unknown> } {
  return typeof (w as any)?.twgsl === 'function';
}

Try / catch

try {
  await WebGPUTintWASM.InitTwgsl(twgslOptions);
} catch (e) {
  if (e instanceof Error && e.message === 'twgsl is not available.') {
    await ThinEngine._LoadScriptAsync(twgslJsUrl); // lazy-load then retry once
    await WebGPUTintWASM.InitTwgsl(twgslOptions);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling WebGPUTintWASM.InitTwgsl (or creating a WebGPUEngine that needs shader conversion) when the twgsl script (twgsl.js) was never included / loaded via script tag or dynamic import.

Common situations: Bundlers tree-shaking or not copying twgsl.js/wasm assets; missing twgslUrl/twgslJsPath/twgslWasmUrl options in WebGPUEngine creation; CORS or 404 when loading twgsl.js from CDN; offline environments where the WASM fetch fails so the global never registers.

Related errors


AI-assisted analysis of BabylonJS/Babylon.js@0592b347b8 (2026-08-30). Data as JSON: /api/errors/07c2861a390d057d. Report an issue: GitHub.