Search

intermediate

Using WebAssembly

Compile Draft to WebAssembly, integrate with JavaScript, and build browser-based applications.

Learning Objectives

  • Compile Draft to WASM
  • Load and run WASM in the browser
  • Call Draft functions from JavaScript
  • Pass data between JS and WASM

Prerequisites


Compiling to WASM

Create a Draft library that exports functions:

math.draft
export action add(a, b)
return a + b
fin action.
export action multiply(a, b)
return a * b
fin action.
export action greet(name)
return "Hello, " + name + "!"
fin action.

Compile to WASM:

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

This produces math.wasm and a JavaScript glue file math.js.


Browser Integration

Create an index.html file:

<!DOCTYPE html>
<html>
<head>
<title>Draft + WASM</title>
</head>
<body>
<h1>Draft in the Browser</h1>
<div id="output"></div>
<script type="module">
import { add, multiply, greet } from "./math.js";
const output = document.getElementById("output");
const sum = add(10, 20);
output.innerHTML += `<p>10 + 20 = ${sum}</p>`;
const product = multiply(6, 7);
output.innerHTML += `<p>6 × 7 = ${product}</p>`;
const message = greet("Draft");
output.innerHTML += `<p>${message}</p>`;
</script>
</body>
</html>

Serve the directory with any static file server:

Terminal window
npx serve .

Open http://localhost:3000 in your browser.


JavaScript Interop

Calling JS from Draft

Use the js module to call JavaScript functions:

import js
action main()
@timestamp = js.now()
write("Timestamp: " + timestamp)
fin action.

Passing Callbacks

Pass JavaScript functions to Draft:

main.js
import { processItems } from "./app.js";
function onProgress(percent) {
console.log("Progress: " + percent + "%");
}
processItems(items, onProgress);
app.draft
export action processItems(items, callback)
loop i from 0 to items.length - 1
callback((i / items.length) * 100)
again
fin action.

Complete Example: Todo List in the Browser

todo.draft
import js
export action init_todos()
@stored = js.localStorage_get("todos")
if stored then
return json.decode(stored)
else
return []
fi
fin action.
export action add_todo(todos, title)
@new_todo = {
id: todos.length + 1,
title: title,
done: false
}
todos.push(new_todo)
js.localStorage_set("todos", json.encode(todos))
return todos
fin action.
export action toggle_todo(todos, id)
loop i from 0 to todos.length - 1
if todos[i].id == id then
todos[i].done = not todos[i].done
fi
again
js.localStorage_set("todos", json.encode(todos))
return todos
fin action.
export action delete_todo(todos, id)
loop i from 0 to todos.length - 1
if todos[i].id == id then
todos.remove(i)
fi
again
js.localStorage_set("todos", json.encode(todos))
return todos
fin action.
<!DOCTYPE html>
<html>
<head>
<title>Draft Todo</title>
</head>
<body>
<h1>Draft Todo</h1>
<input type="text" id="taskInput" placeholder="New task">
<button onclick="addTask()">Add</button>
<ul id="taskList"></ul>
<script type="module">
import { init_todos, add_todo, toggle_todo, delete_todo } from "./todo.js";
let todos = init_todos();
render();
async function addTask() {
const input = document.getElementById("taskInput");
const title = input.value.trim();
if (title) {
todos = add_todo(todos, title);
input.value = "";
render();
}
}
async function toggleTask(id) {
todos = toggle_todo(todos, id);
render();
}
function render() {
const list = document.getElementById("taskList");
list.innerHTML = "";
for (const todo of todos) {
const li = document.createElement("li");
li.textContent = (todo.done ? "[x] " : "[ ] ") + todo.title;
li.onclick = () => toggleTask(todo.id);
list.appendChild(li);
}
}
</script>
</body>
</html>

Try It Yourself

  1. Save the Draft code as todo.draft and compile to WASM.
  2. Create index.html with the JavaScript code.
  3. Serve the directory and open it in your browser.
  4. Add, toggle, and delete tasks.

Checkpoint

  1. What flag compiles Draft to WASM?

    • --target wasm
    • --wasm
    • --output wasm
  2. How do you persist data in the browser from Draft?

    • js.localStorage_set(key, value)
    • fs.write("key", value)
    • browser.storage.set(key, value)
  3. What module provides JavaScript interop?

    • wasm
    • js
    • dom
Answers
  1. --target wasm
  2. js.localStorage_set(key, value)
  3. js

Summary

You compiled Draft to WebAssembly and integrated it with JavaScript in the browser. WASM lets you run Draft code anywhere with near-native performance.

Next Steps