tagless

WebAssembly

If you want to use a systems programming language like Rust or C++, you can compile it to WebAssembly (WASM). Your WASM code can then drive the DOM or canvas directly. You only need the allowed shell tags (<html>, <head>, <body>, <meta>, <script>, <style>, and <canvas>) to load the WebAssembly, keeping the rest of your UI out of literal HTML.

Here is a step-by-step guide to doing this with Rust and web-sys.

Step 1: Rust Setup

First, you'll need a Rust function that runs when the WASM module loads. We use wasm_bindgen to communicate between Rust and JavaScript.

use wasm_bindgen::prelude::*;
use web_sys::{window, HtmlCanvasElement, CanvasRenderingContext2d};

// This attribute tells wasm-pack to run this function immediately upon loading
#[wasm_bindgen(start)]
pub fn main() -> Result<(), JsValue> {
    // 1. Get the global window and document objects
    let document = window().unwrap().document().unwrap();
    
    // 2. Create a canvas element dynamically via Rust!
    // We cast it to HtmlCanvasElement so we can access canvas-specific methods.
    let canvas = document
        .create_element("canvas")?
        .dyn_into::<HtmlCanvasElement>()?;
        
    // 3. Set the dimensions of the canvas
    canvas.set_width(800);
    canvas.set_height(600);
    
    // 4. Append it to the document body
    document.body().unwrap().append_child(&canvas)?;

    // 5. Get the 2D rendering context
    let ctx = canvas
        .get_context("2d")?
        .unwrap()
        .dyn_into::<CanvasRenderingContext2d>()?;

    // 6. Draw a pink rectangle on the canvas, entirely from Rust!
    ctx.set_fill_style_str("#e94560");
    ctx.fill_rect(100.0, 100.0, 300.0, 200.0);
    
    Ok(())
}

Step 2: Build the WebAssembly

Once your Rust code is ready, you need to compile it to WebAssembly targeting the web.

Run this command in your terminal:

wasm-pack build --target web

This generates a .wasm file and a JavaScript wrapper that makes it easy to load in the browser.

Step 3: Load the WASM from HTML

Finally, in your tagless HTML shell, you use an allowed <script> tag to import the generated JS module and initialize it.

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <!-- Load the JS wrapper generated by wasm-pack -->
    <script type="module">
      import init from './pkg/your_project_name.js';
      
      // Initialize the WASM module, which automatically calls your #[wasm_bindgen(start)] function!
      init();
    </script>
  </body>
</html>

Useful references: