WebShumway

Ctrl-Enter consults · Ctrl-S saves · Tab completes

Answers

loading the engine…

Are you sure?

Libraries

Available to every workspace as :- use_module(library(Name)). A library from another system loads under its own dialect.

Import from a URL

A GitHub directory — open it on github.com and copy the address.

Import library

Predicate reference

WebShumway

Prolog in the browser — the Shumway engine compiled to WebAssembly.

WebShumway brings the Shumway Prolog engine to your browser. The engine and your files live in this tab and never leave it. There is no server behind this page.

Making a query

Type a goal in the ?- box and press Enter. When an answer appears the engine is holding the next one back: press ; (or space) for it, . (or Enter, or Escape) to stop. Stop abandons a search that is taking too long — the page stays usable while one runs.

Writing a program

Write it on the left and press Consult (Ctrl+Enter) to load it. Loading REPLACES what the text defines, so pressing it twice does not define anything twice. Tab completes a predicate name; Ctrl+S saves the file.

Files and workspaces

A workspace is a folder of files and the engine's working directory, so :- use_module('other.pl'). and open/4 resolve inside it. A file you edit is reloaded by the file that imports it when you consult again. Switching workspace starts a fresh engine, because a workspace is a separate program.

Files live in this browser's private storage and survive a reload. Download saves one to your computer, Export saves the whole workspace as a zip.

Libraries that come with the engine

Constraints, coroutining and reified conditions (library(reif)) are opt-in, from the program itself:

:- use_module(library(clpfd)).

puzzle([S,E,N,D,M,O,R,Y]) :-
    Vars = [S,E,N,D,M,O,R,Y], Vars ins 0..9,
    all_different(Vars), S #\= 0, M #\= 0,
    1000*S + 100*E + 10*N + D + 1000*M + 100*O + 10*R + E
      #= 10000*M + 1000*O + 100*N + 10*E + Y,
    label(Vars).

The Predicate reference (the book icon) lists everything the engine provides, with the mode of each argument.

Libraries from elsewhere

Libraries… brings in someone else's. Import a folder from your computer, or fetch one straight from a GitHub directory — Scryer's, SWI's and Trealla's are offered, so you do not have to install a Prolog system to borrow its library. What you import is a collection: Scryer's lib/ is one folder and sixty libraries, and every x.pl in it becomes library(x).

Each collection carries a dialect. A library written for Scryer loads under Scryer's reading of Prolog and one written for SWI under SWI's, which is what lets both systems' versions of the same library sit here at once.

A library loads faster once compiled — several times faster, because the compiling stops happening on every consult. That runs by itself in the background after an import, doing the ones your programs import first, and each becomes fast the moment its own build lands. It pauses while you consult and you can stop it at any time; what is already built stays built, across reloads too.

You can read and edit a library's sources from the same dialog. They are sources: what a program resolves is the compiled version beside them, so an edit takes effect when you press rebuild. Libraries are shared by every workspace and do not travel in a workspace's zip or share link.

Sharing

Share puts a file or a whole workspace INSIDE the link, in the part of a URL browsers never send to a server. Opening one never overwrites your files without asking.

Debugging

Debug switches the page into debug mode: the engine restarts compiled for debugging and your program is loaded again — by its file name, so breakpoints work in every file of the workspace, not just the one on screen. Debug again returns to the normal top level.

In debug mode the program runs on the interpreter, so breakpoints can stop it, and the first line of the top level shows Tier-0. Libraries stay compiled; stepping does not go into them.

Breakpoints

Click the strip left of the code to set one; click it again to remove it. A hollow dot is a breakpoint on a line the loaded program does not reach. Right-click the gutter — or the source itself — for the line's menu:

  • Set / remove breakpoint.
  • Enable / disable — kept but greyed out, so a breakpoint you want back later need not be redrawn.
  • Condition — a goal; the breakpoint stops only when it succeeds.
  • Log message — makes it a logpoint: it prints the message and keeps going instead of stopping. {X} holes fill from the frame.
  • Run to cursor — run to this line and stop, once.
  • Set next statement — move where execution will resume to this line, running nothing in between.

Breakpoints are remembered per workspace and survive a reload; a deleted workspace forgets its own.

Stopping and stepping

When a query stops, the tabs under the transcript show where it is and what it holds:

  • Call stack — click a frame to go to it; library and prelude frames collapse into one [library code] row.
  • Locals — the frame's variables, residual constraints included.
  • Watches — goals you add, re-evaluated at every stop.
  • Immediate — see below.

The ⇄ button at the end of a tab bar sends its current view to a second dock under the program panel, so two views — say the call stack and the locals — stay visible at once. An emptied dock gives its space back. Drag a dock's top edge to make it taller or shorter — as the divider between the two panels resizes those — and a double-click gives a seam its default back. All of it is remembered.

Step with the buttons above the answers, or the usual keys:

  • F5 continue
  • F10 step over · F11 step into · Shift+F11 step out
  • F9 toggle a breakpoint on the caret's line · Ctrl+F9 enable/disable it
  • Ctrl+F10 run to the caret's line · Ctrl+Shift+F10 set the next statement there

Break pauses a running query at its next goal — it lands in your own program's code, so a goal made only of builtins offers it nowhere to stop (Stop still abandons the query there).

Calling debugger_break from your program stops in debug mode exactly as a breakpoint on that line would — a breakpoint you write into the code, useful for a spot a click is awkward to place. Outside debug mode it does nothing and succeeds, so it can be left in.

The Immediate window

While stopped, the ?- box (and the Immediate tab) evaluate a goal against the selected frame — the variables you name are that frame's:

  • A goal runs on a copy; ; asks for its next solution.
  • A goal prefixed with ! runs on the real frame, so a binding or a posted constraint sticks (append , fail for a dry run).

What is different here

Programs run on two tiers: the bytecode interpreter (Tier-0) and a just-in-time compiler (Tier-1) that translates them to WebAssembly. By default the whole program is compiled after each consult. jit_compile(off) keeps everything on the interpreter, jit_compile(16) compiles a predicate after it has been called 16 times, and jit_compile(all) restores the default. The first line of the top level shows which tier is in use. Both tiers give the same results; only the speed changes.

Shumway — a Prolog compiler and interpreter for .NET. MIT licensed. Copyright © 2026 Gustavo Brown. github.com/gusbro/Shumway

Share a link

The link carries the code itself, in the part of the URL browsers never send to a server.

Breakpoint

Name