diff --git a/.github/workflows/emscripten.yml b/.github/workflows/emscripten.yml
new file mode 100644
index 00000000..4be8931f
--- /dev/null
+++ b/.github/workflows/emscripten.yml
@@ -0,0 +1,71 @@
+name: Emscripten
+
+on:
+ push:
+ branches: [develop, main]
+ pull_request:
+ branches: [develop, main]
+ workflow_dispatch:
+
+jobs:
+ emscripten:
+ name: Emscripten WebAssembly Build
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup Emscripten
+ uses: mymindstorm/setup-emsdk@v14
+
+ - name: Configure
+ run: emcmake cmake -B build-em -S emscripten -DCMAKE_BUILD_TYPE=Release
+
+ - name: Build
+ run: cmake --build build-em -j
+
+ - name: Verify artifacts
+ run: |
+ test -f build-em/chaiscript.js
+ test -f build-em/chaiscript.wasm
+ test -f build-em/chaiscript.html
+ echo "All expected artifacts present"
+ ls -lh build-em/chaiscript.*
+
+ - name: Upload artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: chaiscript-web
+ path: |
+ build-em/chaiscript.js
+ build-em/chaiscript.wasm
+ build-em/chaiscript.html
+ retention-days: 90
+
+ publish:
+ name: Publish WASM Release
+ needs: emscripten
+ if: github.ref == 'refs/heads/develop' && github.event_name == 'push'
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - uses: actions/download-artifact@v4
+ with:
+ name: chaiscript-web
+ path: artifacts
+
+ - name: Publish to wasm-latest release
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ # Flatten: artifacts may contain build-em/ subdirectory
+ find artifacts -type f \( -name 'chaiscript.js' -o -name 'chaiscript.wasm' -o -name 'chaiscript.html' \) -exec cp {} . \;
+
+ # Delete existing release if present, then recreate
+ gh release delete wasm-latest --repo "$GITHUB_REPOSITORY" -y 2>/dev/null || true
+ gh release create wasm-latest \
+ --repo "$GITHUB_REPOSITORY" \
+ --title "ChaiScript WASM Build (latest)" \
+ --notes "Auto-published from develop branch. Built with Emscripten for browser use." \
+ --prerelease \
+ chaiscript.js chaiscript.wasm chaiscript.html
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ad3e79a0..029f33da 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -423,6 +423,10 @@ if(BUILD_TESTING)
target_link_libraries(multifile_test ${LIBS})
add_test(NAME MultiFile_Test COMMAND multifile_test)
+ add_executable(emscripten_eval_test unittests/emscripten_eval_test.cpp)
+ target_link_libraries(emscripten_eval_test ${LIBS})
+ add_test(NAME Emscripten_Eval_Test COMMAND emscripten_eval_test)
+
install(TARGETS test_module RUNTIME DESTINATION bin LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/chaiscript")
endif()
endif()
diff --git a/emscripten/CMakeLists.txt b/emscripten/CMakeLists.txt
new file mode 100644
index 00000000..7751631f
--- /dev/null
+++ b/emscripten/CMakeLists.txt
@@ -0,0 +1,35 @@
+# Emscripten/WebAssembly build for ChaiScript
+# Based on work by Rob Loach: https://github.com/RobLoach/ChaiScript.js
+#
+# Usage:
+# emcmake cmake -B build-em -S emscripten
+# cmake --build build-em
+
+cmake_minimum_required(VERSION 3.12)
+project(chaiscript_em)
+
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+# Emscripten-specific compiler/linker flags
+add_definitions(-DCHAISCRIPT_NO_THREADS -DCHAISCRIPT_NO_DYNLOAD)
+
+add_executable(chaiscript chaiscript_em.cpp)
+target_include_directories(chaiscript PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../include)
+
+# Emscripten link flags: enable embind, allow memory growth, export as ES module-compatible
+target_link_options(chaiscript PRIVATE
+ --bind
+ -sALLOW_MEMORY_GROWTH=1
+ -sEXPORT_ES6=0
+ -sMODULARIZE=0
+ -sINVOKE_RUN=0
+)
+
+# Copy the HTML shell to the build output directory
+add_custom_command(TARGET chaiscript POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_SOURCE_DIR}/chaiscript.html
+ ${CMAKE_CURRENT_BINARY_DIR}/chaiscript.html
+ COMMENT "Copying HTML frontend to build directory"
+)
diff --git a/emscripten/chaiscript.html b/emscripten/chaiscript.html
new file mode 100644
index 00000000..29e32252
--- /dev/null
+++ b/emscripten/chaiscript.html
@@ -0,0 +1,237 @@
+
+
+
+
+
+
+ ChaiScript
+
+
+
+
+ ChaiScript
+ Interactive Playground
+ Loading...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/emscripten/chaiscript_em.cpp b/emscripten/chaiscript_em.cpp
new file mode 100644
index 00000000..06806797
--- /dev/null
+++ b/emscripten/chaiscript_em.cpp
@@ -0,0 +1,23 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2019, Rob Loach (https://github.com/RobLoach/ChaiScript.js)
+// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
+// http://www.chaiscript.com
+//
+// Emscripten/WebAssembly wrapper for ChaiScript.
+// Based on work by Rob Loach: https://github.com/RobLoach/ChaiScript.js
+
+#include "chaiscript_eval.hpp"
+
+#ifdef __EMSCRIPTEN__
+#include
+
+EMSCRIPTEN_BINDINGS(chaiscript) {
+ emscripten::function("eval", &chaiscript_eval);
+ emscripten::function("evalString", &chaiscript_eval_string);
+ emscripten::function("evalBool", &chaiscript_eval_bool);
+ emscripten::function("evalInt", &chaiscript_eval_int);
+ emscripten::function("evalFloat", &chaiscript_eval_float);
+ emscripten::function("evalDouble", &chaiscript_eval_double);
+}
+#endif
diff --git a/emscripten/chaiscript_eval.hpp b/emscripten/chaiscript_eval.hpp
new file mode 100644
index 00000000..52791ab3
--- /dev/null
+++ b/emscripten/chaiscript_eval.hpp
@@ -0,0 +1,48 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2019, Rob Loach (https://github.com/RobLoach/ChaiScript.js)
+// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
+// http://www.chaiscript.com
+
+// Shared eval helper functions for the ChaiScript Emscripten wrapper.
+// These functions provide typed evaluation of ChaiScript expressions,
+// used by both the Emscripten/WebAssembly build and native tests.
+
+#ifndef CHAISCRIPT_EMSCRIPTEN_EVAL_HPP_
+#define CHAISCRIPT_EMSCRIPTEN_EVAL_HPP_
+
+#include
+#include
+
+namespace detail {
+inline chaiscript::ChaiScript &get_chai_instance() {
+ static chaiscript::ChaiScript chai;
+ return chai;
+}
+} // namespace detail
+
+inline void chaiscript_eval(const std::string &input) {
+ detail::get_chai_instance().eval(input);
+}
+
+inline std::string chaiscript_eval_string(const std::string &input) {
+ return detail::get_chai_instance().eval(input);
+}
+
+inline bool chaiscript_eval_bool(const std::string &input) {
+ return detail::get_chai_instance().eval(input);
+}
+
+inline int chaiscript_eval_int(const std::string &input) {
+ return detail::get_chai_instance().eval(input);
+}
+
+inline float chaiscript_eval_float(const std::string &input) {
+ return detail::get_chai_instance().eval(input);
+}
+
+inline double chaiscript_eval_double(const std::string &input) {
+ return detail::get_chai_instance().eval(input);
+}
+
+#endif /* CHAISCRIPT_EMSCRIPTEN_EVAL_HPP_ */
diff --git a/unittests/emscripten_eval_test.cpp b/unittests/emscripten_eval_test.cpp
new file mode 100644
index 00000000..6084c9d3
--- /dev/null
+++ b/unittests/emscripten_eval_test.cpp
@@ -0,0 +1,51 @@
+// Test that validates the eval patterns used by the Emscripten wrapper.
+// Based on work by Rob Loach (https://github.com/RobLoach/ChaiScript.js)
+
+#ifndef CHAISCRIPT_NO_THREADS
+#define CHAISCRIPT_NO_THREADS
+#endif
+
+#ifndef CHAISCRIPT_NO_DYNLOAD
+#define CHAISCRIPT_NO_DYNLOAD
+#endif
+
+#include
+#include "../emscripten/chaiscript_eval.hpp"
+#include
+#include
+#include
+
+int main() {
+ // Test eval (void return) - same as Emscripten eval()
+ chaiscript_eval("var x = 42");
+
+ // Test evalString - same as Emscripten evalString()
+ std::string s = chaiscript_eval_string("to_string(x)");
+ assert(s == "42");
+
+ // Test evalInt - same as Emscripten evalInt()
+ int i = chaiscript_eval_int("1 + 2");
+ assert(i == 3);
+
+ // Test evalBool - same as Emscripten evalBool()
+ bool b = chaiscript_eval_bool("true");
+ assert(b == true);
+
+ b = chaiscript_eval_bool("false");
+ assert(b == false);
+
+ // Test evalFloat - same as Emscripten evalFloat()
+ float f = chaiscript_eval_float("1.5f");
+ assert(std::abs(f - 1.5f) < 0.001f);
+
+ // Test evalDouble - same as Emscripten evalDouble()
+ double d = chaiscript_eval_double("3.14");
+ assert(std::abs(d - 3.14) < 0.001);
+
+ // Test a more complex expression
+ chaiscript_eval("def square(n) { return n * n; }");
+ int sq = chaiscript_eval_int("square(7)");
+ assert(sq == 49);
+
+ return 0;
+}