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:

  1. Create the init script:
sudo nano /etc/init.d/swipl-tinker
  1. 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
  1. Make it executable and enable:
sudo chmod +x /etc/init.d/swipl-tinker
sudo update-rc.d swipl-tinker defaults
  1. Start the service:
sudo /etc/init.d/swipl-tinker start
  1. 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:

  1. Create the service file:
sudo nano /etc/systemd/system/swipl-tinker.service
  1. 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
  1. 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:

  1. Check that the server is running:

    ps aux | grep swipl
    
  2. Test in browser: http://localhost:8080/wasm/tinker
  3. Verify SWI-Prolog version in the console:

    ?- current_prolog_flag(version, V).
    

    Should return the version number (e.g., V = 100101 for 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

Published: 2026-01-22