Skip to content

Instantly share code, notes, and snippets.

@sdan
Created December 5, 2025 23:11
Show Gist options
  • Select an option

  • Save sdan/fc702f2328c4a04a5fdfe2a78646c275 to your computer and use it in GitHub Desktop.

Select an option

Save sdan/fc702f2328c4a04a5fdfe2a78646c275 to your computer and use it in GitHub Desktop.
import React, { useState, useEffect, useRef } from 'react';
export default function BrailleTsunami() {
const [frame, setFrame] = useState('');
const timeRef = useRef(0);
// Braille character encoding - each char is 2x4 dots
// Dot positions: 1 4
// 2 5
// 3 6
// 7 8
const dotMap = [0x01, 0x02, 0x04, 0x40, 0x08, 0x10, 0x20, 0x80];
const toBraille = (dots) => String.fromCharCode(0x2800 + dots);
useEffect(() => {
const WIDTH = 80; // characters wide
const HEIGHT = 24; // characters tall
const PIXEL_W = WIDTH * 2; // actual pixel width (2 dots per char horizontally)
const PIXEL_H = HEIGHT * 4; // actual pixel height (4 dots per char vertically)
const renderFrame = () => {
const t = timeRef.current;
timeRef.current += 0.05;
// Z-buffer and pixel buffer
const zbuf = new Float32Array(PIXEL_W * PIXEL_H);
const pixels = new Uint8Array(PIXEL_W * PIXEL_H);
// Camera/view parameters
const camDist = 8;
const fov = 60;
// Rotation matrices
const rotX = t * 0.3;
const rotY = t * 0.5;
const cosX = Math.cos(rotX), sinX = Math.sin(rotX);
const cosY = Math.cos(rotY), sinY = Math.sin(rotY);
// Sample the wave surface
for (let u = -3; u <= 3; u += 0.08) {
for (let v = -3; v <= 3; v += 0.08) {
// Wave function - multiple sine waves create tsunami-like surface
const wave1 = Math.sin(u * 2 + t * 2) * 0.4;
const wave2 = Math.sin(v * 1.5 - t * 1.5) * 0.3;
const wave3 = Math.sin((u + v) * 1.2 + t) * 0.2;
const wave4 = Math.sin(u * 3 - t * 3) * 0.15 * Math.exp(-Math.abs(u - 1));
// Big tsunami wave
const tsunami = Math.exp(-Math.pow(u - 1.5 + Math.sin(t * 0.5), 2) * 0.5) *
Math.sin(u * 4 + t * 2) * 0.8;
let y = wave1 + wave2 + wave3 + wave4 + tsunami;
let x = u;
let z = v;
// Rotate around X axis
let y1 = y * cosX - z * sinX;
let z1 = y * sinX + z * cosX;
// Rotate around Y axis
let x2 = x * cosY + z1 * sinY;
let z2 = -x * sinY + z1 * cosY;
let y2 = y1;
// Project to screen
const depth = z2 + camDist;
if (depth <= 0.1) continue;
const scale = fov / depth;
const screenX = Math.floor(PIXEL_W / 2 + x2 * scale * 3);
const screenY = Math.floor(PIXEL_H / 2 - y2 * scale * 3);
if (screenX >= 0 && screenX < PIXEL_W && screenY >= 0 && screenY < PIXEL_H) {
const idx = screenY * PIXEL_W + screenX;
const invZ = 1 / depth;
if (invZ > zbuf[idx]) {
zbuf[idx] = invZ;
// Calculate normal for lighting
const du = 0.01;
const dv = 0.01;
const h0 = wave1 + wave2 + wave3 + wave4 + tsunami;
const hu = Math.sin((u + du) * 2 + t * 2) * 0.4 +
Math.sin(v * 1.5 - t * 1.5) * 0.3 +
Math.sin((u + du + v) * 1.2 + t) * 0.2;
const hv = Math.sin(u * 2 + t * 2) * 0.4 +
Math.sin((v + dv) * 1.5 - t * 1.5) * 0.3 +
Math.sin((u + v + dv) * 1.2 + t) * 0.2;
const nx = -(hu - h0) / du;
const ny = 1;
const nz = -(hv - h0) / dv;
const len = Math.sqrt(nx * nx + ny * ny + nz * nz);
// Light direction
const lx = 0.5, ly = 0.8, lz = -0.3;
const lLen = Math.sqrt(lx * lx + ly * ly + lz * lz);
const light = Math.max(0, (nx * lx + ny * ly + nz * lz) / (len * lLen));
pixels[idx] = light > 0.3 ? 1 : 0;
}
}
}
}
// Convert pixel buffer to braille characters
let output = '';
for (let cy = 0; cy < HEIGHT; cy++) {
for (let cx = 0; cx < WIDTH; cx++) {
let dots = 0;
// Sample 2x4 pixel block for this braille character
for (let dy = 0; dy < 4; dy++) {
for (let dx = 0; dx < 2; dx++) {
const px = cx * 2 + dx;
const py = cy * 4 + dy;
const idx = py * PIXEL_W + px;
if (pixels[idx]) {
// Map to braille dot position
const dotIdx = dy + (dx * 4);
const braillePos = [0, 1, 2, 6, 3, 4, 5, 7][dotIdx];
dots |= dotMap[braillePos];
}
}
}
output += toBraille(dots);
}
output += '\n';
}
setFrame(output);
};
const interval = setInterval(renderFrame, 50);
renderFrame();
return () => clearInterval(interval);
}, []);
return (
<div className="min-h-screen bg-black flex flex-col items-center justify-center p-4">
<pre
className="font-mono text-cyan-300 leading-none"
style={{
fontSize: '10px',
letterSpacing: '0px',
lineHeight: '10px',
textShadow: '0 0 10px rgba(0, 255, 255, 0.5)'
}}
>
{frame}
</pre>
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment