Back to Home

Rust WASM: Porting CLI to Browser

The article describes porting a console application on Rust to WebAssembly with creating a web interface in terminal style. Breakdown of function export, wasm-pack build, JS integration, and automated deploy via GitLab Pages. The approach suits lightweight utilities without system dependencies.

How to Build Rust CLI into WASM and Run in Browser
Advertisement 728x90

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:

Google AdInline article slot
  • 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:

Google AdInline article slot
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:

Google AdInline article slot
  • ./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

Advertisement 728x90

Read Next