-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
121 lines (100 loc) · 3.51 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
function main() {
const url = "sandfall.wasm";
// this is an old version of loading wasm modules; however the new streaming API doesn't work with neocities or github pages
fetch(url)
.then((response) => response.arrayBuffer())
.then((bytes) => WebAssembly.instantiate(bytes, { console }))
.then(
(obj) => {
const vw = document.documentElement.clientWidth;
const vh = document.documentElement.clientHeight;
const scale = 4;
const width = Math.floor(vw / scale);
const height = Math.floor(vh / scale);
const { memory, init, step, stamp, block_stamp: blockStamp } =
obj.instance.exports;
init(width, height);
const length = width * height * 4; // 4 bytes in 32 bit int
const arr = new Uint8ClampedArray(memory.buffer);
const canvas = document.getElementById("canvas");
const tutorial = document.getElementById("tutorial");
const helpText = document.querySelector("#tutorial p");
if (!window.matchMedia("(pointer: coarse)").matches) {
helpText.remove();
}
const ctx = canvas.getContext("2d", { alpha: true });
const imageData = new ImageData(arr.subarray(0, length), width, height);
canvas.width = width;
canvas.height = height;
canvas.style.transform = `scale(${scale})`;
const rgb = [0, 0, 0];
const cursors = [];
const cancel = () => {
cursors.length = 0;
};
const move = (event, xys) => {
tutorial.remove();
const bounding = canvas.getBoundingClientRect();
cursors.length = 0;
cursors.push(...xys.map(([clientX, clientY]) => [
clamp(~~((clientX - bounding.left) / scale), 0, width),
clamp(~~((clientY - bounding.top) / scale), 0, height),
]));
};
canvas.addEventListener("mousemove", (event) => {
const clientX = event.clientX;
const clientY = event.clientY;
move(event, [[clientX, clientY]]);
});
canvas.addEventListener("touchmove", (event) => {
move(
event,
Array.from(event.touches).map((e) => [e.clientX, e.clientY]),
);
});
canvas.addEventListener("touchcancel", cancel);
canvas.addEventListener("touchend", cancel);
canvas.addEventListener("mouseleave", cancel);
canvas.classList.remove("loading");
const twiddle = (x, delta = 15) => {
const dx = Math.floor(Math.random() * delta - (0.5 * delta));
return (x + dx + 256) % 256;
};
const raf = () => {
const [r, g, b] = rgb;
// slightly shift color
rgb[0] = twiddle(r);
rgb[1] = twiddle(g);
rgb[2] = twiddle(b);
// draw block of pixels
cursors.forEach(([x, y]) => {
blockStamp(x, y, r, g, b);
});
// time step
step();
ctx.putImageData(imageData, 0, 0);
requestAnimationFrame(raf);
};
raf();
},
);
}
function clamp(val, min, max) {
return Math.min(Math.max(val, min), max);
}
function debounce(func, timeout = 300) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, timeout);
};
}
document.addEventListener("DOMContentLoaded", () => {
main();
const reload = debounce(() => {
location.reload();
}, 500);
window.addEventListener("resize", reload);
});