Wiki Discussion: SWI-Prolog in the browser using WASM

This is a topic to discuss the wiki

SWI-Prolog in the browser using WASM


this happened


Welcome to SWI-Prolog (32 bits, version 8.5.13-33-g3ad3dafc6).
SWI-Prolog WebAssembly ready!

?- assertz(parent(tom,ann)).
?- assertz(parent(mary,ann)).
?- parent(P,ann).
ERROR: char_code/2: Type error: `character_code' expected, found `-1' (an integer)
P = tom

The problem is reproduceable for nondeterministic queries, e.g.


?- between(1,5,N).
ERROR: char_code/2: Type error: `character_code' expected, found `-1' (an integer)
N = 1

Running Google Chrome on Windows 10 if that helps.

Yes, as the wiki says, execution of the query should not try to read. A nondet toplevel queries tries to read the action from the user. This can surely be solved, but I don鈥檛 know how (yet).

After (ref 1) (ref 2)

  • [2022-08-05] Extended JavaScript binding, run the browser shell using yield.

Much nicer. :slightly_smiling_face:


In right panel created parent/2 facts.
Then below that clicked (Re)consult which ran consult('/').

In left panel ran parent(P,ann) which prints first result and prompts for next action: Next or Stop.


May I ask a rather fundamental question: Is it a good idea at all to run applications in a browser? Nowadays browsers are already way too complex, with a correspondingly large surface for attackers.

also the next paragraph on Security

Fair enough

Excellent work @jan and it is really supporting our goal to enable client side reasoning to reach World wide welding machine transforming data into proofs
It was pretty straightforward to do N3 proof generation in Eyebrow but I鈥檓 not sure why the page not always starts with

Welcome to SWI-Prolog (32 bits, version 8.5.15-33-g3ad3dafc6)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.
For online help and background, visit
For built-in help, use ?- help(Topic). or ?- apropos(Word).
SWI-Prolog WebAssembly ready!

and why a reload is needed.
Most probably it is because eyebrow/socrates.html at c070571d980406db5c05d6b166dfe7aa470eb5f2 路 josd/eyebrow 路 GitHub needs a PR :wink:

I don鈥檛 know. So far it seems to start properly each time. Now that you are some versions behind. Current development is on the yield-wasm branch. Testing and feedback welcome. Be aware that there are still a large number of issues and interfaces change quite often. The first challenge is to get the basic toplevel interaction working.

It is clarified and with the help of Jesse Wright fix: remove loading race conditions by jeswr 路 Pull Request #1 路 josd/eyebrow 路 GitHub

Nice and so far so good with the latest yield-wasm branch when applied to and although I am not sure why the latter retursn undefined instead of true.

The foreign yield has a bug that causes the block of 鈥渨ell known terms鈥, among which the well formed semantics (WFS) delay list to be garbage collected :frowning: That is my TODO for this morning 鈥 Thanks for reporting.

I have pushed a new version to the normal master branch and updated SWI-Prolog WebAssembly build test to address this issue. The shell is now a bit more friendly, including the possibility to abort long running queries, handle multiple files and make the programs and command history persistent using the browser鈥檚 localStorage.

Async behavior now uses auto yielding and restarts using setTimeout() That seems to slow down a lot. Hopefully async functions can make this better.

We are getting to a state where some basic evaluation is feasible :slight_smile:


Notes on

