2025-10-16 11:16:02 +02:00
var Godot = ( ( ) => {
var _scriptName = typeof document != 'undefined' ? document . currentScript ? . src : undefined ;
return (
async function ( moduleArg = { } ) {
var moduleRtn ;
2025-10-12 16:57:41 +02:00
2026-01-26 16:49:45 +01:00
var Module = moduleArg ; var ENVIRONMENT _IS _WEB = typeof window == "object" ; var ENVIRONMENT _IS _WORKER = typeof WorkerGlobalScope != "undefined" ; var ENVIRONMENT _IS _NODE = typeof process == "object" && process . versions ? . node && process . type != "renderer" ; var ENVIRONMENT _IS _SHELL = ! ENVIRONMENT _IS _WEB && ! ENVIRONMENT _IS _NODE && ! ENVIRONMENT _IS _WORKER ; var arguments _ = [ ] ; var thisProgram = "./this.program" ; var quit _ = ( status , toThrow ) => { throw toThrow } ; if ( ENVIRONMENT _IS _WORKER ) { _scriptName = self . location . href } var scriptDirectory = "" ; function locateFile ( path ) { if ( Module [ "locateFile" ] ) { return Module [ "locateFile" ] ( path , scriptDirectory ) } return scriptDirectory + path } var readAsync , readBinary ; if ( ENVIRONMENT _IS _SHELL ) { const isNode = typeof process == "object" && process . versions ? . node && process . type != "renderer" ; if ( isNode || typeof window == "object" || typeof WorkerGlobalScope != "undefined" ) throw new Error ( "not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)" ) } else if ( ENVIRONMENT _IS _WEB || ENVIRONMENT _IS _WORKER ) { try { scriptDirectory = new URL ( "." , _scriptName ) . href } catch { } if ( ! ( typeof window == "object" || typeof WorkerGlobalScope != "undefined" ) ) throw new Error ( "not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)" ) ; { if ( ENVIRONMENT _IS _WORKER ) { readBinary = url => { var xhr = new XMLHttpRequest ; xhr . open ( "GET" , url , false ) ; xhr . responseType = "arraybuffer" ; xhr . send ( null ) ; return new Uint8Array ( xhr . response ) } } readAsync = async url => { assert ( ! isFileURI ( url ) , "readAsync does not work with file:// URLs" ) ; var response = await fetch ( url , { credentials : "same-origin" } ) ; if ( response . ok ) { return response . arrayBuffer ( ) } throw new Error ( response . status + " : " + response . url ) } } } else { throw new Error ( "environment detection error" ) } var out = console . log . bind ( console ) ; var err = console . error . bind ( console ) ; var IDBFS = "IDBFS is no longer included by default; build with -lidbfs.js" ; assert ( ! ENVIRONMENT _IS _NODE , "node environment detected but not enabled at build time. Add `node` to `-sENVIRONMENT` to enable." ) ; assert ( ! ENVIRONMENT _IS _SHELL , "shell environment detected but not enabled at build time. Add `shell` to `-sENVIRONMENT` to enable." ) ; var wasmBinary ; if ( typeof WebAssembly != "object" ) { err ( "no native wasm support detected" ) } var ABORT = false ; var EXITSTATUS ; function assert ( condition , text ) { if ( ! condition ) { abort ( "Assertion failed" + ( text ? ": " + text : "" ) ) } } var isFileURI = filename => filename . startsWith ( "file://" ) ; function writeStackCookie ( ) { var max = _emscripten _stack _get _end ( ) ; assert ( ( max & 3 ) == 0 ) ; if ( max == 0 ) { max += 4 } HEAPU32 [ max >> 2 ] = 34821223 ; HEAPU32 [ max + 4 >> 2 ] = 2310721022 ; HEAPU32 [ 0 >> 2 ] = 1668509029 } function checkStackCookie ( ) { if ( ABORT ) return ; var max = _emscripten _stack _get _end ( ) ; if ( max == 0 ) { max += 4 } var cookie1 = HEAPU32 [ max >> 2 ] ; var cookie2 = HEAPU32 [ max + 4 >> 2 ] ; if ( cookie1 != 34821223 || cookie2 != 2310721022 ) { abort ( ` Stack overflow! Stack cookie has been overwritten at ${ ptrToString ( max ) } , expected hex dwords 0x89BACDFE and 0x2135467, but received ${ ptrToString ( cookie2 ) } ${ ptrToString ( cookie1 ) } ` ) } if ( HEAPU32 [ 0 >> 2 ] != 1668509029 ) { abort ( "Runtime error: The application has corrupted its heap memory area (address zero)!" ) } } var runtimeDebug = true ; ( ( ) => { var h16 = new Int16Array ( 1 ) ; var h8 = new Int8Array ( h16 . buffer ) ; h16 [ 0 ] = 25459 ; if ( h8 [ 0 ] !== 115 || h8 [ 1 ] !== 99 ) throw "Runtime error: expected the system to be little-endian! (Run with -sSUPPORT_BIG_ENDIAN to bypass)" } ) ( ) ; function consumedModuleProp ( prop ) { if ( ! Object . getOwnPropertyDescriptor ( Module , prop ) ) { Object . defineProperty ( Module , prop , { configurable : true , set ( ) { abort ( ` Attempt to set \` Module. ${ prop } \` after it has already been processed. This can happen, for example, when code is injected via '--post-js' rather than '--pre-js' ` ) } } ) } } function makeInvalidEarlyAccess ( name ) { return ( ) => assert ( false , ` call to ' ${ name } ' via reference taken before Wasm module initialization ` ) } function ignoredModuleProp ( prop ) { if ( Object . getOwnPropertyDescriptor ( Module , prop ) ) { abort ( ` \` Module. ${ prop } \` was supplied but \` ${ prop } \` not
2025-10-12 16:57:41 +02:00
2025-10-16 11:16:02 +02:00
return moduleRtn ;
2025-10-12 16:57:41 +02:00
}
) ;
} ) ( ) ;
2025-10-16 11:16:02 +02:00
if ( typeof exports === 'object' && typeof module === 'object' ) {
2025-10-12 16:57:41 +02:00
module . exports = Godot ;
2025-10-16 11:16:02 +02:00
// This default export looks redundant, but it allows TS to import this
// commonjs style module.
module . exports . default = Godot ;
} else if ( typeof define === 'function' && define [ 'amd' ] )
define ( [ ] , ( ) => Godot ) ;
2025-10-12 16:57:41 +02:00
2025-10-16 11:16:02 +02:00
const Features = {
2025-10-12 16:57:41 +02:00
/ * *
* Check whether WebGL is available . Optionally , specify a particular version of WebGL to check for .
*
* @ param { number = } [ majorVersion = 1 ] The major WebGL version to check for .
* @ returns { boolean } If the given major version of WebGL is available .
* @ function Engine . isWebGLAvailable
* /
isWebGLAvailable : function ( majorVersion = 1 ) {
try {
return ! ! document . createElement ( 'canvas' ) . getContext ( [ 'webgl' , 'webgl2' ] [ majorVersion - 1 ] ) ;
} catch ( e ) { /* Not available */ }
return false ;
} ,
/ * *
* Check whether the Fetch API available and supports streaming responses .
*
* @ returns { boolean } If the Fetch API is available and supports streaming responses .
* @ function Engine . isFetchAvailable
* /
isFetchAvailable : function ( ) {
return 'fetch' in window && 'Response' in window && 'body' in window . Response . prototype ;
} ,
/ * *
* Check whether the engine is running in a Secure Context .
*
* @ returns { boolean } If the engine is running in a Secure Context .
* @ function Engine . isSecureContext
* /
isSecureContext : function ( ) {
return window [ 'isSecureContext' ] === true ;
} ,
/ * *
* Check whether the engine is cross origin isolated .
* This value is dependent on Cross - Origin - Opener - Policy and Cross - Origin - Embedder - Policy headers sent by the server .
*
* @ returns { boolean } If the engine is running in a Secure Context .
* @ function Engine . isSecureContext
* /
isCrossOriginIsolated : function ( ) {
return window [ 'crossOriginIsolated' ] === true ;
} ,
/ * *
* Check whether SharedBufferArray is available .
*
* Most browsers require the page to be running in a secure context , and the
* the server to provide specific CORS headers for SharedArrayBuffer to be available .
*
* @ returns { boolean } If SharedArrayBuffer is available .
* @ function Engine . isSharedArrayBufferAvailable
* /
isSharedArrayBufferAvailable : function ( ) {
return 'SharedArrayBuffer' in window ;
} ,
/ * *
* Check whether the AudioContext supports AudioWorkletNodes .
*
* @ returns { boolean } If AudioWorkletNode is available .
* @ function Engine . isAudioWorkletAvailable
* /
isAudioWorkletAvailable : function ( ) {
return 'AudioContext' in window && 'audioWorklet' in AudioContext . prototype ;
} ,
/ * *
* Return an array of missing required features ( as string ) .
*
* @ returns { Array < string > } A list of human - readable missing features .
* @ function Engine . getMissingFeatures
2025-10-16 11:16:02 +02:00
* @ param { { threads : ( boolean | undefined ) } } supportedFeatures
2025-10-12 16:57:41 +02:00
* /
2025-10-16 11:16:02 +02:00
getMissingFeatures : function ( supportedFeatures = { } ) {
const {
// Quotes are needed for the Closure compiler.
'threads' : supportsThreads = true ,
} = supportedFeatures ;
2025-10-12 16:57:41 +02:00
const missing = [ ] ;
if ( ! Features . isWebGLAvailable ( 2 ) ) {
missing . push ( 'WebGL2 - Check web browser configuration and hardware support' ) ;
}
if ( ! Features . isFetchAvailable ( ) ) {
missing . push ( 'Fetch - Check web browser version' ) ;
}
if ( ! Features . isSecureContext ( ) ) {
missing . push ( 'Secure Context - Check web server configuration (use HTTPS)' ) ;
}
2025-10-16 11:16:02 +02:00
if ( supportsThreads ) {
if ( ! Features . isCrossOriginIsolated ( ) ) {
missing . push ( 'Cross-Origin Isolation - Check that the web server configuration sends the correct headers.' ) ;
}
if ( ! Features . isSharedArrayBufferAvailable ( ) ) {
missing . push ( 'SharedArrayBuffer - Check that the web server configuration sends the correct headers.' ) ;
}
2025-10-12 16:57:41 +02:00
}
2025-10-16 11:16:02 +02:00
2025-10-12 16:57:41 +02:00
// Audio is normally optional since we have a dummy fallback.
return missing ;
} ,
} ;
const Preloader = /** @constructor */ function ( ) { // eslint-disable-line no-unused-vars
function getTrackedResponse ( response , load _status ) {
function onloadprogress ( reader , controller ) {
return reader . read ( ) . then ( function ( result ) {
if ( load _status . done ) {
return Promise . resolve ( ) ;
}
if ( result . value ) {
controller . enqueue ( result . value ) ;
load _status . loaded += result . value . length ;
}
if ( ! result . done ) {
return onloadprogress ( reader , controller ) ;
}
load _status . done = true ;
return Promise . resolve ( ) ;
} ) ;
}
const reader = response . body . getReader ( ) ;
return new Response ( new ReadableStream ( {
start : function ( controller ) {
onloadprogress ( reader , controller ) . then ( function ( ) {
controller . close ( ) ;
} ) ;
} ,
} ) , { headers : response . headers } ) ;
}
function loadFetch ( file , tracker , fileSize , raw ) {
tracker [ file ] = {
total : fileSize || 0 ,
loaded : 0 ,
done : false ,
} ;
return fetch ( file ) . then ( function ( response ) {
if ( ! response . ok ) {
return Promise . reject ( new Error ( ` Failed loading file ' ${ file } ' ` ) ) ;
}
const tr = getTrackedResponse ( response , tracker [ file ] ) ;
if ( raw ) {
return Promise . resolve ( tr ) ;
}
return tr . arrayBuffer ( ) ;
} ) ;
}
function retry ( func , attempts = 1 ) {
function onerror ( err ) {
if ( attempts <= 1 ) {
return Promise . reject ( err ) ;
}
return new Promise ( function ( resolve , reject ) {
setTimeout ( function ( ) {
retry ( func , attempts - 1 ) . then ( resolve ) . catch ( reject ) ;
} , 1000 ) ;
} ) ;
}
return func ( ) . catch ( onerror ) ;
}
const DOWNLOAD _ATTEMPTS _MAX = 4 ;
const loadingFiles = { } ;
const lastProgress = { loaded : 0 , total : 0 } ;
let progressFunc = null ;
const animateProgress = function ( ) {
let loaded = 0 ;
let total = 0 ;
let totalIsValid = true ;
let progressIsFinal = true ;
Object . keys ( loadingFiles ) . forEach ( function ( file ) {
const stat = loadingFiles [ file ] ;
if ( ! stat . done ) {
progressIsFinal = false ;
}
if ( ! totalIsValid || stat . total === 0 ) {
totalIsValid = false ;
total = 0 ;
} else {
total += stat . total ;
}
loaded += stat . loaded ;
} ) ;
if ( loaded !== lastProgress . loaded || total !== lastProgress . total ) {
lastProgress . loaded = loaded ;
lastProgress . total = total ;
if ( typeof progressFunc === 'function' ) {
progressFunc ( loaded , total ) ;
}
}
if ( ! progressIsFinal ) {
requestAnimationFrame ( animateProgress ) ;
}
} ;
this . animateProgress = animateProgress ;
this . setProgressFunc = function ( callback ) {
progressFunc = callback ;
} ;
this . loadPromise = function ( file , fileSize , raw = false ) {
return retry ( loadFetch . bind ( null , file , loadingFiles , fileSize , raw ) , DOWNLOAD _ATTEMPTS _MAX ) ;
} ;
this . preloadedFiles = [ ] ;
this . preload = function ( pathOrBuffer , destPath , fileSize ) {
let buffer = null ;
if ( typeof pathOrBuffer === 'string' ) {
const me = this ;
return this . loadPromise ( pathOrBuffer , fileSize ) . then ( function ( buf ) {
me . preloadedFiles . push ( {
path : destPath || pathOrBuffer ,
buffer : buf ,
} ) ;
return Promise . resolve ( ) ;
} ) ;
} else if ( pathOrBuffer instanceof ArrayBuffer ) {
buffer = new Uint8Array ( pathOrBuffer ) ;
} else if ( ArrayBuffer . isView ( pathOrBuffer ) ) {
buffer = new Uint8Array ( pathOrBuffer . buffer ) ;
}
if ( buffer ) {
this . preloadedFiles . push ( {
path : destPath ,
buffer : pathOrBuffer ,
} ) ;
return Promise . resolve ( ) ;
}
return Promise . reject ( new Error ( 'Invalid object for preloading' ) ) ;
} ;
} ;
/ * *
* An object used to configure the Engine instance based on godot export options , and to override those in custom HTML
* templates if needed .
*
* @ header Engine configuration
* @ summary The Engine configuration object . This is just a typedef , create it like a regular object , e . g . :
*
* ` ` const MyConfig = { executable : 'godot' , unloadAfterInit : false } ` `
*
* @ typedef { Object } EngineConfig
* /
const EngineConfig = { } ; // eslint-disable-line no-unused-vars
/ * *
* @ struct
* @ constructor
* @ ignore
* /
const InternalConfig = function ( initConfig ) { // eslint-disable-line no-unused-vars
const cfg = /** @lends {InternalConfig.prototype} */ {
/ * *
2025-10-16 11:16:02 +02:00
* Whether to unload the engine automatically after the instance is initialized .
2025-10-12 16:57:41 +02:00
*
* @ memberof EngineConfig
* @ default
* @ type { boolean }
* /
unloadAfterInit : true ,
/ * *
* The HTML DOM Canvas object to use .
*
* By default , the first canvas element in the document will be used is none is specified .
*
* @ memberof EngineConfig
* @ default
* @ type { ? HTMLCanvasElement }
* /
canvas : null ,
/ * *
* The name of the WASM file without the extension . ( Set by Godot Editor export process ) .
*
* @ memberof EngineConfig
* @ default
* @ type { string }
* /
executable : '' ,
/ * *
* An alternative name for the game pck to load . The executable name is used otherwise .
*
* @ memberof EngineConfig
* @ default
* @ type { ? string }
* /
mainPack : null ,
/ * *
* Specify a language code to select the proper localization for the game .
*
* The browser locale will be used if none is specified . See complete list of
* : ref : ` supported locales <doc_locales> ` .
*
* @ memberof EngineConfig
* @ type { ? string }
* @ default
* /
locale : null ,
/ * *
* The canvas resize policy determines how the canvas should be resized by Godot .
*
* ` ` 0 ` ` means Godot won ' t do any resizing . This is useful if you want to control the canvas size from
* javascript code in your template .
*
* ` ` 1 ` ` means Godot will resize the canvas on start , and when changing window size via engine functions .
*
* ` ` 2 ` ` means Godot will adapt the canvas size to match the whole browser window .
*
* @ memberof EngineConfig
* @ type { number }
* @ default
* /
canvasResizePolicy : 2 ,
/ * *
* The arguments to be passed as command line arguments on startup .
*
* See : ref : ` command line tutorial <doc_command_line_tutorial> ` .
*
* * * Note * * : : js : meth : ` startGame <Engine.prototype.startGame> ` will always add the ` ` -- main - pack ` ` argument .
*
* @ memberof EngineConfig
* @ type { Array < string > }
* @ default
* /
args : [ ] ,
/ * *
* When enabled , the game canvas will automatically grab the focus when the engine starts .
*
* @ memberof EngineConfig
* @ type { boolean }
* @ default
* /
focusCanvas : true ,
/ * *
* When enabled , this will turn on experimental virtual keyboard support on mobile .
*
* @ memberof EngineConfig
* @ type { boolean }
* @ default
* /
experimentalVK : false ,
/ * *
* The progressive web app service worker to install .
* @ memberof EngineConfig
* @ default
* @ type { string }
* /
serviceWorker : '' ,
/ * *
* @ ignore
* @ type { Array . < string > }
* /
persistentPaths : [ '/userfs' ] ,
/ * *
* @ ignore
* @ type { boolean }
* /
persistentDrops : false ,
/ * *
* @ ignore
* @ type { Array . < string > }
* /
gdextensionLibs : [ ] ,
/ * *
* @ ignore
* @ type { Array . < string > }
* /
fileSizes : [ ] ,
2025-10-16 11:16:02 +02:00
/ * *
* @ ignore
* @ type { number }
* /
emscriptenPoolSize : 8 ,
/ * *
* @ ignore
* @ type { number }
* /
godotPoolSize : 4 ,
2025-10-12 16:57:41 +02:00
/ * *
* A callback function for handling Godot ' s ` ` OS . execute ` ` calls .
*
* This is for example used in the Web Editor template to switch between project manager and editor , and for running the game .
*
* @ callback EngineConfig . onExecute
* @ param { string } path The path that Godot ' s wants executed .
* @ param { Array . < string > } args The arguments of the "command" to execute .
* /
/ * *
* @ ignore
* @ type { ? function ( string , Array . < string > ) }
* /
onExecute : null ,
/ * *
* A callback function for being notified when the Godot instance quits .
*
* * * Note * * : This function will not be called if the engine crashes or become unresponsive .
*
* @ callback EngineConfig . onExit
* @ param { number } status _code The status code returned by Godot on exit .
* /
/ * *
* @ ignore
* @ type { ? function ( number ) }
* /
onExit : null ,
/ * *
* A callback function for displaying download progress .
*
* The function is called once per frame while downloading files , so the usage of ` ` requestAnimationFrame ( ) ` `
* is not necessary .
*
* If the callback function receives a total amount of bytes as 0 , this means that it is impossible to calculate .
* Possible reasons include :
*
* - Files are delivered with server - side chunked compression
* - Files are delivered with server - side compression on Chromium
* - Not all file downloads have started yet ( usually on servers without multi - threading )
*
* @ callback EngineConfig . onProgress
* @ param { number } current The current amount of downloaded bytes so far .
* @ param { number } total The total amount of bytes to be downloaded .
* /
/ * *
* @ ignore
* @ type { ? function ( number , number ) }
* /
onProgress : null ,
/ * *
* A callback function for handling the standard output stream . This method should usually only be used in debug pages .
*
* By default , ` ` console . log ( ) ` ` is used .
*
* @ callback EngineConfig . onPrint
* @ param { ... * } [ var _args ] A variadic number of arguments to be printed .
* /
/ * *
* @ ignore
* @ type { ? function ( ... * ) }
* /
onPrint : function ( ) {
console . log . apply ( console , Array . from ( arguments ) ) ; // eslint-disable-line no-console
} ,
/ * *
* A callback function for handling the standard error stream . This method should usually only be used in debug pages .
*
* By default , ` ` console . error ( ) ` ` is used .
*
* @ callback EngineConfig . onPrintError
* @ param { ... * } [ var _args ] A variadic number of arguments to be printed as errors .
* /
/ * *
* @ ignore
* @ type { ? function ( ... * ) }
* /
onPrintError : function ( var _args ) {
console . error . apply ( console , Array . from ( arguments ) ) ; // eslint-disable-line no-console
} ,
} ;
/ * *
* @ ignore
* @ struct
* @ constructor
* @ param { EngineConfig } opts
* /
function Config ( opts ) {
this . update ( opts ) ;
}
Config . prototype = cfg ;
/ * *
* @ ignore
* @ param { EngineConfig } opts
* /
Config . prototype . update = function ( opts ) {
const config = opts || { } ;
// NOTE: We must explicitly pass the default, accessing it via
// the key will fail due to closure compiler renames.
function parse ( key , def ) {
if ( typeof ( config [ key ] ) === 'undefined' ) {
return def ;
}
return config [ key ] ;
}
// Module config
this . unloadAfterInit = parse ( 'unloadAfterInit' , this . unloadAfterInit ) ;
this . onPrintError = parse ( 'onPrintError' , this . onPrintError ) ;
this . onPrint = parse ( 'onPrint' , this . onPrint ) ;
this . onProgress = parse ( 'onProgress' , this . onProgress ) ;
// Godot config
this . canvas = parse ( 'canvas' , this . canvas ) ;
this . executable = parse ( 'executable' , this . executable ) ;
this . mainPack = parse ( 'mainPack' , this . mainPack ) ;
this . locale = parse ( 'locale' , this . locale ) ;
this . canvasResizePolicy = parse ( 'canvasResizePolicy' , this . canvasResizePolicy ) ;
this . persistentPaths = parse ( 'persistentPaths' , this . persistentPaths ) ;
this . persistentDrops = parse ( 'persistentDrops' , this . persistentDrops ) ;
this . experimentalVK = parse ( 'experimentalVK' , this . experimentalVK ) ;
this . focusCanvas = parse ( 'focusCanvas' , this . focusCanvas ) ;
this . serviceWorker = parse ( 'serviceWorker' , this . serviceWorker ) ;
this . gdextensionLibs = parse ( 'gdextensionLibs' , this . gdextensionLibs ) ;
this . fileSizes = parse ( 'fileSizes' , this . fileSizes ) ;
2025-10-16 11:16:02 +02:00
this . emscriptenPoolSize = parse ( 'emscriptenPoolSize' , this . emscriptenPoolSize ) ;
this . godotPoolSize = parse ( 'godotPoolSize' , this . godotPoolSize ) ;
2025-10-12 16:57:41 +02:00
this . args = parse ( 'args' , this . args ) ;
this . onExecute = parse ( 'onExecute' , this . onExecute ) ;
this . onExit = parse ( 'onExit' , this . onExit ) ;
} ;
/ * *
* @ ignore
* @ param { string } loadPath
* @ param { Response } response
* /
Config . prototype . getModuleConfig = function ( loadPath , response ) {
let r = response ;
2025-10-16 11:16:02 +02:00
const gdext = this . gdextensionLibs ;
2025-10-12 16:57:41 +02:00
return {
'print' : this . onPrint ,
'printErr' : this . onPrintError ,
'thisProgram' : this . executable ,
'noExitRuntime' : false ,
2025-10-16 11:16:02 +02:00
'dynamicLibraries' : [ ` ${ loadPath } .side.wasm ` ] . concat ( this . gdextensionLibs ) ,
'emscriptenPoolSize' : this . emscriptenPoolSize ,
2025-10-12 16:57:41 +02:00
'instantiateWasm' : function ( imports , onSuccess ) {
function done ( result ) {
onSuccess ( result [ 'instance' ] , result [ 'module' ] ) ;
}
if ( typeof ( WebAssembly . instantiateStreaming ) !== 'undefined' ) {
WebAssembly . instantiateStreaming ( Promise . resolve ( r ) , imports ) . then ( done ) ;
} else {
r . arrayBuffer ( ) . then ( function ( buffer ) {
WebAssembly . instantiate ( buffer , imports ) . then ( done ) ;
} ) ;
}
r = null ;
return { } ;
} ,
'locateFile' : function ( path ) {
if ( ! path . startsWith ( 'godot.' ) ) {
return path ;
} else if ( path . endsWith ( '.audio.worklet.js' ) ) {
return ` ${ loadPath } .audio.worklet.js ` ;
2025-10-16 11:16:02 +02:00
} else if ( path . endsWith ( '.audio.position.worklet.js' ) ) {
return ` ${ loadPath } .audio.position.worklet.js ` ;
2025-10-12 16:57:41 +02:00
} else if ( path . endsWith ( '.js' ) ) {
return ` ${ loadPath } .js ` ;
2025-10-16 11:16:02 +02:00
} else if ( path in gdext ) {
return path ;
2025-10-12 16:57:41 +02:00
} else if ( path . endsWith ( '.side.wasm' ) ) {
return ` ${ loadPath } .side.wasm ` ;
} else if ( path . endsWith ( '.wasm' ) ) {
return ` ${ loadPath } .wasm ` ;
}
return path ;
} ,
} ;
} ;
/ * *
* @ ignore
* @ param { function ( ) } cleanup
* /
Config . prototype . getGodotConfig = function ( cleanup ) {
// Try to find a canvas
if ( ! ( this . canvas instanceof HTMLCanvasElement ) ) {
const nodes = document . getElementsByTagName ( 'canvas' ) ;
if ( nodes . length && nodes [ 0 ] instanceof HTMLCanvasElement ) {
const first = nodes [ 0 ] ;
this . canvas = /** @type {!HTMLCanvasElement} */ ( first ) ;
}
if ( ! this . canvas ) {
throw new Error ( 'No canvas found in page' ) ;
}
}
// Canvas can grab focus on click, or key events won't work.
if ( this . canvas . tabIndex < 0 ) {
this . canvas . tabIndex = 0 ;
}
// Browser locale, or custom one if defined.
let locale = this . locale ;
if ( ! locale ) {
locale = navigator . languages ? navigator . languages [ 0 ] : navigator . language ;
locale = locale . split ( '.' ) [ 0 ] ;
}
locale = locale . replace ( '-' , '_' ) ;
const onExit = this . onExit ;
// Godot configuration.
return {
'canvas' : this . canvas ,
'canvasResizePolicy' : this . canvasResizePolicy ,
'locale' : locale ,
'persistentDrops' : this . persistentDrops ,
'virtualKeyboard' : this . experimentalVK ,
2025-10-16 11:16:02 +02:00
'godotPoolSize' : this . godotPoolSize ,
2025-10-12 16:57:41 +02:00
'focusCanvas' : this . focusCanvas ,
'onExecute' : this . onExecute ,
'onExit' : function ( p _code ) {
cleanup ( ) ; // We always need to call the cleanup callback to free memory.
if ( typeof ( onExit ) === 'function' ) {
onExit ( p _code ) ;
}
} ,
} ;
} ;
return new Config ( initConfig ) ;
} ;
/ * *
* Projects exported for the Web expose the : js : class : ` Engine ` class to the JavaScript environment , that allows
* fine control over the engine ' s start - up process .
*
* This API is built in an asynchronous manner and requires basic understanding
* of ` Promises <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises> ` _ _ .
*
* @ module Engine
* @ header Web export JavaScript reference
* /
const Engine = ( function ( ) {
const preloader = new Preloader ( ) ;
let loadPromise = null ;
let loadPath = '' ;
let initPromise = null ;
/ * *
* @ classdesc The ` ` Engine ` ` class provides methods for loading and starting exported projects on the Web . For default export
* settings , this is already part of the exported HTML page . To understand practical use of the ` ` Engine ` ` class ,
* see : ref : ` Custom HTML page for Web export <doc_customizing_html5_shell> ` .
*
* @ description Create a new Engine instance with the given configuration .
*
* @ global
* @ constructor
* @ param { EngineConfig } initConfig The initial config for this instance .
* /
function Engine ( initConfig ) { // eslint-disable-line no-shadow
this . config = new InternalConfig ( initConfig ) ;
this . rtenv = null ;
}
/ * *
* Load the engine from the specified base path .
*
* @ param { string } basePath Base path of the engine to load .
* @ param { number = } [ size = 0 ] The file size if known .
* @ returns { Promise } A Promise that resolves once the engine is loaded .
*
* @ function Engine . load
* /
Engine . load = function ( basePath , size ) {
if ( loadPromise == null ) {
loadPath = basePath ;
loadPromise = preloader . loadPromise ( ` ${ loadPath } .wasm ` , size , true ) ;
requestAnimationFrame ( preloader . animateProgress ) ;
}
return loadPromise ;
} ;
/ * *
* Unload the engine to free memory .
*
* This method will be called automatically depending on the configuration . See : js : attr : ` unloadAfterInit ` .
*
* @ function Engine . unload
* /
Engine . unload = function ( ) {
loadPromise = null ;
} ;
/ * *
* Safe Engine constructor , creates a new prototype for every new instance to avoid prototype pollution .
* @ ignore
* @ constructor
* /
function SafeEngine ( initConfig ) {
const proto = /** @lends Engine.prototype */ {
/ * *
* Initialize the engine instance . Optionally , pass the base path to the engine to load it ,
* if it hasn ' t been loaded yet . See : js : meth : ` Engine.load ` .
*
* @ param { string = } basePath Base path of the engine to load .
* @ return { Promise } A ` ` Promise ` ` that resolves once the engine is loaded and initialized .
* /
init : function ( basePath ) {
if ( initPromise ) {
return initPromise ;
}
if ( loadPromise == null ) {
if ( ! basePath ) {
initPromise = Promise . reject ( new Error ( 'A base path must be provided when calling `init` and the engine is not loaded.' ) ) ;
return initPromise ;
}
Engine . load ( basePath , this . config . fileSizes [ ` ${ basePath } .wasm ` ] ) ;
}
const me = this ;
function doInit ( promise ) {
// Care! Promise chaining is bogus with old emscripten versions.
// This caused a regression with the Mono build (which uses an older emscripten version).
// Make sure to test that when refactoring.
return new Promise ( function ( resolve , reject ) {
promise . then ( function ( response ) {
const cloned = new Response ( response . clone ( ) . body , { 'headers' : [ [ 'content-type' , 'application/wasm' ] ] } ) ;
Godot ( me . config . getModuleConfig ( loadPath , cloned ) ) . then ( function ( module ) {
const paths = me . config . persistentPaths ;
module [ 'initFS' ] ( paths ) . then ( function ( err ) {
me . rtenv = module ;
if ( me . config . unloadAfterInit ) {
Engine . unload ( ) ;
}
resolve ( ) ;
} ) ;
} ) ;
} ) ;
} ) ;
}
preloader . setProgressFunc ( this . config . onProgress ) ;
initPromise = doInit ( loadPromise ) ;
return initPromise ;
} ,
/ * *
* Load a file so it is available in the instance ' s file system once it runs . Must be called * * before * * starting the
* instance .
*
* If not provided , the ` ` path ` ` is derived from the URL of the loaded file .
*
* @ param { string | ArrayBuffer } file The file to preload .
*
* If a ` ` string ` ` the file will be loaded from that path .
*
* If an ` ` ArrayBuffer ` ` or a view on one , the buffer will used as the content of the file .
*
* @ param { string = } path Path by which the file will be accessible . Required , if ` ` file ` ` is not a string .
*
* @ returns { Promise } A Promise that resolves once the file is loaded .
* /
preloadFile : function ( file , path ) {
return preloader . preload ( file , path , this . config . fileSizes [ file ] ) ;
} ,
/ * *
* Start the engine instance using the given override configuration ( if any ) .
* : js : meth : ` startGame <Engine.prototype.startGame> ` can be used in typical cases instead .
*
* This will initialize the instance if it is not initialized . For manual initialization , see : js : meth : ` init <Engine.prototype.init> ` .
* The engine must be loaded beforehand .
*
* Fails if a canvas cannot be found on the page , or not specified in the configuration .
*
* @ param { EngineConfig } override An optional configuration override .
* @ return { Promise } Promise that resolves once the engine started .
* /
start : function ( override ) {
this . config . update ( override ) ;
const me = this ;
return me . init ( ) . then ( function ( ) {
if ( ! me . rtenv ) {
return Promise . reject ( new Error ( 'The engine must be initialized before it can be started' ) ) ;
}
let config = { } ;
try {
config = me . config . getGodotConfig ( function ( ) {
me . rtenv = null ;
} ) ;
} catch ( e ) {
return Promise . reject ( e ) ;
}
// Godot configuration.
me . rtenv [ 'initConfig' ] ( config ) ;
// Preload GDExtension libraries.
if ( me . config . gdextensionLibs . length > 0 && ! me . rtenv [ 'loadDynamicLibrary' ] ) {
return Promise . reject ( new Error ( 'GDExtension libraries are not supported by this engine version. '
+ 'Enable "Extensions Support" for your export preset and/or build your custom template with "dlink_enabled=yes".' ) ) ;
}
2025-10-16 11:16:02 +02:00
return new Promise ( function ( resolve , reject ) {
for ( const file of preloader . preloadedFiles ) {
me . rtenv [ 'copyToFS' ] ( file . path , file . buffer ) ;
}
preloader . preloadedFiles . length = 0 ; // Clear memory
me . rtenv [ 'callMain' ] ( me . config . args ) ;
initPromise = null ;
me . installServiceWorker ( ) ;
resolve ( ) ;
2025-10-12 16:57:41 +02:00
} ) ;
} ) ;
} ,
/ * *
* Start the game instance using the given configuration override ( if any ) .
*
* This will initialize the instance if it is not initialized . For manual initialization , see : js : meth : ` init <Engine.prototype.init> ` .
*
* This will load the engine if it is not loaded , and preload the main pck .
*
* This method expects the initial config ( or the override ) to have both the : js : attr : ` executable ` and : js : attr : ` mainPack `
* properties set ( normally done by the editor during export ) .
*
* @ param { EngineConfig } override An optional configuration override .
* @ return { Promise } Promise that resolves once the game started .
* /
startGame : function ( override ) {
this . config . update ( override ) ;
// Add main-pack argument.
const exe = this . config . executable ;
const pack = this . config . mainPack || ` ${ exe } .pck ` ;
this . config . args = [ '--main-pack' , pack ] . concat ( this . config . args ) ;
// Start and init with execName as loadPath if not inited.
const me = this ;
return Promise . all ( [
this . init ( exe ) ,
this . preloadFile ( pack , pack ) ,
] ) . then ( function ( ) {
return me . start . apply ( me ) ;
} ) ;
} ,
/ * *
* Create a file at the specified ` ` path ` ` with the passed as ` ` buffer ` ` in the instance ' s file system .
*
* @ param { string } path The location where the file will be created .
* @ param { ArrayBuffer } buffer The content of the file .
* /
copyToFS : function ( path , buffer ) {
if ( this . rtenv == null ) {
throw new Error ( 'Engine must be inited before copying files' ) ;
}
this . rtenv [ 'copyToFS' ] ( path , buffer ) ;
} ,
/ * *
* Request that the current instance quit .
*
* This is akin the user pressing the close button in the window manager , and will
* have no effect if the engine has crashed , or is stuck in a loop .
*
* /
requestQuit : function ( ) {
if ( this . rtenv ) {
this . rtenv [ 'request_quit' ] ( ) ;
}
} ,
2025-10-16 11:16:02 +02:00
/ * *
* Install the progressive - web app service worker .
* @ returns { Promise } The service worker registration promise .
* /
installServiceWorker : function ( ) {
if ( this . config . serviceWorker && 'serviceWorker' in navigator ) {
try {
return navigator . serviceWorker . register ( this . config . serviceWorker ) ;
} catch ( e ) {
return Promise . reject ( e ) ;
}
}
return Promise . resolve ( ) ;
} ,
2025-10-12 16:57:41 +02:00
} ;
Engine . prototype = proto ;
// Closure compiler exported instance methods.
Engine . prototype [ 'init' ] = Engine . prototype . init ;
Engine . prototype [ 'preloadFile' ] = Engine . prototype . preloadFile ;
Engine . prototype [ 'start' ] = Engine . prototype . start ;
Engine . prototype [ 'startGame' ] = Engine . prototype . startGame ;
Engine . prototype [ 'copyToFS' ] = Engine . prototype . copyToFS ;
Engine . prototype [ 'requestQuit' ] = Engine . prototype . requestQuit ;
2025-10-16 11:16:02 +02:00
Engine . prototype [ 'installServiceWorker' ] = Engine . prototype . installServiceWorker ;
2025-10-12 16:57:41 +02:00
// Also expose static methods as instance methods
Engine . prototype [ 'load' ] = Engine . load ;
Engine . prototype [ 'unload' ] = Engine . unload ;
return new Engine ( initConfig ) ;
}
// Closure compiler exported static methods.
SafeEngine [ 'load' ] = Engine . load ;
SafeEngine [ 'unload' ] = Engine . unload ;
// Feature-detection utilities.
SafeEngine [ 'isWebGLAvailable' ] = Features . isWebGLAvailable ;
SafeEngine [ 'isFetchAvailable' ] = Features . isFetchAvailable ;
SafeEngine [ 'isSecureContext' ] = Features . isSecureContext ;
SafeEngine [ 'isCrossOriginIsolated' ] = Features . isCrossOriginIsolated ;
SafeEngine [ 'isSharedArrayBufferAvailable' ] = Features . isSharedArrayBufferAvailable ;
SafeEngine [ 'isAudioWorkletAvailable' ] = Features . isAudioWorkletAvailable ;
SafeEngine [ 'getMissingFeatures' ] = Features . getMissingFeatures ;
return SafeEngine ;
} ( ) ) ;
if ( typeof window !== 'undefined' ) {
window [ 'Engine' ] = Engine ;
}