diff --git a/Makefile b/Makefile
index 253475c26f91e8c9a28e319169f09cbe740eb3fb..28ec6b6baba5eca0eca67bc5c5a564bb1677dcb4 100644
--- a/Makefile
+++ b/Makefile
@@ -33,12 +33,8 @@ bin/%.html: bin/tmp
 	git checkout -- src/gui/mainMenu/AlphaDisclaimer.tw
 	mv $< $@
 
-bin/fc.js: bin/
-	devTools/concatFiles.sh js/ '*.js' $@
-
-bin/tmp: bin/fc.js src/gui/mainMenu/AlphaDisclaimer.tw
-	$(TWEEGO) --module=bin/fc.js --head devTools/head.html src/ > $@
-	rm -f bin/fc.js
+bin/tmp: bin/ src/gui/mainMenu/AlphaDisclaimer.tw
+	$(TWEEGO) src/ --head devTools/head.html > $@
 
 src/gui/mainMenu/AlphaDisclaimer.tw:
 	sed -Ei "s/build .releaseID/\0 commit $(COMMIT)/" $@
diff --git a/compile.bat b/compile.bat
index 2a06b08270c083d45c949ee958095a248b32b52d..ad3570dfc45fe2b8291d3652546ccab784faa625 100644
--- a/compile.bat
+++ b/compile.bat
@@ -4,14 +4,12 @@
 :: Set working directory
 pushd %~dp0
 if not exist "bin\resources" mkdir bin\resources
-CALL devTools/concatFiles.bat js/ "*.js" bin/fc.js
 :: Run the appropriate compiler for the user's CPU architecture.
 if %PROCESSOR_ARCHITECTURE% == AMD64 (
-	CALL "%~dp0devTools\tweeGo\tweego_win64.exe" -o "%~dp0bin/FC_pregmod.html" --module=bin/fc.js --head devTools/head.html "%~dp0src"
+	CALL "%~dp0devTools\tweeGo\tweego_win64.exe" -o "%~dp0bin/FC_pregmod.html" --head devTools/head.html "%~dp0src"
 ) else (
-	CALL "%~dp0devTools\tweeGo\tweego_win86.exe" -o "%~dp0bin/FC_pregmod.html" --module=bin/fc.js --head devTools/head.html "%~dp0src"
+	CALL "%~dp0devTools\tweeGo\tweego_win86.exe" -o "%~dp0bin/FC_pregmod.html" --head devTools/head.html "%~dp0src"
 )
-DEL bin\fc.js
 
 popd
 ECHO Done