[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.

For those use to using the up arrow to go back in the command history.
To use the up arrow to go back in the command history the input box has to be active for it to work.

Say for example you run a query and click on the Next button, then the input box is no longer active so you have to click on the input box then the up arrow will work.


The middle icon seems to be a trash can.

How it works for me. (Don鈥檛 know if this is how it is suppose to work or if it has a bug).

  • If the file was updated since being new or clicking re(consult), if you click the trash can the input since the last re(consult) for the file is deleted.
  • If the file was not updated since being new or clicking re(consult), If you click the trash can the file is left alone and becomes the current file.

If you run a long running query, e.g.


you will now get an abort button while it is running.

Now that we have persistent Prolog files need way to remove or delete the files since they are stored in the browsers cache.

Tried this for Chorme but it does not work.

Press F12 to open developer tools.
Go back to Chrome, right click reload button, select hard reset and clear cache

Seems that file names with _ in them are not valid.

The hello world example from SWISH behaves badly.

hello_world :-
    writeln('Hello World!'),

Yes, works now (and beats Ciao Playground, since it has abort):

?- time(fib(25,X)).
ERROR: Execution Aborted
% 94,176 inferences, 1.608 CPU in 1.608 seconds (100% CPU, 58567 Lips)
?- time(fib(25,X)).
% 489,517 inferences, 11.648 CPU in 11.648 seconds (100% CPU, 42026 Lips)
X = 121393.

Cool! Do you see some overhead by auto-yielding?
Whats the auto-yielding rate? (in Dogelog I try to reach 60Hz)
In normal SWI-Prolog its much much faster:

?- time(fib(25,X)).
% 485,570 inferences, 0.031 CPU in 0.036 seconds (87% CPU, 15538240 Lips)
X = 121393.

Maybe you do over auto-yielding? Or whats the issue? Or maybe
memory shortage in WASM? 42026 LIPS is very poor, could be
result of an expensive auto-yielding or some other issue.

For reproduction of the result, I used this code:

fib(0,1) :- !.
fib(1,1) :- !.
fib(N,X) :- N>1, M is N-1, fib(M,Y), L is M-1, fib(L,Z), X is Y+Z.

Ciao Prolog on WASM does fib(25,X) with that code in 20-30ms.

Where does the time out comes from?

Note also that the timeout in Ciao is a completely arbitrary number (mostly for students). It is configurable from the .html code but the next version will allow changing it from the Prolog side too.

I really do not understand 鈥(and beats Ciao Playground, since it has abort)鈥. Ciao Prolog WASM does fib(25,X) in 20-30ms. in my machine and x5 slower in SWI Prolog WASM. It is possible that Ciao WASM is faster than SWI native in those artificial benchmarks (which of course, means absolutely nothing since real applications are much more complex than fib/2鈥).

@jan It may be good to design a shared set of benchmarks to measure different aspects and features of our engines.

Ohh I see. It would be less confusing saying that performance of Ciao WASM with automatic yield is unknown yet (since in the public version yield is never executed). I move the Ciao part of the discussion at (playground) Aborts cause state loss. 路 Issue #56 路 ciao-lang/ciao 路 GitHub

This would be your duty to formulate it like that, if this formulation
suits you better, since you posted 鈥淐iao Prolog WASM does fib(25,X)
in 20-30ms.鈥. I only posted 鈥渟ince it (SWIPL) has abort (now)鈥.

Nevermind. Anyway, you now wrote into the ticket:

We鈥檒l try automatic yield but if there is some serious impact
on performance, we will consider also other options (maybe optionally).

What are other options beside auto yield? What comes to mind,
would be maybe AbortController and friends. If there is somewhere
something like a signalling. But the problem is you need to

connect it to the user interface somehow, which would be
possible if you have a Worker. Another option that came into
my mind was subclassing Worker and give it a field

abort_flag. But Workers are extremly isolated in JavaScript,
don鈥檛 know whether such a shared instance variable is possible.
For example if you do postMessage() the message gets copied.

Its very similar to the Prolog messages, except maybe
with a little bit more isolation than Prolog threads.

I have a bench.git repo that runs in SWI, YAP and SICStus. I鈥檓 happy to include Ciao support if you send a PR.

Following @j4n_bur53鈥檚 remarks, I鈥檝e decreased the yield frequency. The low-level is based on inference count, which has the nasty property to vary a lot. At the JavaScript level I now immediately resume if the last time we yielded is less than 20ms ago, bypassing the setTimeout(). Also updated flush() to be really cheap if there is nothing to flush (thanks for noting).

This results in these times for fib(25,X). All are on AMD 3950X, Ubuntu 22.04. statistics/2 doesn鈥檛 seem to work in the Ciao playground (reports 0.0). SWI-Prolog鈥檚 WASM version now reports wall time as CPU time as (AFAIK) there is no way to measure CPU time in JavaScript/WASM. Note that the SWI-Prolog WASM version does not use GMP. Times may get worse if we do :frowning:

System Time
SWI-Prolog native 0.027
SWI-Prolog native, -O 0.013
SWI-Prolog WASM (FF 103) 0.102
SWI-Prolog WASM (FF 103) -O 0.076
SWI-Prolog WASM (node) -O 0.063
Ciao 0.012