While JavaScript has a garbage-collected heap, WebAssembly has a linear memory space. Nevertheless using a JavaScript ArrayBuffer, we can read and write to WebAssembly’s memory space.
Instructor: [00:00] Here, we start with a little refactoring for easier use of importing modules later in the lesson. We'll introduce an app.js file, and make sure our index.js file only takes care of loading the app module dynamically.
[00:18] We did this change to make sure we can use the static import statements inside app.js. Now, we can focus on the actual goal, accessing memory of a WebAssembly module from Javascript.
[00:32] Each WebAssembly module has a linear memory. From Javascript, we can freely read and write to it. To do so, we can directly import a variable memory from the WASM module. Let's log out the memory. It contains a buffer matching the linear memory of the WebAssembly module.
[01:03] How can we leverage this now? Let's write a small image library in Rust. We define a struct, color, containing red, green, and blue and unsigned integers. Next up, we implement the image. Our image contains pixel, which is a vector of colors.
[01:23] We create the function new, and inside it, add two different pixels, one pure red, with the red value 255, green and blue being 0The second one is a dark gray, with a red part of 60, green 70, and blue 90. Then we store the two pixels in the vector and return the image. Finally, we add a function pixel_pointer, returning the pointer to the pixel vector.
[01:56] Let's move on with Javascript. There, we instantiate a new image and retrieve our pixel pointer. We know all our pixels are unsigned integers with eight bits. In our pixels vector, we store the two pixels. This means we stored six color values.
[02:20] We leverage this information by only accessing the first six values out of our buffer, and store them in a typed array representing eight-bit unsigned integers. Here, the amazing part. These six values match exactly the color parts of our two pixels.
[02:39] This means we can extract the color of the two pixels directly from the memory, without any serialization or deserialization overhead. This is super useful in case serialization and deserialization is your performance bottleneck.
[02:55] In this example, we leverage this functionality to draw pixels to a canvas. Therefore, we create the helper function, drawPixel, accepting an X and Y position, and an array for the colors, red, green, and blue. Using context field style, we can convert the numbers to a hex code and draw the pixel.
[03:33] Be aware, instead of drawing just one pixel, we're drawing a larger rectangle in this case, so we can actually see the result. Finally, we can create our canvas, and use our drawPixel function to actually draw the pixels. We slice the array to retrieve the correct pixel colors.
[03:54] Voila. This works like a charm. We access the linear memory of our WebAssembly module directly from Javascript. While we won't do it in this lesson, you can imagine that this can be used to draw a canvas from the game loop managed from our Rust code.
[04:11] Be aware that WebAssembly's memory can be accessed from Javascript, but not the other way around. WebAssembly doesn't have direct access to Javascript values. That said, we can work around this by storing a Javascript value inside the WebAssembly memory, and then use it inside our Rust code.