Canvas Draw
On destktop, mouse down to draw red dots on canvas. On mobile, touch to draw. The WASM program has 63 bytes.
index.js
(async () => {
const canvas = document.getElementById('canvas');
canvas.style.width = "100%";
const hex_output = document.getElementById('hex');
const width = 512;
const height = 512;
canvas.width = width;
canvas.height = height;
canvas.style.border = '1px solid black';
const ctx = canvas.getContext('2d');
ctx.fillRect(0, 0, width, height);
let imageData = ctx.getImageData(0, 0, width, height);
let x = 0;
let y = 0;
let offset = 0;
const check_boundary = () => {
if (x < 0) { x = 0 }
if (x > width - 1) { x = width - 1 }
if (y < 0) { y = 0 }
if (y > height - 1) { y = height - 1 }
offset = 4 * (y * width + x);
}
let drawing = false;
const start = (e) => {
const rect = canvas.getBoundingClientRect();
x = Math.floor(e.clientX - rect.left);
y = Math.floor(e.clientY - rect.top);
drawing = true;
hexdump();
};
canvas.addEventListener('mousedown', start, false)
canvas.addEventListener('touchstart', start, false)
const end = (e) => {
drawing = false;
hexdump();
}
canvas.addEventListener('mouseup', end, false)
canvas.addEventListener('touchend', end, false);
const move = (e) => {
e.preventDefault();
const rect = canvas.getBoundingClientRect();
const ratio = width / canvas.clientWidth;
const { changedTouches } = e;
if (changedTouches && changedTouches[0]) {
x = Math.floor((changedTouches[0].pageX - canvas.offsetLeft) * ratio);
y = Math.floor((changedTouches[0].pageY - canvas.offsetTop) * ratio);
}
else {
x = Math.floor((e.clientX - rect.left) * ratio);
y = Math.floor((e.clientY - rect.top) * ratio);
}
if (drawing) {
draw();
canvas_render();
}
hexdump();
};
canvas.addEventListener('mousemove', move, false);
canvas.addEventListener('touchmove', move, false);
canvas.addEventListener('mouseout', (e) => {
x = 0;
y = 0;
offset = 0;
drawing = false;
hexdump();
}, false)
const hexdump = () => {
const { data } = imageData;
check_boundary();
let output = '';
for (let i = offset; i < offset + 0x100; i++) {
if (i < data.length) {
if (data[i] < 0x10) {
output += `0${data[i].toString(16)}`;
}
else {
output += `${data[i].toString(16)}`;
}
if ((i % 0x10) === 0x0f) {
output += '\n';
}
else {
output += ' ';
}
}
}
hex_output.innerHTML = `
x: ${parseInt(x)}
y: ${parseInt(y)}
hexdump (offset: ${offset})
${output}
`;
};
const canvas_render = () => {
for (let i = 0; i < imageData.data.length; i++) {
imageData.data[i] = memory_data[i];
}
ctx.putImageData(imageData, 0, 0);
}
const magic = [0x00, 0x61, 0x73, 0x6d];
const version = [0x01, 0x00, 0x00, 0x00];
const section_01 = [
0x01, // type section
0x09, // 9 bytes
0x02, // number of functions
0x60, // first func type (j.o)
0x00, // no input
0x00, // no output
0x60, // second func type (i)
0x02, // takes two params
0x7f, 0x7f, // i32
0x00 // no return
];
const section_02 = [
0x02, // import section
0x07, // 7 bytes
0x01, // 1 import
0x01, // 1 byte
0x6a, // j
0x01, // 1 byte
0x6f, // o
0x00, // no input
0x00 // no output
]
const section_03 = [
0x03, // func section
0x02, // 2 bytes
0x01, // number of functions
0x01, // type of the function (i)
];
const section_05 = [
0x05, // memory section
0x03, // 3 bytes
0x01, // number of memory
0x00, // min
0x10 // max 16 pages of data
];
const section_07 = [
0x07, // export section
0x09, // 9 bytes
0x02, // number of exports
0x01, // 1 byte name
0x6d, // "m"
0x02, // memory
0x00, // memory id
0x01, // 1 byte name
0x69, // "i"
0x00, // function
0x01 // func id
];
const section_0a_header = [
0x0a, // code section
0x0d, // 13 bytes
0x01 // number of function bodies
]
const section_0a_i = [ // input function i(memory_index, value)
0x0b, // 11 bytes
0x00, // number of local variables
0x20, // local.get
0x00, // 0
0x20, // local.get
0x01, // 1
0x36, // i32.store
0x02, // align
0x00, // offset
0x10, // call
0x00, // function j.o() (imported)
0x0b // opcode for end
]
const section_0a = section_0a_header
.concat(section_0a_i);
const wasm = new Uint8Array(
magic.concat(version)
.concat(section_01)
.concat(section_02)
.concat(section_03)
.concat(section_05)
.concat(section_07)
.concat(section_0a)
);
console.log({ wasm });
const importObject = {
j: {
o: () => {
canvas_render();
hexdump();
},
}
};
const module = await WebAssembly.compile(wasm.buffer);
const instance = await WebAssembly.instantiate(module, importObject);
const { exports } = instance;
const { i, m } = exports;
const memory_data = new Uint8ClampedArray(m.buffer);
const draw = () => {
check_boundary();
i(offset, 0xff0000ff); // AABBGGRR
}
})();