<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>WebAssembly &#8211; Matthew Petroff</title>
	<atom:link href="https://mpetroff.net/tag/webassembly/feed/" rel="self" type="application/rss+xml" />
	<link>https://mpetroff.net</link>
	<description>mpetroff.net</description>
	<lastBuildDate>Sun, 21 Feb 2021 12:30:47 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	
	<item>
		<title>Space-efficient Embedding of WebAssembly in JavaScript</title>
		<link>https://mpetroff.net/2021/02/space-efficient-embedding-of-webassembly-in-javascript/</link>
					<comments>https://mpetroff.net/2021/02/space-efficient-embedding-of-webassembly-in-javascript/#comments</comments>
		
		<dc:creator><![CDATA[Matthew Petroff]]></dc:creator>
		<pubDate>Sat, 20 Feb 2021 15:01:07 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[PNG]]></category>
		<category><![CDATA[WASM]]></category>
		<category><![CDATA[WebAssembly]]></category>
		<guid isPermaLink="false">https://mpetroff.net/?p=3336</guid>

					<description><![CDATA[Recently, I came across a blog post about converting parts of a JavaScript library into WebAssembly. The part that interested me the most was a section about efficiently embedding the WebAssembly binary into the JavaScript code such that the library &#8230; <a href="https://mpetroff.net/2021/02/space-efficient-embedding-of-webassembly-in-javascript/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[<p><span class="dropcap">R</span>ecently, I came across a blog post about <a href="https://engineering.q42.nl/webassembly/">converting parts of a JavaScript library into WebAssembly</a>. The part that interested me the most was a section about efficiently embedding the WebAssembly binary into the JavaScript code such that the library could be distributed as a single file, instead of the usual method of providing the WebAssembly binary as a separate file. This is accomplished by Base64-encoding the WebAssembly binary as a string and including the resulting string in the JavaScript file. Unfortunately, this significantly inflates the total file size, since the Base64-encoded string does not compress nearly as well as the original binary. To mitigate this issue, the blog post author had the clever idea of gzip-compressing the binary prior to Base64-encoding it and using the <a href="https://github.com/imaya/zlib.js">zlib.js</a> JavaScript library to decompress the binary client-side, after undoing the Base64-encoding. While this significantly reduced the size of the Base64-encoded WebAssembly binary, it required ~6.5&thinsp;kB for the decompression code, after gzip compression.<sup id="rf1-3336"><a href="https://mpetroff.net/2021/02/space-efficient-embedding-of-webassembly-in-javascript/#fn1-3336" title=" The author mentioned 12&thinsp;kB of decompression code, but that was without gzip-compression of the JavaScript code. " rel="footnote">1</a></sup></p>
<p>While I liked the idea of compressing the WebAssembly binary prior to Base64-encoding it, I thought there must be a way of decompressing it with a smaller decompression code. The simplest change would be to use a raw Deflate-compressed payload instead of one encapsulated with gzip, as the zlib.js library also provides a decompression function for this, which is only ~2.5&thinsp;kB after gzip-compression, saving ~4&thinsp;kB. However, this is still excessive, since it shouldn&#8217;t be necessary to provide a Deflate decompression function as web browsers include such functionality. Although such decompression functionality isn&#8217;t exposed directly to JavaScript, PNG images can be decoded from JavaScript, and PNG images use Deflate compression. Thus, I decided to encode the WebAssembly binary as a grayscale PNG image, Base64-encode the PNG as a data URI, and include the resulting string in the JavaScript file.<span id="more-3336"></span></p>
<p>To encode the binary as a PNG image, the dimensions of the image must first be decided on. For this, I decided to set the image width to the smallest power of two that allowed the image to have a landscape aspect ratio, although this decision was somewhat arbitrary. Each pixel in the grayscale image corresponds to one byte in the WebAssembly binary, starting in the top-left corner of the image and wrapping line-by-line. Any remaining pixels in the last row of the image were set to zero, but this presented a problem, since zero-padding a WebAssembly binary is not allowed. Thus, the first four pixels of the image are used to store the size of the WebAssembly binary as an unsigned 32-bit little-endian integer, which can then be used by the decoder to truncate the image data to the correct length. The resulting PNG image can then be optimized using tools such as <a href="https://github.com/shssoichiro/oxipng">OxiPNG</a> to reduce its file size further, after which the PNG image is Base64-encoded as a data URI.</p>
<p>To decode the Base64-encoded PNG image into WebAssembly from JavaScript, the <code>Image()</code> constructor is used to create an <code>&lt;image&gt;</code> element from the Base64-encoded string. Then, the <code>&lt;image&gt;</code> is drawn to a <code>&lt;canvas&gt;</code> element, and the <code>getImageData()</code> method is used to extract the image data as an array. The array is then filtered to keep only every fourth pixel, to convert the RGBA data to grayscale.<sup id="rf2-3336"><a href="https://mpetroff.net/2021/02/space-efficient-embedding-of-webassembly-in-javascript/#fn2-3336" title=" This step wouldn&#8217;t be necessary for a RGBA PNG, but these don&#8217;t seem to compress WebAssembly binaries quite as well as grayscale PNGs. " rel="footnote">2</a></sup> Next, the first four bytes containing the WebAssembly binary length are decoded, removed from the array, and used to truncate the array to just the WebAssembly binary data. Finally, these data are used to instantiate the WebAssembly code. The decoding routine is &lt;0.3&thinsp;kB after gzip compression.</p>
<p>I have made available a <a href="https://mpetroff.net/files/wasm-compression/">demo</a> that includes an encoding procedure written in Python, the JavaScript decoding procedure, and a live example using MDN&#8217;s <a href="https://github.com/mdn/webassembly-examples/blob/936ab39d0b2f83295966941f7a916c4df65f0f4e/js-api-examples/simple.wasm">simple WebAssembly example</a>. While this demonstrates the technique, it doesn&#8217;t provide a meaningful example of the bandwidth savings. Thus, I also applied the technique to the <a href="https://kripken.github.io/ammo.js/examples/webgl_demo/ammo.wasm.html">ammo.js WebAssembly demo</a>. For this example, the original WebAssembly binary is ~651&thinsp;kB uncompressed, ~252&thinsp;kB when compressed with <code>gzip -9</code>, and ~218&thinsp;kB when compressed with <code>brotli -9</code>. As a Base64-encoded string, it is ~880&thinsp;kB, which is reduced to ~358&thinsp;kB when compressed with <code>gzip -9</code> or ~316&thinsp;kB when compressed with <code>brotli -9</code>. When converted to a PNG image using the technique described in this blog post and optimized using OxiPNG, the resulting image is ~242&thinsp;kB. When converted to a Base64-encoded data URI, it is ~322&thinsp;kB, which is reduced to ~244&thinsp;kB when compressed with <code>gzip -9</code> or ~242&thinsp;kB when compressed with <code>brotli -9</code>. While not quite as small as the Brotli-compressed binary, the technique described in this blog post does better than the gzip-compressed binary and does much better than naively include a Base64-encoded binary in JavaScript.</p>
<hr class="footnotes"><ol class="footnotes" style="list-style-type:decimal"><li id="fn1-3336"><p > The author mentioned 12&thinsp;kB of decompression code, but that was without gzip-compression of the JavaScript code. &nbsp;<a href="https://mpetroff.net/2021/02/space-efficient-embedding-of-webassembly-in-javascript/#rf1-3336" class="backlink" title="Return to footnote 1.">&#8617;</a></p></li><li id="fn2-3336"><p > This step wouldn&#8217;t be necessary for a RGBA PNG, but these don&#8217;t seem to compress WebAssembly binaries quite as well as grayscale PNGs. &nbsp;<a href="https://mpetroff.net/2021/02/space-efficient-embedding-of-webassembly-in-javascript/#rf2-3336" class="backlink" title="Return to footnote 2.">&#8617;</a></p></li></ol>]]></content:encoded>
					
					<wfw:commentRss>https://mpetroff.net/2021/02/space-efficient-embedding-of-webassembly-in-javascript/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 
Content Delivery Network via Amazon Web Services: CloudFront: cdn0.mpetroff.net
Minified using Disk

Served from: mpetroff.net @ 2026-03-30 09:43:59 by W3 Total Cache
-->