diff --git a/compile.sh b/compile.sh
index 4946c57147f9a1f1c62c56e6a750cb23a282e207..dc9140f965b049e6f595d1f0bca62b50d32f8a47 100755
--- a/compile.sh
+++ b/compile.sh
@@ -76,9 +76,7 @@ function compile {
 		file="bin/FC_pregmod.html"
 	fi
 
-	devTools/concatFiles.sh js/ '*.js' bin/fc.js
-	$TWEEGO_EXE -o $file --module=bin/fc.js --head devTools/head.html src/ || build_failed="true"
-	rm -f bin/fc.js
+	$TWEEGO_EXE -o $file src/ --head devTools/head.html || build_failed="true"
 	if [ "$build_failed" = "true" ]
 	then
 		echoError "Build failed."
diff --git a/devTools/concatFiles.bat b/devTools/concatFiles.bat
deleted file mode 100644
index 2a07ad8205c7be5850aa7f5df1135d95751e4183..0000000000000000000000000000000000000000
--- a/devTools/concatFiles.bat
+++ /dev/null
@@ -1,22 +0,0 @@
-@echo off
-
-:: See if we can find a git installation
-setlocal enabledelayedexpansion
-
-for %%k in (HKCU HKLM) do (
-	for %%w in (\ \Wow6432Node\) do (
-		for /f "skip=2 delims=: tokens=1*" %%a in ('reg query "%%k\SOFTWARE%%wMicrosoft\Windows\CurrentVersion\Uninstall\Git_is1" /v InstallLocation 2^> nul') do (
-			for /f "tokens=3" %%z in ("%%a") do (
-				set GIT=%%z:%%b
-				set GITFOUND=yes
-				goto FOUND
-			)
-		)
-	)
-)
-
-:FOUND
-if %GITFOUND% == yes (
-	set "PATH=%GIT%bin;%PATH%"
-	bash devTools/concatFiles.sh "%1" "%2" "%3"
-)
diff --git a/devTools/concatFiles.sh b/devTools/concatFiles.sh
deleted file mode 100755
index a1a8fd8586627b885320acffe2b3cea3647f5d19..0000000000000000000000000000000000000000
--- a/devTools/concatFiles.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/sh
-
-# $1: source dir
-# $2: name wildcard
-# #3: destination file
-
-rm -f "$3"
-files=$(find "$1" -name "$2" -print)
-files=$(echo "$files" | sort)
-for f in $files; do
-	echo -e "\n/* ${f#$1} */\n" >> "$3"
-	cat "$f" >> "$3"
-done
diff --git a/js/002-config/fc-js-init.js b/src/002-config/fc-js-init.js
similarity index 69%
rename from js/002-config/fc-js-init.js
rename to src/002-config/fc-js-init.js
index f01b00419cd4402a38ff4f7dbbbb7442d47e858e..3c923124687bca8b341a43c66007c69029106124 100644
--- a/js/002-config/fc-js-init.js
+++ b/src/002-config/fc-js-init.js
@@ -1,8 +1,13 @@
 /* eslint-disable no-var */
+/*
+* SugarCube executes scripts via eval() inside a closure. Thus to make App global,
+* we declare it as a property of the window object. I don't know why 'App = {}'
+* does not work.
+*/
 // @ts-ignore
-"use strict";
-
-var App = { };
+window.App = { };
+// the same declaration for code parsers that don't like the line above
+var App = window.App || {}; /* eslint-disable-line no-var*/
 
 App.Art = {};
 App.Data = {};
diff --git a/js/utils.js b/src/js/utils.js
similarity index 88%
rename from js/utils.js
rename to src/js/utils.js
index cf39ddc1d09fa1ff3447db4e4f225850370d1e47..eca740c543cbcfbf5729fa9fbd766671d7152e05 100644
--- a/js/utils.js
+++ b/src/js/utils.js
@@ -1,4 +1,3 @@
-/* eslint-disable no-unused-vars */
 /* This file contains only JS functions without dependencies on FC specific variables/conventions and do not rely on
  * custom functions outside this file
  */
@@ -8,17 +7,17 @@
  * @param {any} x
  * @returns {boolean}
  */
-function jsDef(x) {
+window.jsDef = function(x) {
 	return (typeof x !== "undefined" && x !== null && x !== undefined);
-}
+};
 
 /**
  * @param {number} n
  * @returns {boolean}
  */
-function isFloat(n) {
+window.isFloat = function(n) {
 	return Number.isFinite(n) && Math.floor(n) !== n;
-}
+};
 
 /**
  * Determines if a is between low and high
@@ -27,17 +26,17 @@ function isFloat(n) {
  * @param {number} high
  * @returns {boolean}
  */
-function between(a, low, high) {
+window.between = function(a, low, high) {
 	if (low === null) { low = -Infinity; }
 	if (high === null) { high = Infinity; }
 	return (a > low && a < high);
-}
+};
 
 /**
  * @param {number[]} obj
  * @returns {number}
  */
-function hashChoice(obj) {
+window.hashChoice = function hashChoice(obj) {
 	let randint = Math.floor(Math.random() * hashSum(obj));
 	let ret;
 	Object.keys(obj).some((key) => {
@@ -50,37 +49,37 @@ function hashChoice(obj) {
 		}
 	});
 	return ret;
-}
+};
 
 /**
  * @param {number[]} obj
  * @returns {number}
  */
-function hashSum(obj) {
+window.hashSum = function hashSum(obj) {
 	let sum = 0;
 	Object.keys(obj).forEach((key) => {
 		sum += obj[key];
 	});
 	return sum;
-}
+};
 
 /**
  * @param {Array} arr
  * @returns {Object}
  */
-function arr2obj(arr) {
+window.arr2obj = function arr2obj(arr) {
 	const obj = {};
 	arr.forEach((item) => {
 		obj[item] = 1;
 	});
 	return obj;
-}
+};
 
 /**
  * @param {{}} object
  * @param rest
  */
-function hashPush(object, ...rest) {
+window.hashPush = function hashPush(object, ...rest) {
 	rest.forEach((item) => {
 		if (object[item] === undefined) {
 			object[item] = 1;
@@ -88,13 +87,13 @@ function hashPush(object, ...rest) {
 			object[item] += 1;
 		}
 	});
-}
+};
 
 /**
  * @param {[]} array
  * @return {{}}
  */
-function weightedArray2HashMap(array) {
+window.weightedArray2HashMap = function weightedArray2HashMap(array) {
 	const obj = {};
 	array.forEach((item) => {
 		if (obj[item] === undefined) {
@@ -104,14 +103,14 @@ function weightedArray2HashMap(array) {
 		}
 	});
 	return obj;
-}
+};
 
 /**
  * generate a random, almost unique ID that is compliant (possibly) with RFC 4122
  *
  * @return {string}
  */
-function generateNewID() {
+window.generateNewID = function generateNewID() {
 	let date = Date.now(); // high-precision timer
 	let uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
 		let r = (date + Math.random() * 16) % 16 | 0;
@@ -119,44 +118,44 @@ function generateNewID() {
 		return (c === "x" ? r : (r & 0x3 | 0x8)).toString(16);
 	});
 	return uuid;
-}
+};
 
 /**
  * @param {Array} array
  * @param {number} indexA
  * @param {number} indexB
  */
-function arraySwap(array, indexA, indexB) {
+window.arraySwap = function arraySwap(array, indexA, indexB) {
 	const tmp = array[indexA];
 	array[indexA] = array[indexB];
 	array[indexB] = tmp;
-}
+};
 
 /**
  * @param {string} string
  * @returns {string}
  */
-function capFirstChar(string) {
+window.capFirstChar = function capFirstChar(string) {
 	return string.charAt(0).toUpperCase() + string.substr(1);
-}
+};
 
 /**
  * @param {string} word
  * @returns {string}
  */
-function addA(word) {
+window.addA = function(word) {
 	let vocal = ['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'];
 	if (vocal.includes(word.charAt(0))) {
 		return `an ${word}`;
 	}
 	return `a ${word}`;
-}
+};
 
 /**
  * @param {number} i
  * @returns {string}
  */
-function ordinalSuffix(i) {
+window.ordinalSuffix = function ordinalSuffix(i) {
 	let j = i % 10;
 	let k = i % 100;
 	if (j === 1 && k !== 11) {
@@ -169,27 +168,27 @@ function ordinalSuffix(i) {
 		return `${i}rd`;
 	}
 	return `${i}th`;
-}
+};
 
 /**
  * @param {number} i
  * @returns {string}
  */
-function ordinalSuffixWords(i) {
+window.ordinalSuffixWords = function(i) {
 	const text = ["zeroth", "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "ninth", "tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth"];
 	if (i < text.length) {
 		return text[i];
 	}
 	return ordinalSuffix(i);
-}
+};
 
 /**
  * @param {Iterable<any>} array
  * @returns {any[]}
  */
-function removeDuplicates(array) {
+window.removeDuplicates = function removeDuplicates(array) {
 	return [...new Set(array)];
-}
+};
 
 /**
  * Maps an index from one list onto a matching index on the other.
@@ -231,13 +230,13 @@ App.Utils.escapeHtml = function(text) {
  * @param {Iterable} list
  * @return {{}}
  */
-function mapIdList(list) {
+window.mapIdList = function(list) {
 	let mappedList = {};
 	for (const item of list) {
 		mappedList[item.id] = item;
 	}
 	return mappedList;
-}
+};
 
 /**
  * Topological sorting algorithm