WebAssembly

kerfscript.wasm runs KerfScript from any language that can host a WebAssembly module: Python, Rust, Go, C#, Java, Ruby and others. It is a WASI (preview 1) module built from the same source as the JavaScript package, so it makes the same SVG and DXF files, byte for byte.

For JavaScript applications, use the JavaScript API instead: it is smaller and faster.

Getting the module

Download kerfscript-<version>.wasm from the releases page and save it as kerfscript.wasm, or get kerfscript-wasm-<version>.zip, which has the module with this guide and its licences. You can also build it from the repository with Javy:

npm run build
npm run wasm        # writes dist/kerfscript.wasm

Set JAVY to the javy binary if it is not on the PATH.

How it works

The module reads one JSON request from standard input, writes one JSON response to standard output and exits. Start a fresh instance for each request. From a shell, with Wasmtime:

echo '{"source": "rect([80, 50]);"}' | wasmtime run kerfscript.wasm

Request

Field Value
command "render" (default), "params", "version" or "licenses"
source The program, as a string
params Parameter values keyed by name
files Libraries for include and use, keyed by path: {"lib/tab.kerf": "..."}
fonts Font files keyed by name, base64 encoded. Atkinson is built in
defaultFont Name to use when text() does not specify a font
kerf Kerf override for every part, in millimetres
layout "pack" (default) or "none"
sheet Sheet size [width, height] in millimetres
gap Space between packed parts
margin Space between the parts and the sheet edge
guides true to include guide layer geometry

These match the render() options. A path in files can leave out .kerf, as in the program.

Response

Every response has ok. When it is false, error holds the message and echoes holds what the program echoed before it stopped. The module never exits with an error code for a bad program.

  • render: svg (all sheets, for preview), width, height, parts, assembly, warnings, echoes and sheets. assembly is the same as from render(): one entry per copy of a part, with its outline, thickness and placement. Each sheet has width, height, material, thickness, svg and dxf; write one file per sheet to cut.
  • params: parameters, as from parameters().
  • version: version (package) and language.
  • licenses: files, the licence texts of the module and of everything built into it, keyed by file name: LICENSE, COMMERCIAL-LICENSE.md, THIRD-PARTY-NOTICES.md, THIRD-PARTY-LICENSES.md (the JavaScript engine and its Rust crates) and OFL.txt. The release ZIP has the same files.

Python

With the wasmtime package:

import json, os, tempfile
from wasmtime import Engine, Linker, Module, Store, WasiConfig

engine = Engine()
module = Module.from_file(engine, "kerfscript.wasm")
linker = Linker(engine)
linker.define_wasi()

def kerf(request):
    with tempfile.TemporaryDirectory() as d:
        inp, out = os.path.join(d, "in.json"), os.path.join(d, "out.json")
        with open(inp, "w", encoding="utf-8") as f:
            json.dump(request, f)
        wasi = WasiConfig()
        wasi.stdin_file = inp
        wasi.stdout_file = out
        store = Store(engine)
        store.set_wasi(wasi)
        linker.instantiate(store, module).exports(store)["_start"](store)
        with open(out, encoding="utf-8") as f:
            return json.load(f)

result = kerf({"source": "w = 80; rect([w, 50]);", "params": {"w": 120}})
for i, sheet in enumerate(result["sheets"]):
    with open(f"sheet-{i + 1}.svg", "w", encoding="utf-8", newline="") as f:
        f.write(sheet["svg"])

Compile the module once and reuse it; each request only needs a new Store.

Node.js

The JavaScript package is the better fit in Node.js, but the module also runs under node:wasi, which is how the test suite checks it:

import { closeSync, openSync, readFileSync, writeFileSync } from 'node:fs';
import { WASI } from 'node:wasi';

const module = await WebAssembly.compile(readFileSync('kerfscript.wasm'));
writeFileSync('in.json', JSON.stringify({ source: 'rect([80, 50]);' }));
const stdin = openSync('in.json', 'r');
const stdout = openSync('out.json', 'w');
const wasi = new WASI({ version: 'preview1', stdin, stdout, returnOnExit: true });
wasi.start(await WebAssembly.instantiate(module, wasi.getImportObject()));
closeSync(stdin);
closeSync(stdout);
console.log(JSON.parse(readFileSync('out.json', 'utf8')).svg);

Performance

The module carries its own JavaScript engine (QuickJS), so it is about 2 MB and renders roughly three to five times slower than Node.js. The example designs take well under a second.