Skip to content

Instantly share code, notes, and snippets.

@mikewlange
Created August 6, 2019 02:49
Show Gist options
  • Save mikewlange/15ccc65426ee9fd22de416b86a52e7ce to your computer and use it in GitHub Desktop.
Save mikewlange/15ccc65426ee9fd22de416b86a52e7ce to your computer and use it in GitHub Desktop.
Globe
(function () {
'use strict';
// braille
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');
// util
const frame = () =>
new Promise(resolve => requestAnimationFrame(resolve));
const element = (name, options) =>
Object.assign(document.createElement(name), options);
// bayer
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) + .5) / area;
return (x, y) => lut[x%size + y%size*size]
};
// render
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*(.5 + .5*fbm(.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**.5)**.5 + 1);
const t = texture(1e-1*w + f*u, f*v, 1e-2*w);
return t > bayer4(x, y)
};
// main
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 << 1;
const height = rr.height/fontHeight << 2;
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 - 1);
if (globe(x, y, u, v, time))
set(pixels, x, y);
}
}
visible.textContent = render(pixels);
}
};
main();
}());
<script src="//unpkg.com/simplex-noise@2"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.4/js/tether.min.js"></script>
@import url('//cdn.rawgit.com/arcticicestudio/nord/develop/src/nord.css');
@import url('//cdn.jsdelivr.net/gh/be5invis/Iosevka@ff81c66/fonts.css');
:root {
--size: calc(100vmin / 1.618033988749895);
--background: var(--nord0);
--foreground: var(--nord6);
}
html, body {
width: 100%;
height: 100%;
}
body {
margin-left: -20%;
background: var(--background);
color: var(--foreground);
}
body, .braille {
display: flex;
align-items: center;
justify-content: center;
flex-flow: row;
}
.braille {
max-width: 100%;
max-height: 100%;
width: var(--size);
height: var(--size);
font: 11px/12px 'Iosevka Web', monospace;
}
.braille > * {
white-space: pre;
}
.braille .hidden {
position: fixed;
bottom: 100%;
right: 100%;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment