diff --git a/Tools/wasm/README.md b/Tools/wasm/README.md
new file mode 100644
index 00000000..cf2e2b85
--- /dev/null
+++ b/Tools/wasm/README.md
@@ -0,0 +1,12 @@
+
+**Wasm (Linux, macOS or WSL)**
+```bash
+../../Kinc/make --from ../.. wasm --compile
+# Copy resulting armorcore.wasm file to Deployment
+# Copy index.html to Deployment
+# Copy krom.js to Deployment
+# Copy https://github.com/Kode/Kinc/tree/main/Backends/System/Wasm/JS-Sources to Deployment
+# Todo:
+# start.js has hard-coded .wasm file name: https://github.com/Kode/Kinc/blob/main/Backends/System/Wasm/JS-Sources/start.js#L48
+# memory.c has hard-coded size: https://github.com/Kode/Kinc/blob/main/miniClib/memory.c#L6
+```
diff --git a/Tools/wasm/index.html b/Tools/wasm/index.html
new file mode 100644
index 00000000..e7804cc1
--- /dev/null
+++ b/Tools/wasm/index.html
@@ -0,0 +1,11 @@
+
+
+
+
+ Kinc
+
+
+
+
+
+
diff --git a/Tools/wasm/kincflags.js b/Tools/wasm/kincflags.js
new file mode 100644
index 00000000..45aeb0d2
--- /dev/null
+++ b/Tools/wasm/kincflags.js
@@ -0,0 +1,5 @@
+
+// Imported by armorcore/kfile.js
+flags.name = 'armorcore';
+flags.package = 'org.armorcore';
+flags.with_g2 = true;
diff --git a/Tools/wasm/krom.js b/Tools/wasm/krom.js
new file mode 100644
index 00000000..3f9ed3fe
--- /dev/null
+++ b/Tools/wasm/krom.js
@@ -0,0 +1,52 @@
+// G2 test
+
+function foregroundCallback() {}
+function resumeCallback() {}
+function pauseCallback() {}
+function backgroundCallback() {}
+function shutdownCallback() {}
+function keyboardDownCallback(key) {}
+function keyboardUpCallback(key) {}
+function keyboardPressCallback(char) {}
+function mouseDownCallback(button, x, y) {}
+function mouseUpCallback(button, x, y) {}
+function mouseMoveCallback(x, y, mx, my) {}
+function mouseWheelCallback(delta) {}
+
+const api = 6;
+const resizable = 1;
+const minimizable = 2;
+const maximizable = 4;
+Krom.init("KromApp", 640, 480, 1, true, 0, resizable | minimizable | maximizable, api, -1, -1, 60);
+Krom.setCallback(renderCallback);
+Krom.setApplicationStateCallback(foregroundCallback, resumeCallback, pauseCallback, backgroundCallback, shutdownCallback);
+Krom.setKeyboardDownCallback(keyboardDownCallback);
+Krom.setKeyboardUpCallback(keyboardUpCallback);
+Krom.setKeyboardPressCallback(keyboardPressCallback);
+Krom.setMouseDownCallback(mouseDownCallback);
+Krom.setMouseUpCallback(mouseUpCallback);
+Krom.setMouseMoveCallback(mouseMoveCallback);
+Krom.setMouseWheelCallback(mouseWheelCallback);
+
+let logo = { texture_: Krom.loadImage("logo.png", false) };
+let painter_image_vert = Krom.loadBlob("painter-image-webgl2.vert");
+let painter_image_frag = Krom.loadBlob("painter-image-webgl2.frag");
+let painter_colored_vert = Krom.loadBlob("painter-colored-webgl2.vert");
+let painter_colored_frag = Krom.loadBlob("painter-colored-webgl2.frag");
+let painter_text_vert = Krom.loadBlob("painter-text-webgl2.vert");
+let painter_text_frag = Krom.loadBlob("painter-text-webgl2.frag");
+Krom.g2_init(painter_image_vert, painter_image_frag, painter_colored_vert, painter_colored_frag, painter_text_vert, painter_text_frag);
+
+function renderCallback() {
+ Krom.begin(null, null);
+
+ let flags = 0;
+ flags |= 1; // Color
+ Krom.clear(flags, 0xff000000, 1.0, null);
+
+ Krom.g2_begin();
+ Krom.g2_draw_scaled_sub_image(logo, 0, 0, 400, 400, 120, 40, 400, 400);
+ Krom.g2_end();
+
+ Krom.end();
+}