Migrating a Rust CLI Tool to WebAssembly with Deployment to GitLab Pages
A console application for generating text predictions has been ported to WebAssembly using Rust. The computational logic is isolated in a WASM module, while the interface is implemented as a static HTML page with a terminal-like user experience. Deployment via GitLab Pages provides instant access to the demo without any installation required.
The project includes a Rust library for the core logic, a frontend built with vanilla JavaScript and CSS, and wasm-pack artifacts. This approach is ideal for lightweight utilities that don't require file system access or platform-specific APIs.
Project Architecture
The structure is divided into three components:
src/lib.rs— Core logic in Rust with exported functions.public/index.html,styles.css,app.js— Terminal-style interface.public/pkg/— Compiled WASM files and JavaScript wrappers.
The lack of operating system dependencies makes the port straightforward: the application accepts commands and returns text.
Exporting Rust to WASM
The main function is exported using #[wasm_bindgen]:
#[wasm_bindgen]
pub fn generate_reading_text() -> String {
// Generate and format the result
}
Build with the command:
wasm-pack build --target no-modules --release --out-dir public/pkg
Result: iching_wasm.js and iching_wasm_bg.wasm. The --target no-modules option simplifies integration without bundlers.
Integrating WASM into the Browser
In app.js, the module is loaded synchronously with base64-embedded WASM to work with file://:
const wasmBytes = base64ToBytes(window.ICHING_WASM_BASE64);
wasm_bindgen.initSync({ module: wasmBytes });
const text = wasm_bindgen.generate_reading_text();
output.textContent = text;
In index.html, the following are included:
./pkg/iching_wasm.js— Runtime../pkg/iching_wasm_bg_base64.js— Base64 WASM.app.js— UI logic.
JavaScript calls WASM, and the result is rendered into the DOM.
Terminal-Style UI
Framework-free interface:
- Container with a border and window title.
- Monospaced font (e.g., JetBrains Mono).
- Dark theme with green text.
<pre>for output.- Input for commands like
./generate_reading.
Visually and behaviorally mimics a console. A button triggers the WASM call.
CI/CD for Build and Deployment
.gitlab-ci.yml automates the process:
image: rust:slim
create-pages:
pages:
publish: public
rules:
- if: '$CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'
before_script:
- rustup target add wasm32-unknown-unknown
- cargo install wasm-pack --locked
script:
- wasm-pack build --target no-modules --release --out-dir public/pkg
- b64="$(base64 public/pkg/iching_wasm_bg.wasm | tr -d '\n')"
- printf 'window.ICHING_WASM_BASE64 = "%s";\n' "$b64" > public/pkg/iching_wasm_bg_base64.js
Steps: install target, build with wasm-pack, encode WASM to base64, publish public/ to Pages.
Advantages of This Approach
- Accessibility: Demo works in any browser without Rust or local installation.
- Security: WASM isolation eliminates risks from native code.
- Simplicity: Static hosting, one CI job.
- Portability: Suitable for CLI tools without system calls.
Key Takeaways
- Logic without file system or API dependencies fits perfectly into WASM—builds in seconds.
- Base64 embedding of WASM solves CORS and
file://issues. - GitLab Pages + wasm-pack CI provides a ready-made pipeline for prototypes.
- Vanilla JavaScript/HTML for UI minimizes overhead.
#[wasm_bindgen]is the standard for exporting Rust to JavaScript.
— Editorial Team
No comments yet.