(function () { "use strict"; const enc = (x) => ((x & 0x08) << 3) | ((x & 0x70) >> 1) | (x & 0x87) | 0x2800; const row = (x) => String.fromCharCode(...Array.from(x, enc)); const create = (width, height) => Array.from(Array(height >> 2), () => new Uint8Array(width >> 1)); const set = (table, x, y) => (table[y >> 2][x >> 1] |= 1 << ((y & 3) | ((x & 1) << 2))); const render = (table) => table.map(row).join("\n"); const frame = () => new Promise((resolve) => requestAnimationFrame(resolve)); const element = (name, options) => Object.assign(document.createElement(name), options); const bayer = (order, x, y) => { let z = 0; for (let i = order; i--; x >>= 1, y >>= 1) z = ((((x & 1) ^ (y & 1)) | (z << 1)) << 1) | (y & 1); return z; }; const lut = (order) => { const size = 1 << order, area = size * size; const lut = new Float32Array(area); for (let y = 0; y < size; y++) for (let x = 0; x < size; x++) lut[x + y * size] = (bayer(order, x, y) + 0.5) / area; return (x, y) => lut[(x % size) + (y % size) * size]; }; const simplex = new SimplexNoise(); const bayer4 = lut(4); const fbm = (freq, amp, x, y, z) => simplex.noise3D(x * (freq *= 2), y * freq, z * freq) * (amp /= 2) + simplex.noise3D(x * (freq *= 2), y * freq, z * freq) * (amp /= 2) + simplex.noise3D(x * (freq *= 2), y * freq, z * freq) * (amp /= 2) + simplex.noise3D(x * (freq *= 2), y * freq, z * freq) * (amp /= 2) + simplex.noise3D(x * (freq *= 2), y * freq, z * freq) * (amp /= 2) + simplex.noise3D(x * (freq *= 2), y * freq, z * freq) * (amp /= 2) + simplex.noise3D(x * (freq *= 2), y * freq, z * freq) * (amp /= 2) + simplex.noise3D(x * (freq *= 2), y * freq, z * freq) * (amp /= 2); const texture = (u, v, w) => (2 * (0.5 + 0.5 * fbm(0.5, 1, u, v, w))) % 1; const globe = (x, y, u, v, w) => { const d = u * u + v * v; if (d > 1) return false; const f = 1 / ((1 - d ** 0.5) ** 0.5 + 1); const t = texture(1e-1 * w + f * u, f * v, 1e-2 * w); return t > bayer4(x, y); }; const main = async () => { const fillerSize = 100; const filler = "\u28ff".repeat(fillerSize) + "\n\u28ff".repeat(fillerSize - 1); const root = element("div", { className: "braille" }); const hidden = element("div", { className: "hidden", textContent: filler }); const visible = element("div", { className: "visible" }); root.appendChild(hidden); root.appendChild(visible); document.body.appendChild(root); for (; ; await frame()) { const hr = hidden.getBoundingClientRect(); const fontWidth = hr.width / fillerSize; const fontHeight = hr.height / fillerSize; const rr = root.getBoundingClientRect(); const ratio = rr.width / rr.height; const width = (rr.width / fontWidth) << 2; const height = (rr.height / fontHeight) << 3; const pixels = create(width, height); const time = 1e-3 * Date.now(); for (let y = 0; y < height; y++) { const v = (2 * y) / height - 1; for (let x = 0; x < width; x++) { const u = ratio * ((2 * x) / width - 3.3); const shiftedU = u + 1.2; if (globe(x, y, shiftedU, v, time)) set(pixels, x, y); } } visible.textContent = render(pixels); } }; main(); })();