diff --git a/examples/javascript_interop/index.html b/examples/javascript_interop/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..bc1082bdcf047432ff6298379721754c883716db
--- /dev/null
+++ b/examples/javascript_interop/index.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>JavaScript Interop</title>
+
+    <script src="../testapp/testapp.js"></script>
+    <script src="../testapp/qtloader.js"></script>
+    <script>
+
+        // JavaScript function which can be called from C++, see
+        // ../testapp/jsinterop.cpp
+        function helloJs(name) {
+            console.log("Hello " + name + " from helloJs");
+        }
+        
+        // A pice of data, can be accessed from C++ as well
+        window.data = new Uint8Array([1, 2, 3 ,4]);
+        
+        window.addEventListener('load', async () => {
+            const qtloader = QtLoader({
+                containerElements: [document.getElementById("container")],
+                showCanvas: () => {
+
+                    // This example does provide a UI. Instead, enable the "Call C++"
+                    // button which calls the function exported from jsinterop.cpp.
+                    const callCppButton = document.getElementById("callcpp");
+                    callCppButton.disabled = false;
+                    callCppButton.addEventListener("click", () => {
+                        qtloader.module()["helloCpp"]("Qt");
+                    });
+                },
+            });
+            qtloader.loadEmscriptenModule("../testapp/testapp");
+        })
+    </script>
+  </head>
+  <body>
+      <h1>Qt for WebAssembly: JavaScript Interop</h1>
+      <p> This examples show how to call C++ functions from JavaScript,
+          and vice versa. Output is printed to the JavaScript console.</p>
+      <button type="button" disabled="true" id="callcpp" disabled>Call C++ function</button>
+      <div id="container" style="width:320px; height:200px; visibility:hidden"></div>
+  </body>
+</html>
diff --git a/examples/testapp/CMakeLists.txt b/examples/testapp/CMakeLists.txt
index 2e6fb896dbaabab97c60d2b0a2468a2e393fcdb5..35ff3d2e579db7580f0ad7901e1dc7117620b74a 100644
--- a/examples/testapp/CMakeLists.txt
+++ b/examples/testapp/CMakeLists.txt
@@ -6,7 +6,11 @@ set(CMAKE_AUTOMOC ON)
 find_package(Qt6 REQUIRED COMPONENTS Core Gui)
 
 qt_add_executable(testapp
-    main.cpp rasterwindow.cpp ../../qtwebutils.cpp popup.cpp
+    main.cpp
+    rasterwindow.cpp
+    popup.cpp
+    jsinterop.cpp
+    ../../qtwebutils.cpp
 )
 
 target_link_libraries(testapp PUBLIC
diff --git a/examples/testapp/jsinterop.cpp b/examples/testapp/jsinterop.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..49a851ccdf0933e9b39bdc54c408263bd51efda8
--- /dev/null
+++ b/examples/testapp/jsinterop.cpp
@@ -0,0 +1,36 @@
+#include <QtCore>
+#include <algorithm>
+#include <emscripten/bind.h>
+#include <emscripten/val.h>
+
+// This example demonstrates how to interop with JavaScript code
+// and data from C++ using Emscripten bind.h and val.h.
+
+// Sample C++ function. This function is exported to JavaScript using
+// EMSCRIPTEN_BINDINGS below.
+void helloCpp(std::string name)
+{
+    qDebug() << "Hello" << QString::fromStdString(name) << "from helloCpp";
+
+    // Call JS function. See defenition in ../examples/jsinterop/jsinterop.cpp
+    // The function is accessed via the global "window" object.
+    emscripten::val window = emscripten::val::global("window");
+    window.call<void>("helloJs", name);
+
+    // Copy data from a JavaScript Array. This copies from the
+    // JavaScript heap to the C++ heap.
+    QByteArray data = QByteArray::fromEcmaUint8Array(window["data"]);
+    qDebug() << "Got data bytes" << QString::number(data[0]) << QString::number(data[1])
+                                 << QString::number(data[2]) << QString::number(data[3]);
+
+    // Clear Js-side data, if we don't want to hold a copy there
+    window.set("data", emscripten::val::null());
+    
+    // Or assign back to it
+    std::reverse(data.begin(), data.end());
+    window.set("data", data.toEcmaUint8Array());
+}
+
+EMSCRIPTEN_BINDINGS(qt_webutils) {
+    emscripten::function("helloCpp", &helloCpp);
+}