Notes on building SWI-Prolog for WebAssembly (WASM)
Table of Contents
Because I encountered difficulties compiling version 10.1.1 of SWI-Prolog for WASM, I have decided to publish these notes as a complement to the official web page on this topic. I am sincerely thankful to Jan Wielemaker for his patient help, hence this webpage as a return favor. I am also indebted to claude.ai who helped me, both to install SWI-Prolog for WASM and to complete this “howto”.
Prerequisites for Debian/Ubuntu/MX Linux users
Minimal dependencies for WASM compilation only
If you only want to compile SWI-Prolog for WASM (not the native version), install these essential packages:
# Essential build tools
sudo apt-get update
sudo apt-get install -y build-essential cmake ninja-build git
# Minimal required libraries for WASM
sudo apt-get install -y \
zlib1g-dev \
libgmp-dev \
libpcre2-dev \
curl \
ca-certificates \
pkg-config
Note: Node.js is required to test the WASM build. Install it if not already present:
# Install Node.js (version 16 or higher recommended)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
Full dependencies (for native SWI-Prolog compilation)
If you also want to compile the native version of SWI-Prolog, install these additional packages. See Prerequisites for Debian based systems for the most up-to-date list:
# Graphics and GUI (SDL3-based, replacing old X11 dependencies)
sudo apt-get install -y \
libsdl3-dev \
libpango1.0-dev \
libcairo2-dev \
libfreetype6-dev \
libfontconfig1-dev
# Additional libraries
sudo apt-get install -y \
libreadline-dev \
libedit-dev \
libunwind-dev \
libssl-dev \
unixodbc-dev \
libarchive-dev \
libossp-uuid-dev \
libdb-dev \
libyaml-dev
# Optional but recommended
sudo apt-get install -y \
junit \
texlive-latex-base \
graphviz
Note for MX Linux / Debian Stable users: If libsdl3-dev is not available in your repositories,
you can skip the GUI dependencies - they are not needed for WASM compilation or for server-based usage.
To Linux Users (especially MX Linux users)
Do not choose /usr/local/ as installation directory. When installing a
new major version, few distributions allow you to preserve
this path, which belongs to the root partition. This means that, if you
are like me a Linux MX user, to update from version 23 to 25, you will
have to reinstall everything you put in /usr/local/. That is why I
have finally decided to put the installation directory /wasm/ inside
my home: /home/joseph/wasm/. So, inside your home:
mkdir ~/wasm
cd ~/wasm
and everything related to wasm is downloaded and compiled inside ~/wasm.
Preparation
You need to download the Emscripten compiler (inside ~/wasm!).
Follow the instructions on its home page. But, DO NOT INSTALL the
latest version; instead:
cd ~/wasm
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install 4.0.15
./emsdk activate 4.0.15
source ./emsdk_env.sh
Important: You’ll need to run source ~/wasm/emsdk/emsdk_env.sh in each new terminal session where you want to compile WASM code.
Dependencies installation
zlib (mandatory)
The link https://zlib.net/current/zlib.tar.gz
is a permalink for the most recent release. Download this package
inside ~/wasm and then:
cd ~/wasm
wget https://zlib.net/current/zlib.tar.gz
tar xvfz zlib.tar.gz
cd zlib-1.3.1 # (or zlib-x.y.z for a newer version)
emconfigure ./configure --static --prefix=$HOME/wasm
emmake make
emmake make install
pcre2 (optional but recommended)
Inside ~/wasm:
cd ~/wasm
git clone https://github.com/PCRE2Project/pcre2.git
mkdir -p pcre2-build
cd pcre2-build
emcmake cmake -DCMAKE_INSTALL_PREFIX=$HOME/wasm -DPCRE2GREP_SUPPORT_JIT=OFF -G Ninja ../pcre2
ninja
ninja install
GMP library (optional but recommended for arithmetic performance)
Inside ~/wasm, download the latest version
https://gmplib.org/download/gmp/gmp-6.3.0.tar.xz and:
cd ~/wasm
wget https://gmplib.org/download/gmp/gmp-6.3.0.tar.xz
tar xvf gmp-6.3.0.tar.xz
cd gmp-6.3.0
emconfigure ./configure \
--prefix=$HOME/wasm \
--host=none \
--disable-assembly \
--enable-fat=no \
ABI=longlong
emmake make
emmake make install
SWI-Prolog sources
Inside ~/wasm, download the latest version of SWI-Prolog:
cd ~/wasm
git clone https://github.com/SWI-Prolog/swipl-devel.git
cd swipl-devel
Once the sources are downloaded, create the WASM build directory:
cd ~/wasm/swipl-devel
mkdir build.wasm
cd build.wasm
Create a configure file with the following content:
#!/bin/bash
WASM_HOME=$HOME/wasm
source $WASM_HOME/emsdk/emsdk_env.sh
TOOLCHAIN=$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake
cmake -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAIN \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_FIND_ROOT_PATH="$HOME/wasm" \
-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY \
-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY \
-DGMP_ROOT="$HOME/wasm" \
-DINSTALL_DOCUMENTATION=OFF \
-G Ninja ..
Now configure, build and test:
bash configure
ninja
ctest -j 8
node src/swipl.js
The last command should give something like this:
joseph@mx:~/wasm/swipl-devel/build.wasm$ node src/swipl.js
Welcome to SWI-Prolog (32 bits, version 10.1.1-27-ga959da416)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.
CMake built from "/home/joseph/wasm/swipl-devel/build.wasm"
For online help and background, visit https://www.swi-prolog.org
?-
Tinker Installation and Configuration
Tinker is the web-based playground for SWI-Prolog. Here’s how to set it up:
Installing Tinker
Clone the Tinker repository:
cd ~/wasm/swipl-devel/src/wasm/demos
git clone https://github.com/SWI-Prolog/tinker
Or, if you have a custom fork:
cd ~/wasm/swipl-devel/src/wasm/
# Remove the default demos and replace with your version
mv demos demos.original_backup
git clone https://github.com/your-username/your-tinker-fork demos
Copying WASM files to demos
The demos directory needs access to the compiled WASM files:
cp ~/wasm/swipl-devel/build.wasm/src/swipl-bundle.js ~/wasm/swipl-devel/src/wasm/demos/
cp ~/wasm/swipl-devel/build.wasm/src/swipl-web.* ~/wasm/swipl-devel/src/wasm/demos/
Configuring the HTTP server
The server.pl file in ~/wasm/swipl-devel/src/wasm/ should point to the correct paths.
Verify/update these lines:
user:file_search_path(web, '/home/YOUR_USERNAME/wasm/swipl-devel/src/wasm/demos').
user:file_search_path(web, '/home/YOUR_USERNAME/wasm/swipl-devel/src/wasm/demos/tinker').
Testing manually
You can test the server manually:
cd ~/wasm/swipl-devel/build.wasm
swipl ../src/wasm/server.pl --port=8080
Then open your browser to http://localhost:8080/wasm/tinker
Setting up automatic startup (for MX Linux / SysVinit systems)
Create an init script for automatic startup:
- Create the init script:
sudo nano /etc/init.d/swipl-tinker
- Add this content (adjust YOURUSERNAME):
#!/bin/sh
### BEGIN INIT INFO
# Provides: swipl-tinker
# Required-Start: $network
# Required-Stop: $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Description: SWI-Prolog Tinker WASM Server
### END INIT INFO
DAEMON="/usr/local/bin/swipl" # or /usr/bin/swipl
ARGS="../src/wasm/server.pl --port=8080"
WORKDIR="/home/YOUR_USERNAME/wasm/swipl-devel/build.wasm"
NAME="swipl-tinker"
PIDFILE="/var/run/$NAME.pid"
case "$1" in
start)
echo "Starting $NAME..."
cd "$WORKDIR"
nohup $DAEMON $ARGS > /dev/null 2>&1 &
echo $! > "$PIDFILE"
;;
stop)
echo "Stopping $NAME..."
[ -f "$PIDFILE" ] && kill $(cat "$PIDFILE") 2>/dev/null
rm -f "$PIDFILE"
;;
restart)
$0 stop
sleep 2
$0 start
;;
status)
if [ -f "$PIDFILE" ]; then
PID=$(cat "$PIDFILE")
if kill -0 $PID 2>/dev/null; then
echo "$NAME is running (PID: $PID)"
else
echo "$NAME is not running (stale PID file)"
fi
else
echo "$NAME is not running"
fi
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
- Make it executable and enable:
sudo chmod +x /etc/init.d/swipl-tinker
sudo update-rc.d swipl-tinker defaults
- Start the service:
sudo /etc/init.d/swipl-tinker start
- Check status:
sudo /etc/init.d/swipl-tinker status
The server will now start automatically on boot.
For systemd-based systems
If your system uses systemd instead of SysVinit, create a systemd service:
- Create the service file:
sudo nano /etc/systemd/system/swipl-tinker.service
- Add this content (adjust YOURUSERNAME):
[Unit]
Description=SWI-Prolog Tinker WASM Server
After=network.target
[Service]
Type=simple
User=YOUR_USERNAME
WorkingDirectory=/home/YOUR_USERNAME/wasm/swipl-devel/build.wasm
ExecStart=/usr/local/bin/swipl ../src/wasm/server.pl --port=8080
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
- Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable swipl-tinker.service
sudo systemctl start swipl-tinker.service
sudo systemctl status swipl-tinker.service
Verification
After installation and service setup:
Check that the server is running:
ps aux | grep swipl- Test in browser: http://localhost:8080/wasm/tinker
Verify SWI-Prolog version in the console:
?- current_prolog_flag(version, V).Should return the version number (e.g.,
V = 100101for version 10.1.1)
Troubleshooting
Browser cache issues
If you update the WASM files but still see an old version:
- Clear browser cache completely
- Or use hard refresh:
Ctrl+Shift+R(Firefox/Chrome on Linux)
Port already in use
If port 8080 is occupied:
sudo lsof -i :8080 # Find what's using the port
sudo kill PID # Kill the process
Service won’t start
Check logs:
# For SysVinit
tail -f /var/log/syslog
# For systemd
sudo journalctl -u swipl-tinker.service -f
Additional Resources
Updating to a newer SWI-Prolog version
When a new version is released:
cd ~/wasm/swipl-devel
git pull
cd build.wasm
bash configure
ninja
# Copy updated WASM files
cp src/swipl-bundle.js ../src/wasm/demos/
cp src/swipl-web.* ../src/wasm/demos/
# Restart service
sudo /etc/init.d/swipl-tinker restart # or systemctl restart swipl-tinker