4

I need to embed a wasm app directly into the html page itself as opposed to load it (e.g. via xhr) from a separate file. So far I managed to embed it by "printing" the bytes straight into a Uint8Array (see below) but it's not space efficient(obviously). A 65MB wasm file becomes an 220 MB js file.

const go = new Go() WebAssembly.instantiate(new Uint8Array([0,97,115,109,1,0,0,0,0,242,128,128,128,0,10,103,111,46,98,117,105,108,100,105,100,255,32,71,111,32,98,117,105,108,100,32,73,68,58,32,34,56,69,84,87,98,97,65,117,88,67,100,52,98,55,72,90,112,90,114,70,47,85,111,66,116,82,89,102,80,118,83,101,111,69,111,106,117,50,85,99,90,47,115,77,71,110,85,106,....], { type: 'application/wasm' });

Mihai
  • 438
  • 3
  • 13

1 Answers1

2

An efficient way to encode a WebAssembly module is using Base64 encoding. You can encode (in Node) as follows:

const readFileSync = require('fs').readFileSync;

const wasmCode = readFileSync(id);
const encoded = Buffer.from(wasmCode, 'binary').toString('base64')

And decode in a browser or Node as follows:

var encoded = "...";

function asciiToBinary(str) {
  if (typeof atob === 'function') {
    return atob(str)
  } else {
    return new Buffer(str, 'base64').toString('binary');
  }
}

function decode(encoded) {
  var binaryString =  asciiToBinary(encoded);
  var bytes = new Uint8Array(binaryString.length);
  for (var i = 0; i < binaryString.length; i++) {
    bytes[i] = binaryString.charCodeAt(i);
  }
  return bytes.buffer;
}

var instance = WebAssembly.instantiate(decode(encoded), importObject)
      .then(r => r.instance);
ColinE
  • 68,894
  • 15
  • 164
  • 232
  • 5
    It really depends on the definition of "efficient". While it's true that base64 will make raw HTML / JS smaller, the brotli- (or even older gzip-) compressed file might often be smaller with regular Uint8Array literal. This, combined with the time spent manually decoding the string into binary, might make base64 not very efficient solution. – RReverser May 04 '20 at 18:35