init(width, height, options) {
		this.settings = Object.assign(defaultApplicationSettings, options || {});

		// sanitize potential given parameters
		this.settings.width = width;
		this.settings.height = height;
		this.settings.transparent = !!this.settings.transparent;
		this.settings.antiAlias = !!this.settings.antiAlias;
		this.settings.failIfMajorPerformanceCaveat =
			!!this.settings.failIfMajorPerformanceCaveat;
		this.settings.depthTest =
			this.settings.depthTest === "z-buffer" ? "z-buffer" : "sorting";
		this.settings.subPixel = !!this.settings.subPixel;
		this.settings.verbose = !!this.settings.verbose;
		if (
			this.settings.scaleMethod.search(
				/^(fill-(min|max)|fit|flex(-(width|height))?|stretch)$/,
			) !== -1
		) {
			this.settings.autoScale = this.settings.scale === "auto" || true;
		} else {
			// default scaling method
			this.settings.scaleMethod = ScaleMethods.Fit;
			this.settings.autoScale = this.settings.scale === "auto" || false;
		}

		// override renderer settings if &webgl or &canvas is defined in the URL
		let uriFragment = getUriFragment();
		if (
			uriFragment.webgl === true ||
			uriFragment.webgl1 === true ||
			uriFragment.webgl2 === true
		) {
			this.settings.renderer = WEBGL;
			if (uriFragment.webgl1 === true) {
				this.settings.preferWebGL1 = true;
			}
		} else if (uriFragment.canvas === true) {
			this.settings.renderer = CANVAS;
		}

		// normalize scale
		this.settings.scale = this.settings.autoScale
			? 1.0
			: +this.settings.scale || 1.0;

		// default scaled size value
		this.settings.zoomX = width * this.settings.scale;
		this.settings.zoomY = height * this.settings.scale;

		// identify parent element and/or the html target for resizing
		this.parentElement = device.getElement(this.settings.parent);
		if (typeof this.settings.scaleTarget !== "undefined") {
			this.settings.scaleTarget = device.getElement(this.settings.scaleTarget);
		}

		if (typeof this.settings.renderer === "number") {
			switch (this.settings.renderer) {
				case AUTO:
				case WEBGL:
					this.renderer = autoDetectRenderer(this.settings);
					break;
				default:
					this.renderer = new CanvasRenderer(this.settings);
					break;
			}
		} else {
			let CustomRenderer = this.settings.renderer;
			// a renderer class
			this.renderer = new CustomRenderer(this.settings);
		}

		// register to the channel
		eventEmitter.addListener(WINDOW_ONRESIZE, () => {
			return onresize(this);
		});
		eventEmitter.addListener(WINDOW_ONORIENTATION_CHANGE, () => {
			return onresize(this);
		});

		// add our canvas (default to document.body if settings.parent is undefined)
		this.parentElement.appendChild(this.renderer.getCanvas());

		// Mobile browser hacks
		if (device.platform.isMobile) {
			// Prevent the webview from moving on a swipe
			device.enableSwipe(false);
		}

		// trigger an initial resize();
		onresize(this);

		// add an observer to detect when the dom tree is modified
		if ("MutationObserver" in globalThis) {
			// Create an observer instance linked to the callback function
			let observer = new MutationObserver(() => {
				return onresize(this);
			});

			// Start observing the target node for configured mutations
			observer.observe(this.parentElement, {
				attributes: false,
				childList: true,
				subtree: true,
			});
		}

		if (this.settings.consoleHeader !== false) {
			consoleHeader(this);
		}

		// create a new physic world
		this.world = new World(0, 0, this.settings.width, this.settings.height);

		// set the reference to this application instance
		this.world.app = this;
		// set the reference to this application instance
		this.world.physic = this.settings.physic;

		// app starting time
		this.lastUpdate = globalThis.performance.now();
		// manually sort child if depthTest setting is "sorting"
		this.world.autoSort = !(
			this.renderer.type === "WEBGL" && this.settings.depthTest === "z-buffer"
		);

		this.isInitialized = true;

		eventEmitter.emit(GAME_INIT);
		eventEmitter.addListener(STATE_CHANGE, this.repaint.bind(this));
		eventEmitter.addListener(STATE_RESTART, this.repaint.bind(this));
		eventEmitter.addListener(STATE_RESUME, this.repaint.bind(this));
		eventEmitter.addListener(STAGE_RESET, this.reset.bind(this));
		eventEmitter.addListener(TICK, (time) => {
			this.update(time);
			this.draw();
		});

		eventEmitter.addListener(BLUR, () => {
			if (this.stopOnBlur === true) {
				state.stop(true);
			}
			if (this.pauseOnBlur === true) {
				state.pause(true);
			}
		});

		eventEmitter.addListener(FOCUS, () => {
			if (this.stopOnBlur === true) {
				state.restart(true);
			}
			if (this.resumeOnFocus === true) {
				state.resume(true);
			}
		});
	}
init the game instance (create a physic world, update starting time, etc..)
@param {number} width - The width of the canvas viewport
@param {number} height - The height of the canvas viewport
@param {ApplicationSettings} [options] - The optional parameters for the application and default renderer