Some sample webassembly code JS<>WASM

I’ve been playing around with Oxygene and webassembly…

Attached is some sample Oxygene WASM code that talks to the DOM and has JS calling WASM, and WASM calling JS.

I have a Delphi background and no real WASM/JS experience so this is properly not the right way to do things so interested in any feedback. No real doco or sample code as flying a bit blind.

WASM calling JS
By adding a JS function I can call it from Oxygene as per the code below:

  // Call JS functions from WASM
  WebAssembly.Global.jslog("test");

on the JS side…

    function jslog(something) {
      console.log('jslog... ' + something);
    }

JS calling WASM
I found that in the html/js code if I made the var ‘program’ a global I could call JS functions from WASM. In Delphi land globals are normally avoided so not sure if this is the right approach ?

On the JS side…

    function jsadd( a, b) {
      console.log('jsadd... ' +  a + b );
      window.program.WasmLog('jsadd - window.program.WasmLog');
      program.WasmLog('jsadd - program.WasmLog');
    }

this calls a WASM method WasmLog

//WASM method that gets call from JS
method WasmLog( s : String );
begin
  Console.Write( 'wasmlog... ' + s );
end;

Interesting bit about the above is jsadd can be called from WASM… so WASM calling JS calling WASM!!

  WebAssembly.Global.jsadd( 2, 3 );  //JS does not think these are integers

Talking to the DOM
The HTML has a DIV called stage

I can get this element from WASM and add a canvas to the DOM as per below:

  var stage := WebAssembly.GetElementById('stage');
  if stage = nil then begin
    writeLn('Element by ID test is null!');
    exit;
  end;

  var canvas := WebAssembly.CreateElement('canvas');
  canvas.id := "TheCanvas";
  canvas.width   := 800;
  canvas.height  := 800;
  canvas.onclick := new WebAssemblyDelegate( (a) -> CanvasOnClick(a) );
  canvas.style := "position: absolute; z-index: 1; background: WhiteSmoke; border: 1px solid; border-color: silver";

  stage.appendChild(canvas);

DOM events
Getting DOM events back to WASM was… interesting. The following hooks up the event to an Oxygene method called WindowInKeyDown

  var w := WebAssembly.GetWindowObject;
  w.onkeydown := new WebAssemblyDelegate( (a) -> WindowOnKeyDown(a) );

and the method

method WindowOnKeyDown( e: EcmaScriptObject );
begin
  var lObject := new EcmaScriptObject( WebAssemblyCalls.GetArray(e.Handle, 0) );
  var lKey := Double( lObject['keyCode'] );
  
  var text := 'JS DOM Window keydown event ' + 'key:' + lKey.ToString;
  
  CanvasText( text );
  Console.Write( text );
end;

The method gets passed a EcmaScriptObject but for some reason I don’t fully understand I have to create another EcmaScriptObject and pass a handle to get to the actual data (eg keycode). I stole this from the sample wasm VCL code which is about all the doco I could find.

Timers
WASM and/or Elements RTL has no concept of a TTimer but JS has this functionality so this was fairly easy to work out once WASM and JS could talk to each other.
I did this with a JS global var ‘thetimer’ for the timer and three JS functions:

    function timerstart( a ) {
      thetimer = setInterval(timertick, a);
      ticks = 0;
      console.log('timerstart: ' + a );
    }
    
    function timerstop() {
      window.clearInterval(thetimer);
      console.log('timerstop' );
    }
    
    function timertick() {
      ticks = ticks + 1;
      program.WasmOnTimer( ticks );
      //console.log('tick' );
    }

timerstart gets called form WASM to start the timer and calls timertick
timertick calls a WASM method when fired
timerstop gets called from WASM and stops the timer (funny that)

I had a lot of fun working this all out. Not really sure where this is heading but Oxygene/wasm sure is entertaining!

Feedback welcome :slight_smile:

Canvas.zip (730.4 KB)

3 Likes