Inline WASM

A wasm { } block inside a TJS function is compiled to WebAssembly when the file is transpiled, and the bytes are embedded in the emitted JavaScript. This page covers how a block gets its inputs, which numbers it works with, and what it may contain. It assumes you know roughly what WebAssembly is; if you have never used it from TJS, the WASM Quick Start is a shorter walk-through.

When to use a block

When a profiler says a numeric inner loop is hot. Most code is fast enough written plainly, and a call into WASM has its own cost. WASM pays off on tight loops over typed arrays, and most of all with SIMD, which the next chapter covers.

Using WebAssembly from plain JavaScript means writing WAT (WebAssembly's text format) or compiling C or Rust, shipping a separate .wasm file, instantiating it asynchronously, and copying data between JavaScript and WASM memory. A wasm { } block does all of that for you. There is no .wasm file and no build step beyond transpiling the file, and inline blocks instantiate synchronously when the file loads, so the first call already runs in WASM.

The shape of a block

function square(! x: 0.0):! 0.0 {
  return wasm {
    return x * x
  }
}

The block returns a value with return, and the function returns that with return wasm { … }. Two markers appear on every function on this page:

Both are explained under "Why ! and :!" below.

An example

// --- Scalar math ---

function addNumbers(! a: 0, b: 0):! 0 {
  return wasm {
    return a + b
  }
}

function factorial(! n: 0):! 0 {
  return wasm {
    let result = 1.0          // f64: an i32 here would overflow at 13!
    for (let i = 2; i <= n; i++) {
      result = result * i
    }
    return result
  }
}

function lerp(! a: 0.0, b: 0.0, t: 0.0):! 0.0 {
  return wasm {
    return a + (b - a) * t
  }
}

// --- Array processing ---

function sumArray(! arr: Float32Array, len: 0):! 0.0 {
  return wasm {
    let sum = 0.0
    for (let i = 0; i < len; i++) {
      sum = sum + arr[i]
    }
    return sum
  }
}

console.log('addNumbers(3, 4):', addNumbers(3, 4))       // 7
console.log('factorial(10):', factorial(10))             // 3628800
console.log('factorial(13):', factorial(13))             // 6227020800
console.log('lerp(0, 100, 0.25):', lerp(0.0, 100.0, 0.25)) // 25
console.log('sumArray([1,2,3,4]):', sumArray(new Float32Array([1, 2, 3, 4]), 4)) // 10

Things to notice:

Numbers inside a block

A WASM block has no JavaScript numbers. It has two numeric types:

The rules:

Why ! and :!

Checking the block against its fallback

fallback { } is ordinary TJS that runs when the runtime cannot run WASM at all: no WebAssembly, as in iOS Lockdown Mode, or a refused instantiation. When that happens the runtime records it once per file in __tjs.records({ source: 'wasm' }), the flight recorder. Write an explicit fallback when the JavaScript version should be different code, for example a library call the block cannot make; otherwise leave it out and the block's body runs as JavaScript.

Setting globalThis.__tjs_wasm_enabled = false forces every block to run its fallback, even though the WASM is loaded. Use it to test that the two paths agree, or to benchmark one against the other.

function mean(! xs: Float32Array, len: 0):! 0.0 {
  return wasm {
    let sum = 0.0
    for (let i = 0; i < len; i++) {
      sum = sum + xs[i]
    }
    return sum / len
  } fallback {
    return xs.reduce((total, x) => total + x, 0) / len
  }
}

const xs = new Float32Array([1, 2, 3, 4, 5])
const inWasm = mean(xs, 5)

globalThis.__tjs_wasm_enabled = false
const inFallback = mean(xs, 5)
delete globalThis.__tjs_wasm_enabled

console.log('in WASM:    ', inWasm)                  // 3
console.log('in fallback:', inFallback)              // 3
console.log('agree:      ', inWasm === inFallback)   // true

Limits

A wasm { } body is a small, typed subset of JavaScript, not arbitrary code. Anything outside it is a compile error that names the construct; it is never silently shipped as JavaScript, because writing wasm { } is a request for WASM.

function wave(! t: 0.0):! 0.0 {
  return wasm {
    return Math.sin(t)  // compile error: Math.sin is not supported in a block
  }
}

DOCS-WASM.md is the full reference.