Painting on HTML5 Canvas with Rust WASM

Set a baseline using a one-page App: HTML/CSS/JS

Before starting my Rust project I wanted to set a baseline for how difficult the project should be to program. To set a baseline I created the whole program as a one-page app using only html, css, and javascript. The theme of the work was a simple Paint app based on the html5 canvas element. There are quirks and a few browser shims, but otherwise the work only took maybe an hour to complete. The mobile experience is not perfect and the cursor lag is palpable, however for just one hour of work not knowing what I was doing beforehand I have to admit that I am fairly pleased with the results.

<div class="col-md-1">
  <div onclick="ctx.fillStyle='#000000';" class="swatch" style="background-color:#000000;"></div>
  <div onclick="ctx.fillStyle='#779ecb';" class="swatch" style="background-color:#779ecb;"></div>
  <div onclick="ctx.fillStyle='#c23b22';" class="swatch" style="background-color:#c23b22;"></div>
  <div onclick="ctx.fillStyle='#77dd77';" class="swatch" style="background-color:#77dd77;"></div>
  <div onclick="ctx.fillStyle='#ffff00';" class="swatch" style="background-color:#FFFF00;"></div>
  <div onclick="ctx.fillStyle='#00ffff';" class="swatch" style="background-color:#00FFFF;"></div>
  <div onclick="ctx.fillStyle='#ff00ff';" class="swatch" style="background-color:#FF00FF;"></div>
<div class="col-md-11">
  <h1>Click/Tap to Paint</h1>
  <canvas id="canvas" width="800" height="600">
    Your browser does not support the canvas tag.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#EEEEEE";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#000000";
var mousedown = false;
function draw_circle(x,y) {
canvas.addEventListener("mousedown", e => {
  mousedown = true;
  var x = e.pageX;
  var y = e.pageY;
  var bounds = canvas.getBoundingClientRect();
  x -= (bounds.left + window.scrollX);
  y -= ( + window.scrollY);
canvas.addEventListener("mouseup", e => {
  mousedown = false;
canvas.addEventListener("mousemove", e => {
  var x = e.pageX;
  var y = e.pageY;
  var bounds = canvas.getBoundingClientRect();
  x -= (bounds.left + window.scrollX);
  y -= ( + window.scrollY);
  if(mousedown) { draw_circle(x,y); }

Implementing Paint on a WASM backed Canvas

The rustwasm team wrote an entire mdbook about how to draw directly to a canvas from WASM. In their example they implement the Conway's Game of Life in a webpage canvas. The method is straightforward, however the actual drawing to canvas must go through Javascript. They write in length about the penalty that this sort of data transfer incurs, however they do it nonetheless. This makes me think that at the time of writing that article there may not have been a better method. I am writing this article along with my own research, so I am not entirely deterred from trying to implement the draw method entirely in WASM/Rust. I hope that this is possible.

Rust/WASM Utilities

There is a Rust crate called that is built with a WASM target in mind. This crate is also available as an npm package. Their WASM example is very responsive, so I assume that there is an efficient back-and-forth from the canvas element. EDIT: Apparently the preferred method of doing this update is to expose a fixed region of linear memory (WASM memory region) and let a javascript process read off of it. This is great performance if it were stabilized and supported by browsers, however Node.js is the current standard target platform. Browsers are still a fairly unstable target for WASM.

Mothballed way of drawing to canvas

There is an old WASM example in Rust for drawing to canvas. This is very raw and the page is archived, probably for that reason. A more recent example of drawing to canvas is provided by the wasm-bindgen project.