# SWI-Prolog in the browser using WASM

**URL:** <https://swi-prolog.discourse.group/t/swi-prolog-in-the-browser-using-wasm/5650>\
**Category:** Wiki\
**Tags:** swish, wasm, browser\
**Created:** [August 3, 2022, 1:53pm UTC](https://swi-prolog.discourse.group/t/swi-prolog-in-the-browser-using-wasm/5650 "2022-08-03T13:53:19Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [August 3, 2022, 1:53pm UTC](https://swi-prolog.discourse.group/t/swi-prolog-in-the-browser-using-wasm/5650/1 "2022-08-03T13:53:19Z")

</div>

This topic discusses running SWI-Prolog in your browser using WASM (Web Assembler).

# Updates

Below we list important updates that are also handled in this wiki page when applicable.

- [2025-02-28] In the past two months, the WASM version added _engines_, allowing for multiple `async` Prolog tasks to coexist, allow _yielding_ from the debugger and the [WASM shell](http://wasm.swi-prolog.org/wasm/shell) got a basic debugger, ANSI tty escape support for color and hyperlinks and much more.
- [2023-05-03] Various updates to the build process. See also [Building SWI-Prolog using Emscripten for WebAssembly (WASM)](https://www.swi-prolog.org/build/WebAssembly.html)
- [2022-09-19] Released in 8.5.17. Interfaces should from now on be fairly stable. Documentation is at [SWI-Prolog -- library(wasm): WASM version support](https://www.swi-prolog.org/pldoc/man?section=wasm)
- [2022-09-19] Renamed js\_yield/2 to await/2 and js\_can\_yield/0 to is\_async/0.
- [2022-09-19] Support loading entire applications from a URL, e.g.

```prolog
?- ['https://raw.githubusercontent.com/JanWielemaker/chat80/master/prolog/chat80.pl'].

```

- [2022-09-15] Added landing page [SWI-Prolog WASM demos](https://wasm.swi-prolog.org/wasm/) with access to other demos.
- [2022-09-15] Added `library(dom)` to provide (partial) compatibility with [Tau-Prolog](http://tau-prolog.org/). This was used to port @PaulBrownMagic’s demo for Tau-Prolog, now running at [CBG Chords](https://wasm.swi-prolog.org/wasm/cbg).
- [2022-09-15] Added compiling from `<script type="text/prolog">` elements.
- [2022-09-14] Improve handling of asynchronous code. Notable `Prolog.forEach()` allows enumerating Prolog answers asynchronously and `js_yield/2` allows executing arbitrary _Promises_.
- [2022-09-08] Use JavaScript `BigInt` and Emscripten `-s BIGINT` to achieve full transparency between JavaScript and Prolog for big integers.
- [2022-09-06] Pass test suite using `ctest`
- [2022-09-06] Include most extensions that are meaningful and can be supported on WASM.
- [2022-08-25] Included GMP (Unbounded integers and rational numbers)
- [2022-08-25] Build now also works on MacOS (12.4 on M1)
- [2022-08-19] Start including the _foreign extensions_
- [2022-08-18] Added high level calling of Prolog predicates by JavaScript and of JavaScript from Prolog. These changes allow for e.g., DOM
- [2022-08-09] Fixed several build (dependency) issues. See updated build instructions.
- [2022-08-08] Fixed yield. Add _auto yielding_ such that the browser remains responsive and queries can be aborted. Allow creating multiple files, Added persistent (using the browser `localStorage`) command line history and files.
- [2022-08-05] Extended JavaScript binding, run the browser shell using _yield_.
- [2022-08-03] _“Native friend”_ mechanism for building is no longer needed.
- [2022-08-03] Added `-s ALLOW_MEMORY_GROWTH=1` to allow (notably) for bigger stacks)

* * *

# SWI-Prolog in your browser

There are two options for running SWI-Prolog in your browser:

- Using SWISH. This is a server based solution where the actual Prolog code is executed in a sandboxed environment on a (shared) server. SWISH is a comprehensive and mature solution for running SWI-Prolog in your browser that can support many deployment options.
- Actually running it in your browser. This is accomplished by compiling the SWI-Prolog C sources using [Emscripten](https://emscripten.org) to WASM code. The initial port has been created by @rla. @dmchurch has made some improvements. @josderoo used it [to run the EYE reasoner](https://swi-prolog.discourse.group/t/running-eye-on-swipl-wasm) Inspired by the [Ciao playground](https://ciao-lang.org/playground/) I (@jan) took another look at the WASM port.

## A first shot at a SWI-Prolog shell in your browser

A very first version of running SWI-Prolog interactively in the browser was made by @rla. Since then Emscripten evolved and so did the configuration for SWI-Prolog. I have combined the basic test code and @rla’s initial shell to make something that at least performs the very basic tasks. It is online at

- [https://wasm.swi-prolog.org/wasm](https://wasm.swi-prolog.org/wasm)

### How it works

Normally, SWI-Prolog is an interactive application that reads from and writes to a _console_. That doesn’t work in your browser because the JavaScript functions called are supposed to complete (quickly). @rla found a nice trick around that: we initialize Prolog and for running a query we put the query into a string that is used for input and we call break/0. This starts a toplevel that reads and runs the query. The price is that the query cannot receive any input (i.e., cannot read). Another problem we are faced with is that long running Prolog queries make the browser unresponsive, i.e., we need to return to the browser’s _event loop_ to create a responsive web page. This is solved using [foreign yield](https://www.swi-prolog.org/pldoc/man?section=foreign-yield), a mechanism to make the VM main function (PL\_next\_solution()) return such that it can be resumed. Yielding can be dong explicitly using js\_yield/2. This is currently used for reading the next toplevel query and, if a query succeeds with a choicepoint, for reading whether the user wants the next result or stop. Finally, the VM _auto yields_ every N _inferences_, controlled by the Prolog flag `heartbeat`. The JavaScript wrapper resumes the VM, roughly escaping to the browser event loop every 20ms.

Note that an alternative solution is run Prolog in a _web worker_. This however isolates Prolog from the page, requiring postMessage() to be used to communicate between the page and Prolog.

The SWI-Prolog web appliance uses the Emscripten _virtual file system_. SWI-Prolog runs in `/prolog` and the normal Prolog home, containing the boot file and libraries is in `/swipl`. You can use library(shell) utilities ls/0, pwd/0, cd/1 to look around.

The **editor** is an instance of [CodeMirror](https://codemirror.net/) using the basic Prolog mode from SWISH. The **(Re)consult** button copies the content of the textarea into a file ‘/prolog/file.pl’ and calls consult/1 on that file. It has a dropdown to select a file and buttons to add a new file or delete a file. Files are saved to the browser’s `localStorage` on page unload and reloaded on page load. The **query input** field provides _history_ using the arrow up/down. The history is also saved to the browser’s `localStorage`.

### Calling between JavaScript and Prolog

High level calling from JavaScript to Prolog is based on automatic conversion between JavaScript data and Prolog data. This follows [Consider adding an option to use a different JSON Format · Issue #4 · SWI-Prolog/packages-mqi · GitHub](https://github.com/SWI-Prolog/packages-mqi/issues/4). The table below summarizes the conversion.

| JavaScript | → | Prolog | → | JavaScript | Notes |
| --- | --- | --- | --- | --- | --- |
| string | | atom | | string | |
| | | string | | `{$t:"s", v:Text}` | (1) |
| number | | integer or float | | number or bigint | (2) |
| bigint | | integer | | number or bigint | (2) |
| `{$t:"v", v: Id}` | | variable | | `{$t:"v", v: Id}` | (3) |
| `{$t:"t", Func:[A1,..]}` | | compound `Func(A1, ...)` | | `{$t:"t", Func:[A1,..]}` | |
| Array | | List | | Array | |
| `{$t:"partial", v:Array, t:Tail}` | | Partial List | | `{$t:"partial", v:Array, t:Tail}` | |
| Plain Object | | Dict | | Object | |
| Other Object | | Blob `<js_<Class>>(Id)` | | Object | (4) |

**Notes**

1. Using `Prolog.toJSON(term, {string:"string"})` a Prolog string is converted to a plain JavaScript String. This is currently used by Prolog calling JavaScript.
2. JavaScript `number` is a float. When converting to Prolog and the number is integer we create a Prolog integer, otherwise a Prolog float. When converting from Prolog, a float always becomes a number. Prolog integers below 2^{53} are represented as a JavaScript number. Higher integers are converted to a JavaScript BigInt. Exchanging big numbers for now use a decimal string as intermediate representation.
3. When generated, the _Id_ is an integer. For conversion to a Prolog variable the _Id_ is optional and when two variables share the same _Id_, they are unified.
4. Plain objects are instances of `Object`. Such objects are converted to Prolog dicts.  
Other objects that are passed to Prolog are kept in a JavaScript object (`Prolog.objects`) and represented in Prolog using a _blob_. (Atom) garbage collecting the blob removes the object from `Prolog.objects`.

#### JavaScript calling Prolog

The `Module` contains a property `prolog` that represents Prolog. We summarize the most important ones.

Prolog. **call** (String)Evaluate a string as a Prolog goal. This is typically used for simple goals such as setting a Prolog flag.Prolog. **query** (String, Inputs)Returns an iteratable object. String is the goal which may contain variables. Inputs is an object that may bind some of the variables in the goal. Solutions are created using Query.next() or Query.once(). The first satisfies the JavaScript iterable protocol. The second returns a dict holding the values for the output variables.Prolog. **forEach** (String, [Inputs], [OnAnswer], [Options])Run a Prolog goal created from \_String\_ and \_Inputs\_ as `Prolog.query()`. Call \_OnAnswer\_ on each result. Returns as \_\_Promise\_\_. If \_OnAnswer\_ is omitted, the Promise resolves into a list of answers. Otherwise it returns the number of solutions. \_Options\_ can be used to control concurrency. Using `engine:true`, the query runs in its own engine, allowing for concurrent other Prolog tasks. The `heartbeat:number` property defines how often the engine \_auto yields\_, i.e., control is returned to the browser event loop every \_N\_ Prolog \_inferences\_.

**Examples**

The example below calls sum\_list/2 as once/1, passing a JavaScript Array as a Prolog list and returning an object with the key `Sum` that represents the result.

```javascript
function sum_list(list) {
   return Prolog.query("sum_list(List, Sum)", {List:list}).once().Sum;
}

```

The next example enumerates all answers for p/1

```prolog
for(const r of Prolog.query("p(X)")) {
  console.log(r.X);
}

```

#### Prolog calling JavaScript

Calling JavaScript from Prolog is implemented by `:=/2`, referring to classical _assignment_. The predicate can evaluate several JavaScript expressions. We start with some examples:

```
?- Res := myfunc([1,2,3]).
?- Max := 'Math'.max(10, 20).
?- Out := document.getElementById("output"),
?- Par := document.createElement("p"),
   Par.textContent := "Hello World!",
   _ := document.body.appendChild(Par).

```

During the conversion, _values_ are converted according to the rules in the table above, converting Prolog strings to a plain JavaScript string except for

- An _atom_ is considered a _getter_. Dereferencing starts with `window`.
- A compound is considered a _function call_. Arguments a are evaluated according to the same rules.
- A term `#Term` (# is a prefix operator) is taken as a literal value. Thus, `createElement(p)` would use the global variable `p` as argument. Using `"p"` or `#p` gives the desired result.

### Status, issues and future work

#### Lacking features and issues of the SWI-Prolog WASM port

- **Threads** are not supported, but multiple `async` **Engines** can achieve multi-threading using a single core.
- **Engines** are supported. Engines may be created explicitly and queries may be started on a selected engine. Queries can also be forced to run on a dedicated engine, i.e., the engine is created with the query and destroyed when the query completes.
- **Synchronous I/O**
  - Now deals with reading toplevel queries and prompt for more answers using _yield_
  - _auto yielding_ keeps the page responsive
  - Yielding is still problematic
    - It cannot happen if there are intermediate Prolog → WASM (C) → Prolog callbacks
    - Auto yielding currently happens at the _exit_ port. Recursive loops that call no other predicates only exit when all is done. Possibly we can also yield at the _enter_ port (after head unification).

- Sync the API with **Ciao** where possible?

#### The future of this SWI-Prolog shell

The shell at [https://wasm.swi-prolog.org/wasm](https://wasm.swi-prolog.org/wasm) is just a toy. It is intended as a simple platform to help debugging and resolving the above issues. A likely future scenario is to integrate the WASM based solution into SWISH.

* * *

# Building the WASM version

The instructions below have been tested on Ubuntu 22.04 and MacOS 12.4 on an M1. Please edit this page to report on other platforms.

## Install Node.js

You need at least v18.16 of Node.js, which is available from [GitHub - nodejs/node: Node.js JavaScript runtime ✨🐢🚀✨](https://github.com/nodejs/node). To build this, clone the repository, `git checkout v18.16.0` (or whichever version you wish) and follow the instructions at [GitHub - nodejs/node: Node.js JavaScript runtime ✨🐢🚀✨](https://github.com/nodejs/node#building-nodejs) (you may wish to use the command `./configure --prefix=$HOME/.local` instead of just `./configure`)

## Install emscripten and the zlib library

See [Download and install — Emscripten 4.0.21-git (dev) documentation](https://emscripten.org/docs/getting_started/downloads.html). These instructions install emscripten in `~/wasm/emsdk` on a Linux machine (requires about 1.1 Gb disk)

```sh
cd
mkdir wasm
cd wasm
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh

```

#### build the zlib dependency

Note: [zlib](https://zlib.net/) uses the semantic version in the name of the file, as such it is hardcoded in these instructions. If zlib is updated then the zlib name needs to be changed in these instructions accordingly. (As of 2023-05-03 version: **1.2.13** )

```sh
cd ~/wasm
wget https://zlib.net/zlib-1.2.13.tar.gz
tar -xf "zlib-1.2.13.tar.gz"
cd zlib-1.2.13
emconfigure ./configure --static --prefix=$HOME/wasm
emmake make
emmake make install

```

The `emmake make` command generates quite a few warning messages similar to "warning: a function definition without a prototype is deprecated in all versions of C and is not supported in C2x [-Wdeprecated-non-prototype]`

#### Build the pcre2 library (optional)

Provides `library(pcre)` to Prolog.

```sh
git clone https://github.com/PCRE2Project/pcre2
cd pcre2
git checkout pcre2-10.42
emcmake cmake -DCMAKE_INSTALL_PREFIX=$HOME/wasm -DPCRE2GREP_SUPPORT_JIT=OFF -G Ninja .
ninja
ninja install

```

#### Build the GMP library (optional)

As is, using GMP provides faster rational numbers at the cost of a larger binary and (effectively) LGPL license. When omitted, LibBF based unbound integers, random numbers and rational numbers are used, providing exactly the same features.

```sh
cd ~/wasm
wget https://gmplib.org/download/gmp/gmp-6.2.1.tar.lz
tar xf gmp-6.2.1.tar.lz
cd gmp-6.2.1
emconfigure ./configure --host=none --disable-assembly --prefix=${HOME}/wasm
make -j
make install

```

This process is broken on MacOS. See [Host CC cannot compile on Mac OS Monterey (12.2.1) · Issue #16358 · emscripten-core/emscripten · GitHub](https://github.com/emscripten-core/emscripten/issues/16358). To work around this, create a file `conf` holding the content below, make it executable (`chmod +x conf`) and configure using `emconfigure ./conf`

```sh
#!/bin/sh

export HOST_CC=/usr/bin/clang
./configure --host=none --disable-assembly --prefix=${HOME}/wasm

```

## Download and install SWI-Prolog from the git sources

#### Download the sources

```sh
git clone https://github.com/SWI-Prolog/swipl-devel.git
cd swipl-devel
git submodule update --init

```

### Build the WASM version using Emscripten

```sh
cd swipl-devel
mkdir build.wasm
cd build.wasm

```

Now save the following text to a new file `configure` and make it executable using `chmod +x configure` (or: run `swipl-devel/scripts/configure`, although it defaults to not using GMP)

```sh
export WASM_HOME=$HOME/wasm
export GMP_ROOT=$HOME/wasm
source $WASM_HOME/emsdk/emsdk_env.sh
TOOLCHAIN=$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake
[-f $TOOLCHAIN] || echo "Could not find emscripten toolchain"

cmake -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAIN \
      -DCMAKE_BUILD_TYPE=Release \
      -DCMAKE_FIND_ROOT_PATH=$HOME/wasm \
      -DINSTALL_DOCUMENTATION=OFF \
      -DGMP_ROOT=$GMP_ROOT \
      -DNODE_JS_EXECUTABLE=$(type -p node) \
      -G Ninja ..

```

If you did not build GMP or PCRE, you should also specify `-DUSE_GMP=OFF` and/or `-DSWIPL_PACKAGES_PCRE:BOOL=OFF` (this has not been tested).

Now configure and build the version in the `build.wasm` directory using

```sh
./configure
ninja

```

To **update** to the latest version one should run the commands below. Removing `src/wasm-preload` is required to make sure changes to the Prolog libraries are correctly incorporated in `src/swipl-web.data` which populates `/swipl` in the WASM version.

```sh
git pull
git submodule update --init
cd build.wasm
rm -rf src/wasm-preload && rm -f src/swipl-web.* && ninja

```

## Running using Node.js

If all went right, you can now run the wasm version using `node` as

```sh
$ node src/swipl.js
Welcome to SWI-Prolog (32 bits, version 8.5.15-26-gc1ca50a94)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.

    CMake built from "/home/janw/src/swipl-devel/build.wasm"

For online help and background, visit https://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).

1 ?-

```

## Foreign extensions (packages that rely on C/C++ code)

Although Emscripten does support dynamic linking, it is discouraged because it increases the code size and slows down execution. Therefore the Emscripten port uses a new cmake option: `-DSTATIC_EXTENSIONS`. This adds the foreign libraries ([use\_foreign\_library/1,2](https://www.swi-prolog.org/pldoc/man?predicate=use_foreign_library/1)) to `libswipl` and generates `src/static_packages.h` which is used to make use\_foreign\_module/1 call the initialization function of the package. This way no changes to the packages themselves is required.

Currently ported foreign extensions are below. This set will be extended. You can verify supported features using [check\_installation/0](https://www.swi-prolog.org/pldoc/doc_for?object=check_installation%3Acheck_installation/0).

| Library | Prolog code | C code |
| --- | --- | --- |
| [library(crypt)](https://www.swi-prolog.org/pldoc/man?section=crypt) | [Prolog](https://github.com/SWI-Prolog/packages-clib/blob/master/crypt.pl) | [C](https://github.com/SWI-Prolog/packages-clib/blob/master/crypt.c) |
| [library(double\_metaphone)](https://www.swi-prolog.org/pldoc/doc/_SWI_/library/double_metaphone.pl) | [Prolog](https://github.com/SWI-Prolog/packages-nlp/blob/master/double_metaphone.pl) | [C](https://github.com/SWI-Prolog/packages-nlp/blob/master/double_metaphone.c) |
| [library(porter\_stem)](https://www.swi-prolog.org/pldoc/man?section=porter-stem) | [Prolog](https://github.com/SWI-Prolog/packages-nlp/blob/master/porter_stem.pl) | [C](https://github.com/SWI-Prolog/packages-nlp/blob/master/porter_stem.c) |
| [library(readutil)](https://www.swi-prolog.org/pldoc/doc/_SWI_/library/readutil.pl) | [Prolog](https://github.com/SWI-Prolog/swipl/blob/master/library/readutil.pl) | [C](https://github.com/SWI-Prolog/packages-clib/blob/master/readutil.c) |
| [library(semweb/rdf\_ntriples)](https://www.swi-prolog.org/pldoc/man?section=rdfntriples) | [Prolog](https://github.com/SWI-Prolog/packages-semweb/blob/master/rdf_ntriples.pl) | [C](https://github.com/SWI-Prolog/packages-semweb/blob/master/ntriples.c) |
| [library(semweb/turtle)](https://www.swi-prolog.org/pldoc/man?section=turtle) | [Prolog](https://github.com/SWI-Prolog/packages-semweb/blob/master/turtle.pl) | [C](https://github.com/SWI-Prolog/packages-semweb/blob/master/turtle.c) |
| [library(sgml)](https://www.swi-prolog.org/pldoc/doc/_SWI_/library/sgml.pl) | [Prolog](https://github.com/SWI-Prolog/packages-sgml/blob/master/sgml.pl) | [C](https://github.com/SWI-Prolog/packages-sgml/blob/master/sgml2pl.c) |
| [library(sha)](https://www.swi-prolog.org/pldoc/man?section=sha) | [Prolog](https://github.com/SWI-Prolog/packages-clib/blob/master/sha.pl) | [C](https://github.com/SWI-Prolog/packages-clib/blob/master/sha4pl.c) |
| [library(snowball)](https://www.swi-prolog.org/pldoc/man?section=snowball) | [Prolog](https://github.com/SWI-Prolog/packages-nlp/blob/master/snowball.pl) | [C](https://github.com/SWI-Prolog/packages-nlp/blob/master/snowball.c) |
| [library(uri)](https://www.swi-prolog.org/pldoc/man?section=uri) | [Prolog](https://github.com/SWI-Prolog/packages-clib/blob/master/uri.pl) | [C](https://github.com/SWI-Prolog/packages-clib/blob/master/uri.c) |
| [library(zlib)](https://www.swi-prolog.org/pldoc/man?section=zlib) | [Prolog](https://github.com/SWI-Prolog/packages-zlib/blob/master/zlib.pl) | [C](https://github.com/SWI-Prolog/packages-zlib/blob/master/zlib4pl.c) |
| [library(pcre)](https://www.swi-prolog.org/pldoc/man?section=pcre) | [Prolog](https://github.com/SWI-Prolog/packages-pcre/blob/master/pcre.pl) | [C](https://github.com/SWI-Prolog/packages-zlib/blob/master/pcre4pl.c) |

## Running in your browser

The generated file for your browser is `src/swipl-bundle.js`. The _Prolog shell_ discussed above can be found in `swipl-devel/src/wasm/shell.html`. It can be started using a Prolog based web server in the same directory named `server.pl`. The server must be started in the _build_ directory as it uses `src/swipl-bundle.js` to find the WASM components.

```sh
cd build.wasm
swipl ../src/wasm/server.pl [--port=8000]

```

# How to support such work

As noted in this [post](https://swi-prolog.discourse.group/t/swi-prolog-in-the-browser-using-wasm-reply-1/5652), give these GitHub repositories a star [SWI-Prolog](https://github.com/SWI-Prolog/swipl-devel), [Ciao](https://github.com/ciao-lang/ciao) and [SWISH](https://github.com/SWI-Prolog/swish)

[Star history](https://star-history.com/#SWI-Prolog/swipl-devel&SWI-Prolog/swish&ciao-lang/ciao&Date)

# Discourse Linkify words

Discourse has a plug-in (theme component) that searches post for specific text and automatically converts the text to a link (linkify).

These words with a # prefix were added for this topic

- [#changelog](https://www.swi-prolog.org/ChangeLog?branch=development)
- [#devel\_commits](https://github.com/SWI-Prolog/swipl-devel/commits/master)
- [#wasm\_demo](https://wasm.swi-prolog.org/wasm/shell)
- [#wasm\_wiki](https://swi-prolog.discourse.group/t/swi-prolog-in-the-browser-using-wasm/5650)

#changelog - A web page to view SWI-Prolog change log(s). There is an option to select a range of versions and select either the development or stable release.

#devel\_commits -To understand the changes to the code, review the GitHub commits for SWI-Prolog development repository. To make it easy to identify the link when writing a post noting the GitHub SWI-Prolog development repository commits just add `#devel_commits`,  
Note: That while not always most of the commits related to WASM start with the name `WASM`.

#wasm\_demo - For those that follow along with the post actively, knowing were to find the demo site is easy. For those dropping in the middle via a Google search some years in the future it can become a needle in a haystack search for the site. To make it easy to identify the link when writing a post noting the demo site just add `#wasm_demo`,

#wasm\_wiki - While the [discussion](https://swi-prolog.discourse.group/t/wiki-discussion-swi-prolog-in-the-browser-using-wasm/5651) thread gets all activity the post that has the crucial facts is not always identified in the post that discuss them. To make it easy to identify the link when writing a post noting this wiki page just add `#wasm_wiki`
