diff --git a/create_main.sh b/create_main.sh
index 181d200408f71c4ce4c033357093a41d68c69bb8..5b41dafe46aae631a908b82e60b2e8a1f05ef409 100755
--- a/create_main.sh
+++ b/create_main.sh
@@ -40,7 +40,7 @@ emcc --bind -s USE_PTHREADS=1 -s TOTAL_MEMORY=2GB -s PTHREAD_POOL_SIZE=25 \
     -s USE_WEBGL2=1 \
     --profiling-funcs \
     -s OFFSCREENCANVAS_SUPPORT=1 \
-    -s MODULARIZE=1 -s EXPORT_NAME="initModule" \
+    -s MODULARIZE=1 -s EXPORT_ES6=1 \
     -s EXPORTED_RUNTIME_METHODS="[allocateUTF8, writeAsciiToMemory]" \
     -s ASYNCIFY=1 -O3 \
     -s EXIT_RUNTIME=1 -s ASSERTIONS=1 \
diff --git a/lib/libvlc.js b/lib/libvlc.js
index 8aa8bff73f0d93e3411acc9b7e42e5214bef3159..ac98a858b48db145dd2c8cba035d9c3470204293 100644
--- a/lib/libvlc.js
+++ b/lib/libvlc.js
@@ -1,8 +1,19 @@
-// Encapsulate functions exported from exports_*.c
+// Copyright 2021 - 2025, Videolabs and the Videolan contributors
+// SPDX-License-Identifier: LGPL-2.0-or-later
+
+class InvalidRuntime extends Error {
+    constructor() {
+        super("LibVLC runtime is invalid");
+        this.name = this.constructor.name;
+    }
+}
 
 // Encapsulates libvlc_media_player_t
 export class MediaPlayer {
   constructor(module, path) {
+    if (module === undefined)
+      throw new InvalidRuntime;
+
     this.module = module;
     this.media_player_ptr = module._wasm_media_player_new();
     module._attach_update_events(this.media_player_ptr);
@@ -214,10 +225,12 @@ export class MediaPlayer {
   }
 }
 
-
 // Encapsulates libvlc_media_t
 export class Media {
   constructor(module, path, raw_ptr) {
+    if (module === undefined)
+      throw new InvalidRuntime;
+
     if (raw_ptr != null) {
       this.module = module;
       this.media_ptr = raw_ptr;
diff --git a/libvlc-loader.js b/libvlc-loader.js
new file mode 100644
index 0000000000000000000000000000000000000000..52c80b8220e737704a5d2dc0236e9ef80ecd2e10
--- /dev/null
+++ b/libvlc-loader.js
@@ -0,0 +1,14 @@
+// Copyright 2025 - 2025, Videolabs and the Videolan contributors
+// SPDX-License-Identifier: MIT
+
+export { MediaPlayer, Media } from "./lib/libvlc.js";
+
+export async function initRuntime(ModuleExt) {
+    try {
+        const EmscriptenModule = await import('./experimental.js');
+        return await EmscriptenModule.default(ModuleExt);
+    } catch (e) {
+        console.error(`Couldn't load libvlc runtime: ${e}`);
+        return undefined;
+    }
+}
diff --git a/vlc.html b/vlc.html
index 0484682f4c82d86572b87b4d574d9a08cd10b34b..04f95a8c42555444bfc4dfdc9a1da9202dbc4c40 100644
--- a/vlc.html
+++ b/vlc.html
@@ -107,10 +107,9 @@
       </div>
     </div>
     <script src="./lib/module-loader.js"></script>
-    <script src="./experimental.js"></script>
     <script type="module">
       import { update_overlay, on_overlay_click } from "./lib/overlay.js";
-      import { MediaPlayer } from "./lib/libvlc.js";
+      import { initRuntime, MediaPlayer } from "./libvlc-loader.js";
 
       var vlc_options = "";
 
@@ -204,30 +203,24 @@
           }
         });
 
-        try {
-          // VlcModule is a function generated by emscripten in experimental.js,
-          // that loads the wasm file and generates a module object from it.
-          // VlcModuleExt is an object defined in 'lib/module-loader.js' with a
-          // bunch of options; also, all the fields of VlcModuleExt are added to
-          // the returned Module.
-          globalThis.Module = await initModule(VlcModuleExt);
-          createHandlers();
-          localStorage.setItem('valid-options', options.value);
-        } catch (e) {
-          const validOptions = localStorage.getItem('valid-options');
-          localStorage.setItem('options', validOptions);
-          window.location.reload();
+        const Emscripten = await initRuntime(VlcModuleExt);
+        if (Emscripten !== undefined) {
+            createHandlers(Emscripten);
+            globalThis.Module = Emscripten;
+        }
+        else {
+            body.dispatchEvent(new CustomEvent('isLoading', { detail: { loading: false } }));
         }
       }
 
-      function createHandlers() {
+      function createHandlers(Emscripten) {
         window.onerror = function(event) {
           // TODO: do not warn on ok events like simulating an infinite loop or exitStatus
-          Module.setStatus('Exception thrown, see JavaScript console');
+          Emscripten.setStatus('Exception thrown, see JavaScript console');
           // spinnerElement.style.display = 'none';
           body.dispatchEvent(isNotLoading);
-          Module.setStatus = function(text) {
-            if (text) Module.printErr('[post-exception status] ' + text);
+          Emscripten.setStatus = function(text) {
+            if (text) Emscripten.printErr('[post-exception status] ' + text);
           };
         };
 
@@ -236,7 +229,7 @@
           for (var i=0; i < this.files.length; i++) {
             var name = this.files.item(i).name;
             console.log("opened file: ", name);
-            Module['vlc_access_file'][1] = this.files.item(i);
+            Emscripten['vlc_access_file'][1] = this.files.item(i);
           }
 
           globalThis.files = this.files;
@@ -262,16 +255,16 @@
           vlc_opts_size += vlc_opts_array[i].length + 1;
         }
 
-        var buffer = Module._malloc(vlc_opts_size);
+        var buffer = Emscripten._malloc(vlc_opts_size);
         var wrote_size = 0;
         for (let i in vlc_opts_array) {
-          Module.writeAsciiToMemory(vlc_opts_array[i], buffer + wrote_size, true);
+          Emscripten.writeAsciiToMemory(vlc_opts_array[i], buffer + wrote_size, true);
           wrote_size += vlc_opts_array[i].length + 1;
         }
 
-        var vlc_argv = Module._malloc(vlc_opts_array.length * 4 + 4);
+        var vlc_argv = Emscripten._malloc(vlc_opts_array.length * 4 + 4);
         var view_vlc_argv = new Uint32Array(
-          Module.wasmMemory.buffer,
+          Emscripten.wasmMemory.buffer,
           vlc_argv,
           vlc_opts_array.length
         );
@@ -282,13 +275,13 @@
           wrote_size += vlc_opts_array[i].length + 1;
         }
 
-        Module._wasm_libvlc_init(vlc_opts_array.length, vlc_argv);
-        Module._free(vlc_argv);
-        Module._free(buffer);
-        media_player = new MediaPlayer(Module, "emjsfile://1");
+        Emscripten._wasm_libvlc_init(vlc_opts_array.length, vlc_argv);
+        Emscripten._free(vlc_argv);
+        Emscripten._free(buffer);
+        media_player = new MediaPlayer(Emscripten, "emjsfile://1");
         media_player.set_volume(80);
 
-        Module._set_global_media_player(media_player.media_player_ptr);
+        Emscripten._set_global_media_player(media_player.media_player_ptr);
         window.media_player = media_player;
         window.on_overlay_click = on_overlay_click;
         window.update_overlay = update_overlay;