Pascal WebAssembly + pas2js

9 April 2022

WebAssembly (abbreviated Wasm) is "a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications." Currently, all major web browsers support Wasm. Standalone and embeddable Wasm runtimes also exist that allow running Wasm programs outside of the web browser.

So far, Wasm seems be have been of more interest among the AOT compile-to-native programming languages. Free Pascal, which works across a large number of processor architectures, operating systems, and embedded platforms, also support Wasm compilation targets; see this wiki page on setting it up. Additionally, Free Pascal also comes with a Pascal-to-Javascript transpiler known as pas2js.

I've put up a reworked Free Pascal Wasm demo of Conway's Game of Life. The game play was implemented by Dmitry Boyarintsev, in Pascal, and compiled to Wasm. The user interface is implemented by me, in Pascal, and transpiled to Javascript. For good measure, I've included a simple web server, also implemented in Pascal, that serves the Wasm, Javascript and other static content.

So, to build all Pascal programs in the demo from source, three Free Pascal compilation targets are used:

  • the native compiler, for the web server,
  • pas2js, for the user interface,
  • the Wasm compiler, for the game play.

Here's a screenshot of the user interface running on my desktop web browser in mobile-responsive mode:

pas2js Conway Game of Life

fpwebview Embedded Web Server

17 February 2022

I've implemented an embedded localhost web server demo for fpwebview. The demo runs on Linux macOS, and Windows 10. Here's a Linux screenshot:

fpwebview Embedded Server Demo

The program consists of a multi-threaded web server listening on localhost:8000, and the web browser widget showing the content, styled by CSS, all loaded from said web server.

Because the embedded web server is listening on localhost:8000, you could visit it with a regular web browser running on the same machine. However, in the demo program, the buttons work by using webview APIs to perform what is effectively in-process RPC between Javascript and Pascal. From a regular web browser, clicking those buttons will have no effect, since the browser's Javascript runtime has nothing to do with the Pascal procedures implementing the buttons' functionalities.

For serious usage beyond a demo, additional security measures are possible to ensure that the embedded web server serves only the web browser widget and no other client.

fpwebview Bidirectional Javascript-Pascal Calling

16 February 2022

I've implemented bidirectional Javascript-Pascal calling for fpwebview. Here's the demo program:

program js_bidir;

{$linklib libwebview}

{$mode objfpc}{$H+}

uses
  {$ifdef unix}cthreads,{$endif}
  math,
  webview;

var
  w: PWebView;
  html: String;

procedure SayHello(const seq: PAnsiChar; const req: PAnsiChar; arg: Pointer); cdecl;
var
  s: String;
begin
  s := 'var p = document.createElement("p")' + LineEnding +
    'p.innerHTML = "Yo again!"' + LineEnding +
    'document.body.appendChild(p)';
  webview_eval(w, PAnsiChar(s));
  webview_return(w, seq, WebView_Return_Ok, '{result: "<p>Yo!</p>"}');
end;

begin
  { Set math masks. libwebview throws at least one of these from somewhere deep inside. }
  SetExceptionMask([exInvalidOp, exDenormalized, exZeroDivide, exOverflow, exUnderflow, exPrecision]);

  html := 'data:text/html,<html>' + LineEnding +
    '<head></head>' + LineEnding +
    '<body><button onClick="SayHello()">Say Hello</button>' + LineEnding +
    '<div id="greeting"></div>' + LineEnding +
    '<script>var SayHello = function() {' + LineEnding +
    '  HostSayHello().then((x) => document.getElementById("greeting").innerHTML = x.result)' + LineEnding +
    '}</script>' + LineEnding +
    '</body>' + LineEnding +
    '</html>';

  w := webview_create(WebView_DevTools, nil);
  webview_set_size(w, 700, 200, WebView_Hint_None);
  webview_set_title(w, PAnsiChar('WebView - Pascal Javascript Bidirectional'));
  webview_bind(w, PAnsiChar('HostSayHello'), @SayHello, nil);
  webview_navigate(w, PAnsiChar(html));
  webview_run(w);
  webview_destroy(w);
end.

In the program, the Pascal procedure SayHello is made accessible to Javascript as HostSayHello. When Javascript calls HostSayHello, the Pascal code in turns invokes Javascript to update the DOM, then returns a response to the Javascript caller.

Next up, an embedded web server to serve HTML, CSS and other content including Javascript source.

fpwebview - webview in Pascal

14 February 2022

Webview is a "tiny cross-platform webview library for C/C++/Golang to build modern cross-platform GUIs." Webview uses Cocoa/WebKit on macOS, gtk-webkit2 on Linux, and Edge on Windows 10.

There exist various language bindings for Webview, as listed on Webview's Github page. I've just published my Free Pascal binding here.

Here's a simple Pascal program that uses Webview to implement a web browser.

program browser_cli;

{$linklib libwebview}

uses
  {$ifdef unix}cthreads,{$endif}
  math,
  webview;

var
  w: PWebView;

begin
  { Set math masks. libwebview throws at least one of these from somewhere deep inside. }
  SetExceptionMask([exInvalidOp, exDenormalized, exZeroDivide, exOverflow, exUnderflow, exPrecision]);

  writeln('Hello, webview, from Pascal!');
  w := webview_create(WebView_DevTools, nil);
  webview_set_size(w, 1024, 768, WebView_Hint_None);
  webview_set_title(w, PAnsiChar('WebView Free Pascal'));
  webview_navigate(w, PAnsiChar('https://www.freepascal.org/'));
  webview_run(w);
  webview_destroy(w);
  writeln('Goodbye, webview.');
end.

Coincidentally, Toma┼ż Turk recently announced Pharo-WebView.