Skip to content

Commit d30c658

Browse files
committed
termbit
1 parent 8289d3c commit d30c658

File tree

4 files changed

+175
-1
lines changed

4 files changed

+175
-1
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@
1111
[Circles Physics](https://python273.github.io/web-sketches/circles-physics/)
1212

1313
[Scratch the surface](https://python273.github.io/web-sketches/scratch-the-surface/)
14+
15+
[termbit](https://python273.github.io/web-sketches/termbit/termbit.html)
File renamed without changes.

scratch-the-surface/scratch-the-surface.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
</head>
1818
<body>
1919
<canvas id="c" style="background-color: rgb(0, 0, 0);"></canvas>
20-
<script src="twgl.min.js"></script>
20+
<script src="../libs/twgl.min.js"></script>
2121
<script id="vs" type="text/plain">
2222
attribute vec4 position;
2323
uniform vec2 canvasAspect;

termbit/termbit.html

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>termbit</title>
7+
<style>
8+
* { margin: 0; padding: 0; }
9+
body { background: #111; overflow: hidden; }
10+
</style>
11+
<script src="../libs/twgl.min.js"></script>
12+
</head>
13+
<body>
14+
<script>
15+
// https://x.com/corvus_ikshana/status/1827636589817692391
16+
// https://codesandbox.io/p/sandbox/gallant-flower-yrv666
17+
18+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$&()*+-:;<=>?@[]^{}~';
19+
const fontSize = 11;
20+
const fontPadding = 2;
21+
// window.devicePixelRatio = 1.0;
22+
23+
const canvas = document.createElement('canvas');
24+
document.body.appendChild(canvas);
25+
const gl = canvas.getContext('webgl2');
26+
27+
// Create char atlas
28+
const textCanvas = document.createElement('canvas');
29+
textCanvas.width = chars.length * fontSize * window.devicePixelRatio;
30+
textCanvas.height = fontSize * window.devicePixelRatio;
31+
const textCtx = textCanvas.getContext('2d');
32+
textCanvas.style = 'image-rendering: crisp-edges; background: #000;';
33+
// document.body.appendChild(textCanvas)
34+
textCtx.setTransform(
35+
window.devicePixelRatio, 0,
36+
0, window.devicePixelRatio,
37+
0, 0
38+
);
39+
textCtx.font = `${fontSize}px monospace`;
40+
textCtx.fillStyle = '#fff';
41+
textCtx.textBaseline = 'top';
42+
chars.split('').forEach((char, i) => {
43+
textCtx.fillText(char, fontPadding + i * fontSize, 1);
44+
});
45+
const texture = twgl.createTexture(gl, {
46+
src: textCanvas,
47+
min: gl.LINEAR,
48+
mag: gl.LINEAR,
49+
wrap: gl.CLAMP_TO_EDGE,
50+
});
51+
52+
const vs = `#version 300 es
53+
in vec4 position;
54+
in vec2 texcoord;
55+
56+
uniform float time;
57+
uniform vec2 resolution;
58+
uniform int cols;
59+
60+
out vec2 v_texcoord;
61+
62+
void main() {
63+
vec2 center = resolution * 0.5;
64+
65+
int yi = gl_InstanceID / cols;
66+
float y = float(yi);
67+
float x = float(gl_InstanceID - yi*cols);
68+
float charIndex = mod(
69+
float(gl_InstanceID) + floor(42.0 * floor(time/5000.0 + sin(float(gl_InstanceID)))),
70+
${chars.length}.
71+
);
72+
73+
vec2 instancePos = vec2(x * ${fontSize}., y * ${fontSize}.);
74+
vec2 relPos = instancePos - center;
75+
76+
float offset = sin(length(relPos) * 0.03 + time/100000.0) * 3.14159;
77+
mat2 rotation = mat2(
78+
cos(offset), -sin(offset),
79+
sin(offset), cos(offset)
80+
);
81+
82+
vec2 rotatedInstancePos = rotation * relPos + center;
83+
84+
vec2 vertPos = (position.xy + vec2(1.0)) * vec2(${fontSize}. * 0.5);
85+
vec2 rotatedVertPos = rotation * vertPos;
86+
87+
vec2 finalPos = rotatedInstancePos + rotatedVertPos;
88+
89+
vec2 clipSpace = (finalPos / resolution) * 2.0 - 1.0;
90+
gl_Position = vec4(clipSpace, 0, 1);
91+
v_texcoord = vec2((charIndex + texcoord.x) / ${chars.length}., texcoord.y);
92+
}
93+
`;
94+
95+
const fs = `#version 300 es
96+
precision highp float;
97+
98+
in vec2 v_texcoord;
99+
out vec4 fragColor;
100+
101+
uniform sampler2D u_texture;
102+
103+
void main() {
104+
vec4 color = texture(u_texture, v_texcoord);
105+
fragColor = color;
106+
}
107+
`;
108+
109+
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
110+
111+
const arrays = {
112+
position: {
113+
numComponents: 2,
114+
data: new Float32Array([
115+
-1, -1,
116+
1, -1,
117+
1, 1,
118+
-1, -1,
119+
1, 1,
120+
-1, 1,
121+
])
122+
},
123+
texcoord: {
124+
numComponents: 2,
125+
data: new Float32Array([
126+
0, 1,
127+
1, 1,
128+
1, 0,
129+
0, 1,
130+
1, 0,
131+
0, 0,
132+
])
133+
}
134+
};
135+
136+
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
137+
const vertexArrayInfo = twgl.createVertexArrayInfo(gl, programInfo, bufferInfo);
138+
139+
function render(time) {
140+
canvas.style.width = `${document.documentElement.clientWidth}px`;
141+
canvas.style.height = `${document.documentElement.clientHeight}px`;
142+
twgl.resizeCanvasToDisplaySize(gl.canvas, window.devicePixelRatio);
143+
144+
const [w, h] = [
145+
gl.canvas.width / window.devicePixelRatio,
146+
gl.canvas.height / window.devicePixelRatio
147+
];
148+
const [cols, rows] = [Math.floor(w / fontSize), Math.floor(h / fontSize)];
149+
150+
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
151+
gl.enable(gl.BLEND);
152+
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
153+
gl.clearColor(0, 0.094, 0.42, 1.0);
154+
gl.clear(gl.COLOR_BUFFER_BIT);
155+
156+
gl.useProgram(programInfo.program);
157+
twgl.setBuffersAndAttributes(gl, programInfo, vertexArrayInfo);
158+
twgl.setUniforms(programInfo, {
159+
time,
160+
u_texture: texture,
161+
resolution: [w, h],
162+
cols,
163+
});
164+
const instanceCount = cols * rows;
165+
twgl.drawBufferInfo(gl, vertexArrayInfo, gl.TRIANGLES, 6, 0, instanceCount);
166+
requestAnimationFrame(render);
167+
}
168+
169+
requestAnimationFrame(render);
170+
</script>
171+
</body>
172+
</html>

0 commit comments

Comments
 (0)