https://turtleware.eu/posts/Common-Lisp-and-WebAssembly.html TurtleWare About Blog Mastodon Fossil Codeberg Patreon RSS feed Common Lisp and WebAssembly Tagged as lisp, webassembly Written on 2025-11-28 by Daniel Kochmanski Table of Contents 1. Building ECL 2. Building WECL 3. Building user programs 4. Extending ASDF 5. Funding Using Common Lisp in WASM enabled runtimes is a new frontier for the Common Lisp ecosystem. In the previous post Using Common Lisp from inside the Browser I've discussed how to embed Common Lisp scripts directly on the website, discussed the foreign function interface to JavaScript and SLIME port called LIME allowing the user to connect with a local Emacs instance. This post will serve as a tutorial that describes how to build WECL and how to cross-compile programs to WASM runtime. Without further ado, let's dig in. Building ECL To compile ECL targeting WASM we first build the host version and then we use it to cross-compile it for the target architecture. git clone https://gitlab.com/embeddable-common-lisp/ecl.git cd ecl export ECL_SRC=`pwd` export ECL_HOST=${ECL_SRC}/ecl-host ./configure --prefix=${ECL_HOST} && make -j32 && make install Currently ECL uses Emscripten SDK that implements required target primitives like libc. In the meantime, I'm also porting ECL to WASI, but it is not ready yet. In any case we need to install and activate emsdk: git clone https://github.com/emscripten-core/emsdk.git pushd emsdk ./emsdk install latest ./emsdk activate latest source ./emsdk_env.sh popd Finally it is time to build the target version of ECL. A flag --disable-shared is optional, but keep in mind that cross-compilation of user programs is a new feature and it is still taking shape. Most notably some nuances with compiling systems from .asd files may differ depending on the flag used here. make distclean # removes build/ directory export ECL_WASM=${ECL_SRC}/ecl-wasm export ECL_TO_RUN=${ECL_HOST}/bin/ecl emconfigure ./configure --host=wasm32-unknown-emscripten --build=x86_64-pc-linux-gnu \ --with-cross-config=${ECL_SRC}/src/util/wasm32-unknown-emscripten.cross_config \ --prefix=${ECL_WASM} --disable-shared --with-tcp=no --with-cmp=no emmake make -j32 && emmake make install # some files need to be copied manually cp build/bin/ecl.js build/bin/ecl.wasm ${ECL_WASM} Running from a browser requires us to host the file. To spin Common Lisp web server on the spot, we can use one of our scripts (that assume that quicklisp is installed to download hunchentoot). export WEBSERVER=${ECL_SRC}/src/util/webserver.lisp ${ECL_TO_RUN} --load $WEBSERVER # After the server is loaded run: # firefox localhost:8888/ecl-wasm/ecl.html Running from node is more straightforward from the console perspective, but there is one caveat: read operations are not blocking, so if we try to run a default REPL we'll have many nested I /O errors because stdin returns EOF. Running in batch mode works fine though: node ecl-wasm/ecl.js --eval '(format t "Hello world!~%")' --eval '(quit)' warning: unsupported syscall: __syscall_prlimit64 Hello world! program exited (with status: 0), but keepRuntimeAlive() is set (counter=0) due to an async operation, so halting execution but not exiting the runtime or preventing further async execution (you can use emscripten_force_exit, if you want to force a true shutdown) The produced wasm is not suitable for running in other runtimes, because Emscripten requires additional functions to emulate setjmp. For example: wasmedge ecl-wasm/ecl.wasm [2025-11-21 13:34:54.943] [error] instantiation failed: unknown import, Code: 0x62 [2025-11-21 13:34:54.943] [error] When linking module: "env" , function name: "invoke_iii" [2025-11-21 13:34:54.943] [error] At AST node: import description [2025-11-21 13:34:54.943] [error] This may be the import of host environment like JavaScript or Golang. Please check that you've registered the necessary host modules from the host programming language. [2025-11-21 13:34:54.943] [error] At AST node: import section [2025-11-21 13:34:54.943] [error] At AST node: module Building WECL The previous step allowed us to run vanilla ECL. Now we are going to use artifacts created during the compilation to create an application that skips boilerplate provided by vanilla Emscripten and includes Common Lisp code for easier development - FFI to JavaScript, windowing abstraction, support for