In the of this , we already learned how to write Wasm modules in AssemblyScript. In this part, we will use this knowledge in a practical scenario: image manipulations with WebAssembly. previous part series We will demonstrate a typical use-case by a simple function for converting an image to . grayscale Albeit the calculation of the gray color is not very demanding, it clearly demonstrates the real-world usage of WebAssembly on the web: . computation-intensive tasks You can find the full discussed source code on . my GitHub Browser Runtime Unlike our previous experiments with AssemblyScript, this time we will run our Wasm module in a browser. You might first recall how to use a , but the code is quite straight-forward: web browser as a Wasm runtime WebAssembly .instantiateStreaming(fetch( ), {}) .then( { ... } 'grayscale.wasm' ( ) => { instance } To make the work you must serve the web page via HTTP(S). Fetch API The second argument of the is an object with Wasm imports. All environment imports for Wasm modules, which were compiled from AssemblyScript, are included in an object: instantiateStreaming env When running in the browser, we must import an error-callback function : abort WebAssembly .instantiateStreaming(fetch( ), { : { : .error( ) } }) 'grayscale.wasm' env abort ( ) => _msg, _file, line, column console `Error at : ` ${line} ${column} In Node.js those are already provided by the AssemblyScript loader out of the box. Canvas To show the image and its transformation in a browser, we will use the HTML : canvas element < = = = > canvas id "canvas" width "500" height "500" </ > canvas And its 2D context: canvas = .getElementById( ); ctx = canvas.getContext( ); [width, height] = [canvas.width, canvas.height]; const document 'canvas' const '2d' const First, we will draw the original image on the canvas: img = Image(); img.src = ; img.crossOrigin = ; img.onload = ctx.drawImage(img, , , width, height); const new './my-pretty-picture.png' 'anonymous' => () 0 0 Then, we will get the that we will work with: image data imageData = ctx.getImageData( , , width, height); data = imageData.data; const 0 0 const The image is represented as a a one-dimensional array in the RGBA order, with integer values between 0 and 255 (inclusive). data Uint8ClampedArray Memory Because we will work extensively with memory, we should . recall the fundamentals We can either create a memory instance from JavaScript and import it into the Wasm module or let the module initialize memory and export its instance into JavaScript. One benefit of the former approach is the possibility of initializing memory to the needed size. When exporting memory from Wasm, the initial size is one (64 KB). To increase its size, we have to call programmatically, which could be impractical, at least in cases where the size is known in advance. page memory.grow() Our image might be bigger than 64 KB, so we had better create a big-enough memory instance: arraySize = (width * height * ) >>> ; nPages = ((arraySize + ) & ~ ) >>> ; memory = WebAssembly.Memory({ : nPages }); const 4 0 const 0xffff 0xffff 16 const new initial Here comes the catch. To be able to import memory into a Wasm module, we have to compile our AssemblyScript code with the flag: --importMemory $ npm run asbuild:optimized -- --importMemory This will generate the following line in the compiled Wasm module: ( ( $ )) import "env" "memory" memory 0 1 Now, we can import our memory object from JavaScript into Wasm: WebAssembly .instantiateStreaming(fetch( ), { :{ memory } }) 'grayscale.wasm' env The next step is to initialize the memory with the image data: bytes = (memory.buffer); ( i = ; i < data.length; i++) bytes[i] = data[i]; const new Uint8ClampedArray for let 0 As you might have noticed, we use the same — — as image data to format and access memory bytes. typed array Uint8ClampedArray When the memory bytes are filled, we can execute our Wasm function: instance.exports .convertToGrayscale(width, height); And store the result from memory back into the image: ( i = ; i < bytes.length; i++) data[i] = bytes[i]; ctx.putImageData(imageData, , ); for let 0 0 0 Image Manipulation Now, when we have all we need to run an image-manipulation function in a browser, we shall actually write one. As previously mentioned, we will create a function that converts an image to grayscale: { len = width * height * ; ( i = ; i < len; i += ) { r = load<u8>(i); g = load<u8>(i + ); b = load<u8>(i + ); gray = u8( r * + g * + b * ); store<u8>(i, gray); store<u8>(i + , gray); store<u8>(i + , gray); } } export ( ): function convertToGrayscale width: i32, height: i32 void const 4 for let 0 4 /*rgba*/ const const 1 const 2 const 0.2126 0.7152 0.0722 1 2 As the image data contains linear RGBA quadruples of 8-bit signed integers (clamped to the range of values from 0 to 255), we iterate the array of data in 4-incremental steps. We load the RGB values from memory, compute the gray color, and store the color values back into the memory. Demo Time That’s it! Now we have all the know-how needed to develop awesome image processing Wasm functions in AssemblyScript. Here are some more examples: You can see demos . live on my blog The source code is on . my GitHub Previously published at https://blog.ttulka.com/learning-webassembly-10-image-processing-in-assemblyscript