Skip to content
Draft

WebAssembly

Draft compiles to WebAssembly (WASM) for portable, sandboxed execution.

WebAssembly allows Draft code to run in browsers, edge runtimes, and any WASM runtime.

Terminal window
draft build main.draft --target wasm -o output.wasm
Terminal window
draft build main.draft --target wasm --release -o output.wasm

For server-side WASM with system access:

Terminal window
draft build main.draft --target wasi -o output.wasm
const module = await WebAssembly.instantiateStreaming(fetch('output.wasm'));
const { greet } = module.instance.exports;
console.log(greet());
<!DOCTYPE html>
<html>
<head>
<title>Draft WASM Demo</title>
</head>
<body>
<script type="module">
import { Draft } from './draft.js';
const app = await Draft.load('output.wasm');
app.main();
</script>
</body>
</html>
@// lib.draft
pub action add(@a: Int, @b: Int) -> Int
return @a + @b
fin action.
pub action greet(@name: String) -> String
return "Hello, " + @name
fin action.

Compile as a library:

Terminal window
draft build lib.draft --target wasm --lib -o lib.wasm

Import JavaScript functions into Draft:

@// Import external functions
extern action log(@message: String)
extern action now() -> Int
action main()
@log("Hello from Draft!")
@timestamp = @now()
@log("Timestamp: " + string(@timestamp))
fin action.

JavaScript side:

const imports = {
env: {
log: (ptr, len) => {
const message = getString(ptr, len);
console.log(message);
},
now: () => Date.now()
}
};
const module = await WebAssembly.instantiate(wasm, imports);

WASM uses linear memory. Draft manages this automatically:

@// Allocate memory
@ptr = @wasm#malloc(1024)
@// Free memory
@wasm#free(@ptr)

WASI provides file system and network access:

import fs
action main()
@content = @fs#read_file("/input.txt")
@fs#write_file("/output.txt", @content)
fin action.

Run with a WASI runtime:

Terminal window
wasmtime output.wasm
wasmtime output.wasm --dir=.

Deploy to edge platforms:

Terminal window
draft build main.draft --target wasm --edge -o worker.wasm
import http
action handle_request(@request: Request) -> Response
return @http#Response::text("Hello from the edge!")
fin action.
Terminal window
draft build main.draft --target wasm -o module.wasm
Terminal window
draft build main.draft --target wasm --edge -o compute.wasm

WASM performance characteristics:

Operation Native WASM Slowdown
Integer math 1x 1.0-1.2x ~10%
Float math 1x 1.0-1.1x ~5%
Memory access 1x 1.0-1.5x ~20%
Function calls 1x 1.0-1.3x ~15%
Terminal window
draft build main.draft --target wasm --source-map -o output.wasm
Terminal window
draft build main.draft --target wasm --debug -o output.wasm
pub action process_string(@input: String) -> String
return @input#upper()
fin action.
pub action sum_array(@data: Array<Int>) -> Int
mut @total = 0
for @value in @data
@total = @total + @value
fin for.
return @total
fin action.
record Point
x: Float
y: Float
fin record.
pub action distance(@a: Point, @b: Point) -> Float
@dx = @a#x - @b#x
@dy = @a#y - @b#y
return @math#sqrt(@dx * @dx + @dy * @dy)
fin action.

Compile WASM modules in the browser:

const response = await fetch('source.draft');
const source = await response.text();
const wasm = await Draft.compile(source, { target: 'wasm' });
const module = await WebAssembly.instantiate(wasm);
Terminal window
draft build main.draft --target wasm --minify -o output.wasm
Terminal window
draft build main.draft --target wasm --compress -o output.wasm.gz
Build Size
Debug ~500 KB
Release ~50 KB
Minified ~30 KB
Compressed ~10 KB
@test
action test_add()
@result = add(2, 3)
assert @result == 5
fin action.
@test
action test_greet()
@result = greet("World")
assert @result == "Hello, World"
fin action.

Run tests in WASM:

Terminal window
draft test --target wasm