summaryrefslogtreecommitdiff
path: root/juliaset
diff options
context:
space:
mode:
authorMitya Selivanov <automainint@guattari.tech>2025-01-16 23:26:35 +0100
committerMitya Selivanov <automainint@guattari.tech>2025-01-16 23:26:35 +0100
commit4ff31ff39b562eacaffbe3ed5c22cf564ed65fb5 (patch)
tree91e7a12c2f4513d88c80847e3e24489c2f6bb690 /juliaset
parentde476aa919244df699d4223fe2ebac5fb1f2ea28 (diff)
downloadstatic-4ff31ff39b562eacaffbe3ed5c22cf564ed65fb5.zip
Add Julia Set wasm program
Diffstat (limited to 'juliaset')
-rw-r--r--juliaset/index.htm603
-rwxr-xr-xjuliaset/julia_set.wasmbin0 -> 6912 bytes
2 files changed, 603 insertions, 0 deletions
diff --git a/juliaset/index.htm b/juliaset/index.htm
new file mode 100644
index 0000000..8da8454
--- /dev/null
+++ b/juliaset/index.htm
@@ -0,0 +1,603 @@
+<!DOCTYPE html>
+<html style="height: 100%; overflow: hidden; overflow-x: hidden;">
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width,initial-scale=1">
+ <link rel='icon' type='image/gif' href='/favicon.gif'>
+ <title> Reduced System Layer </title>
+ </head>
+ <body style="margin: 0; height: 100%; overflow: hidden; overflow-x: hidden;">
+ <canvas style="margin: 0; width: 100%; height: 100%;" id="frame" oncontextmenu="event.preventDefault()"></canvas>
+ <script type="worklet">
+ registerProcessor(
+ "Sound_Node",
+ class Sound_Node extends AudioWorkletProcessor {
+ constructor(options) {
+ super();
+
+ this.frame_width = 0;
+ this.frame_height = 0;
+ this.pixels_size = 0;
+
+ let event_handlers = {
+ wasm : (ev) => {
+ WebAssembly.instantiate(
+ ev.wasm,
+ {
+ wasi_snapshot_preview1 : {
+ clock_time_get : () => { return Date.now(); },
+ args_sizes_get : () => { throw new Error("Unexpected args_sizes_get call"); },
+ args_get : () => { throw new Error("Unexpected args_get call"); },
+ proc_exit : () => { throw new Error("Unexpected proc_exit call"); },
+ fd_close : () => { throw new Error("Unexpected fd_close call"); },
+ fd_write : () => { throw new Error("Unexpected fd_write call"); },
+ fd_seek : () => { throw new Error("Unexpected fd_seek call"); },
+ },
+ env : {
+ p_clipboard_write_impl : (size, text) => {
+ let text_buffer = new ArrayBuffer(size);
+ new Uint8Array(text_buffer).set(new Uint8Array(this.memory_buffer, text, size));
+
+ this.port.postMessage({
+ id : "clipboard",
+ text_buffer : text_buffer,
+ });
+ },
+ p_time_impl : Date.now,
+
+ floor : Math.floor,
+ ceil : Math.ceil,
+ sqrt : Math.sqrt,
+ cbrt : Math.cbrt,
+ pow : Math.pow,
+ log : Math.log,
+ log2 : Math.log2,
+ log10 : Math.log10,
+ exp : Math.exp,
+ sin : Math.sin,
+ cos : Math.cos,
+ tan : Math.tan,
+ asin : Math.asin,
+ acos : Math.acos,
+ atan : Math.atan,
+ atan2 : Math.atan2,
+ },
+ }
+ ).then((program) => {
+ this.memory_buffer = program.exports.memory.buffer;
+
+ let href_bytes = options.processorOptions.href;
+ let href_address = program.exports.js_href();
+ let href_len = Math.min(program.exports.js_href_size(), href_bytes.length);
+ new Uint8Array(this.memory_buffer, href_address, href_len).set(href_bytes.subarray(0, href_len));
+
+ program.exports.js_main();
+
+ this.sound_buffer_address = program.exports.js_sound_buffer();
+ this.pixels_address = program.exports.js_pixels();
+ this.frames = new Float32Array(this.memory_buffer, this.sound_buffer_address, program.exports.js_max_num_sound_frames());
+
+ let title_address = program.exports.js_title();
+
+ let title_buffer;
+
+ if (title_address != 0) {
+ let bytes = new Uint8Array(this.memory_buffer);
+ let title_len = 0;
+ while (bytes[title_address + title_len] != 0)
+ ++title_len;
+ title_buffer = new ArrayBuffer(title_len);
+ new Uint8Array(title_buffer).set(bytes.subarray(title_address, title_address + title_len));
+ } else
+ title_buffer = undefined;
+
+ this.port.postMessage({
+ id : "init",
+ title_buffer : title_buffer,
+ });
+
+ this.program = program;
+ });
+ },
+
+ resize : (ev) => {
+ this.frame_width = ev.width;
+ this.frame_height = ev.height;
+ this.pixels_size = ev.width * ev.height * 4;
+ },
+
+ mousedown : (ev) => {
+ if (this.program === undefined)
+ return;
+
+ this.program.exports.js_mousedown(ev.buttons);
+ },
+
+ mouseup : (ev) => {
+ if (this.program === undefined)
+ return;
+
+ this.program.exports.js_mouseup(ev.buttons);
+ },
+
+ mousemove : (ev) => {
+ if (this.program === undefined)
+ return;
+
+ this.program.exports.js_mousemove(ev.x, ev.y);
+ },
+
+ keydown : (ev) => {
+ if (this.program === undefined)
+ return;
+
+ this.program.exports.js_keydown(ev.key, ev.mod, ev.ch);
+ },
+
+ keyup : (ev) => {
+ if (this.program === undefined)
+ return;
+
+ this.program.exports.js_keyup(ev.key, ev.mod);
+ },
+
+ wheel : (ev) => {
+ if (this.program === undefined)
+ return;
+
+ this.program.exports.js_wheel(ev.x * -0.01, ev.y * -0.01);
+ },
+
+ paste : (ev) => {
+ if (this.program === undefined)
+ return;
+
+ let address = this.program.exports.js_clipboard_buffer(ev.bytes.length);
+ let len = this.program.exports.js_clipboard_size();
+
+ new Uint8Array(this.memory_buffer, address, len).set(ev.bytes.subarray(0, len));
+
+ this.program.exports.js_keydown(ev.key, ev.mod, ev.ch);
+ },
+ };
+
+ this.port.onmessage = (ev) => {
+ if (ev.data.id in event_handlers)
+ event_handlers[ev.data.id](ev.data);
+ };
+ }
+
+ process(input, output, parameters) {
+ if (this.program === undefined)
+ return true;
+
+ let num_channels = output[0].length;
+ let num_samples = output[0][0].length;
+ let num_frames = num_samples * num_channels;
+
+ this.program.exports.js_frame(this.frame_width, this.frame_height, num_samples);
+
+ let pixels_buffer = new ArrayBuffer(this.pixels_size);
+ new Uint8Array(pixels_buffer).set(new Uint8Array(this.memory_buffer, this.pixels_address, this.pixels_size));
+
+ this.port.postMessage({
+ id : "frame",
+ pixels_buffer : pixels_buffer,
+ });
+
+ for (let j = 0; j < num_channels; ++j)
+ for (let i = 0; i < num_samples; ++i)
+ output[0][j][i] = this.frames[j * num_samples + i];
+
+ return true;
+ }
+ }
+ );
+ </script>
+ <script type="text/javascript">
+ let key_map = {};
+
+ function key_from_code(code) {
+ if (code in key_map)
+ return key_map[code];
+ return 0;
+ };
+
+ function mod_from_event(ev) {
+ let mod = 0;
+ if (ev.ctrlKey) mod |= 1;
+ if (ev.shiftKey) mod |= 2;
+ if (ev.altKey) mod |= 4;
+ if (ev.metaKey) mod |= 8;
+ return mod;
+ };
+
+ function char_from_event(ev) {
+ if (ev.key.length != 1)
+ return 0;
+ return ev.key.charCodeAt(0);
+ }
+
+ async function obtain_defaults(wasm) {
+ let program = await WebAssembly.instantiate(
+ wasm,
+ {
+ wasi_snapshot_preview1 : {
+ clock_time_get : () => {},
+ args_sizes_get : () => {},
+ args_get : () => {},
+ proc_exit : () => {},
+ fd_close : () => {},
+ fd_write : () => {},
+ fd_seek : () => {},
+ },
+ env : {
+ p_clipboard_write_impl : () => {},
+ p_time_impl : () => {},
+
+ floor : () => {},
+ ceil : () => {},
+ sqrt : () => {},
+ cbrt : () => {},
+ pow : () => {},
+ log : () => {},
+ log2 : () => {},
+ log10 : () => {},
+ exp : () => {},
+ sin : () => {},
+ cos : () => {},
+ tan : () => {},
+ asin : () => {},
+ acos : () => {},
+ atan : () => {},
+ atan2 : () => {},
+ },
+ }
+ );
+
+ let num = program.exports.js_max_num_keys();
+ let address = program.exports.js_key_map();
+ let keys = new Uint16Array(program.exports.memory.buffer, address, num);
+
+ let n = 0;
+
+ key_map["Backspace"] = keys[n++];
+ key_map["Tab"] = keys[n++];
+ key_map["Enter"] = keys[n++];
+ key_map["ControlLeft"] = keys[n++];
+ key_map["ControlRight"] = keys[n++];
+ key_map["ShiftLeft"] = keys[n++];
+ key_map["ShiftRight"] = keys[n++];
+ key_map["AltLeft"] = keys[n++];
+ key_map["AltRight"] = keys[n++];
+ key_map["ArrowLeft"] = keys[n++];
+ key_map["ArrowRight"] = keys[n++];
+ key_map["ArrowUp"] = keys[n++];
+ key_map["ArrowDown"] = keys[n++];
+ key_map["Pause"] = keys[n++];
+ key_map["Insert"] = keys[n++];
+ key_map["Home"] = keys[n++];
+ key_map["End"] = keys[n++];
+ key_map["PageUp"] = keys[n++];
+ key_map["PageDown"] = keys[n++];
+ key_map["Escape"] = keys[n++];
+ key_map["PrintScreen"] = keys[n++];
+ key_map["Space"] = keys[n++];
+ key_map["MetaLeft"] = keys[n++];
+ key_map["MetaRight"] = keys[n++];
+ key_map["Quote"] = keys[n++];
+ key_map["Comma"] = keys[n++];
+ key_map["Minus"] = keys[n++];
+ key_map["Period"] = keys[n++];
+ key_map["Slash"] = keys[n++];
+ key_map["Digit0"] = keys[n++];
+ key_map["Digit1"] = keys[n++];
+ key_map["Digit2"] = keys[n++];
+ key_map["Digit3"] = keys[n++];
+ key_map["Digit4"] = keys[n++];
+ key_map["Digit5"] = keys[n++];
+ key_map["Digit6"] = keys[n++];
+ key_map["Digit7"] = keys[n++];
+ key_map["Digit8"] = keys[n++];
+ key_map["Digit9"] = keys[n++];
+ key_map["Semicolon"] = keys[n++];
+ key_map["Equal"] = keys[n++];
+ key_map["BracketLeft"] = keys[n++];
+ key_map["Backslash"] = keys[n++];
+ key_map["BracketRight"] = keys[n++];
+ key_map["Backquote"] = keys[n++];
+ key_map["KeyA"] = keys[n++];
+ key_map["KeyB"] = keys[n++];
+ key_map["KeyC"] = keys[n++];
+ key_map["KeyD"] = keys[n++];
+ key_map["KeyE"] = keys[n++];
+ key_map["KeyF"] = keys[n++];
+ key_map["KeyG"] = keys[n++];
+ key_map["KeyH"] = keys[n++];
+ key_map["KeyI"] = keys[n++];
+ key_map["KeyJ"] = keys[n++];
+ key_map["KeyK"] = keys[n++];
+ key_map["KeyL"] = keys[n++];
+ key_map["KeyM"] = keys[n++];
+ key_map["KeyN"] = keys[n++];
+ key_map["KeyO"] = keys[n++];
+ key_map["KeyP"] = keys[n++];
+ key_map["KeyQ"] = keys[n++];
+ key_map["KeyR"] = keys[n++];
+ key_map["KeyS"] = keys[n++];
+ key_map["KeyT"] = keys[n++];
+ key_map["KeyU"] = keys[n++];
+ key_map["KeyV"] = keys[n++];
+ key_map["KeyW"] = keys[n++];
+ key_map["KeyX"] = keys[n++];
+ key_map["KeyY"] = keys[n++];
+ key_map["KeyZ"] = keys[n++];
+ key_map["Delete"] = keys[n++];
+ key_map["F1"] = keys[n++];
+ key_map["F2"] = keys[n++];
+ key_map["F3"] = keys[n++];
+ key_map["F4"] = keys[n++];
+ key_map["F5"] = keys[n++];
+ key_map["F6"] = keys[n++];
+ key_map["F7"] = keys[n++];
+ key_map["F8"] = keys[n++];
+ key_map["F9"] = keys[n++];
+ key_map["F10"] = keys[n++];
+ key_map["F11"] = keys[n++];
+ key_map["F12"] = keys[n++];
+ key_map["F13"] = keys[n++];
+ key_map["F14"] = keys[n++];
+ key_map["F15"] = keys[n++];
+ key_map["F16"] = keys[n++];
+ key_map["F17"] = keys[n++];
+ key_map["F18"] = keys[n++];
+ key_map["F19"] = keys[n++];
+ key_map["F20"] = keys[n++];
+ key_map["F21"] = keys[n++];
+ key_map["F22"] = keys[n++];
+ key_map["F23"] = keys[n++];
+ key_map["F24"] = keys[n++];
+
+ let defaults = {
+ sound_sample_rate : program.exports.js_sound_sample_rate(),
+ num_sound_channels : program.exports.js_num_sound_channels(),
+ key_paste : key_map["KeyV"],
+ };
+
+ return defaults;
+ }
+
+ async function run(attrs) {
+ let wasm = await WebAssembly.compileStreaming(attrs.wasm);
+
+ let { sound_sample_rate, num_sound_channels, key_paste } = await obtain_defaults(wasm);
+
+ let frame_width = 0;
+ let frame_height = 0;
+
+ let canvas;
+ let context;
+ let pixels_buffer;
+ let animation_frame;
+
+ let sound_ready = false;
+ let sound_init;
+ let sound_context;
+ let sound_node;
+
+ let paste_mod = 0;
+ let paste_ch = 0;
+
+ canvas = attrs.canvas;
+ context = canvas.getContext("2d");
+
+ context.fillStyle = "#000";
+ context.fillRect(0, 0, canvas.width, canvas.height);
+
+ context.fillStyle = "#fff";
+ context.font = "1.7em serif";
+ context.textAlign = "center";
+ context.textBaseline = "middle";
+ context.fillText("Click to run", canvas.width / 2.0, canvas.height / 2.0);
+
+ animation_frame = () => {
+ if (attrs.fit_window) {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+ }
+
+ if (frame_width != canvas.width || frame_height != canvas.height) {
+ frame_width = canvas.width;
+ frame_height = canvas.height;
+
+ sound_node.port.postMessage({
+ id : "resize",
+ width : frame_width,
+ height : frame_height,
+ });
+ }
+
+ let num_bytes = 4 * canvas.width * canvas.height;
+
+ if (pixels_buffer !== undefined && pixels_buffer.byteLength >= num_bytes) {
+ let pixels = new Uint8ClampedArray(pixels_buffer, 0, num_bytes);
+
+ let data = new ImageData(
+ pixels,
+ canvas.width,
+ canvas.height
+ );
+
+ context.putImageData(data, 0, 0);
+ } else {
+ context.fillStyle = "#000";
+ context.fillRect(0, 0, canvas.width, canvas.height);
+ }
+
+ window.requestAnimationFrame(animation_frame);
+ };
+
+ sound_init = async () => {
+ sound_context = new AudioContext({
+ sampleRate : sound_sample_rate,
+ });
+
+ let blob = new Blob(
+ [ document.querySelector("script[type=worklet]").innerText ],
+ { type : "application/javascript", }
+ );
+
+ await sound_context.audioWorklet.addModule(URL.createObjectURL(blob));
+
+ sound_node = new AudioWorkletNode(
+ sound_context,
+ "Sound_Node",
+ { numberOfInputs : 0,
+ outputChannelCount : [ num_sound_channels ],
+ processorOptions : { href : new TextEncoder("utf8").encode(document.location.href) }, }
+ );
+
+ sound_node.connect(sound_context.destination);
+
+ let message_handlers = {
+ init : (ev) => {
+ if (ev.title_buffer != undefined)
+ document.title = new TextDecoder("utf8").decode(new Uint8Array(ev.title_buffer));
+
+ sound_ready = true;
+
+ window.requestAnimationFrame(animation_frame);
+ },
+
+ clipboard : (ev) => {
+ navigator.clipboard.writeText(new TextDecoder("utf8").decode(new Uint8Array(ev.text_buffer)));
+ },
+
+ frame : (ev) => {
+ pixels_buffer = ev.pixels_buffer;
+ }
+ };
+
+ sound_node.port.onmessage = (ev) => {
+ if (ev.data.id in message_handlers)
+ message_handlers[ev.data.id](ev.data);
+ };
+
+ sound_node.port.postMessage({
+ id : "wasm",
+ wasm : wasm,
+ });
+
+ wasm = undefined;
+ };
+
+ canvas.addEventListener("mousedown", (ev) => {
+ if (!sound_ready) {
+ sound_init();
+ sound_init = () => {};
+ return;
+ }
+
+ sound_node.port.postMessage({
+ id : "mousedown",
+ buttons : ev.buttons,
+ });
+ });
+
+ canvas.addEventListener("mouseup", (ev) => {
+ if (!sound_ready)
+ return;
+
+ sound_node.port.postMessage({
+ id : "mouseup",
+ buttons : ev.buttons,
+ });
+ });
+
+ canvas.addEventListener("mousemove", (ev) => {
+ if (!sound_ready)
+ return;
+
+ sound_node.port.postMessage({
+ id : "mousemove",
+ x : ev.clientX,
+ y : ev.clientY,
+ });
+ });
+
+ window.addEventListener("keydown", (ev) => {
+ if (!sound_ready)
+ return;
+ if (ev.repeat)
+ return;
+ let key = key_from_code(ev.code);
+ if (key == 0)
+ return;
+ let mod = mod_from_event(ev);
+ let ch = char_from_event(ev);
+
+ if (ev.ctrlKey && key == key_paste) {
+ paste_mod = mod;
+ paste_ch = ch;
+ } else
+ sound_node.port.postMessage({
+ id : "keydown",
+ mod : mod,
+ key : key,
+ ch : ch,
+ });
+ });
+
+ window.addEventListener("keyup", (ev) => {
+ ev.preventDefault();
+ if (!sound_ready)
+ return;
+ if (ev.repeat)
+ return;
+ let key = key_from_code(ev.code);
+ if (key == 0)
+ return;
+ let mod = mod_from_event(ev);
+
+ sound_node.port.postMessage({
+ id : "keyup",
+ key : key,
+ mod : mod,
+ });
+ });
+
+ window.addEventListener("wheel", (ev) => {
+ ev.preventDefault();
+ if (!sound_ready)
+ return;
+
+ sound_node.port.postMessage({
+ id : "wheel",
+ x : ev.deltaX,
+ y : ev.deltaY,
+ });
+ });
+
+ window.addEventListener("paste", (ev) => {
+ ev.preventDefault();
+ if (!sound_ready)
+ return;
+
+ sound_node.port.postMessage({
+ id : "paste",
+ bytes : new TextEncoder("utf8").encode(ev.clipboardData.getData("text")),
+ mod : paste_mod,
+ key : key_paste,
+ ch : paste_ch,
+ });
+ });
+ }
+
+ run({
+ canvas : document.getElementById("frame"),
+ wasm : fetch("julia_set.wasm"),
+ fit_window : true,
+ }).catch((e) => console.error(e));
+ </script>
+ </body>
+</html>
diff --git a/juliaset/julia_set.wasm b/juliaset/julia_set.wasm
new file mode 100755
index 0000000..9b35f10
--- /dev/null
+++ b/juliaset/julia_set.wasm
Binary files differ