this repo has no description
1<!DOCTYPE html>
2<html>
3 <head>
4 <meta charset="utf-8">
5 <style>
6 * {
7 margin: 0;
8 padding: 0;
9 }
10 html, body {
11 overflow: hidden;
12 }
13 canvas {
14 display: block;
15 image-rendering: pixelated;
16 }
17 </style>
18 </head>
19 <body>
20 <canvas id="c" width="88" height="31"></canvas>
21 <script>
22 const canvas = document.getElementById("c"), ctx = canvas.getContext("2d");
23
24 const BUTTON_W = 88, BUTTON_H = 31;
25 const pixelBuffer = new Uint8ClampedArray(BUTTON_W * BUTTON_H * 4);
26
27 let time = 0;
28
29 const bayer = [0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5].map((v) => v / 16);
30 function dither(brightness, x, y) {
31 const threshold = bayer[(y & 3) * 4 + (x & 3)];
32 return brightness > threshold ? 255 : 0;
33 }
34
35 function wave(x, y, time) {
36 const normX = x / BUTTON_W;
37 const normY = y / BUTTON_H;
38
39 const depth = normY;
40 const depth2 = depth * depth;
41
42 const frequency = 7 + 18 * depth2;
43 const scrollSpeed = time * (0.5 + depth2 * 1.4);
44
45 const w1 = Math.sin(normX * frequency + scrollSpeed + Math.sin(normX * 3.1 - time * 0.4) * 1.5);
46 const w2 = Math.sin(
47 normX * frequency * 0.6 - scrollSpeed * 1.2 + Math.cos(normX * 5.7 + time * 0.5) * 1.0,
48 );
49 const w3 = Math.sin(normX * frequency * 1.4 + scrollSpeed * 0.8 + normY * 5 - time);
50 const w4 = Math.cos(
51 normX * frequency * 0.35 - scrollSpeed * 0.6 + Math.sin(normY * 8 + time * 0.7) * 0.8,
52 );
53
54 const weightedBrightness = w1 * 0.35 + w2 * 0.3 + w3 * 0.2 + w4 * 0.15;
55 const foamCrest = Math.max(0, weightedBrightness - 0.45) * 4 * depth;
56 const contrastScaled = weightedBrightness * (0.25 + depth2 * 0.75) + 0.4;
57
58 return Math.max(0, Math.min(1, contrastScaled + foamCrest));
59 }
60
61 const glyphs = {
62 k: ["1000", "1001", "1010", "1100", "1010", "1001"],
63 y: ["0000", "1001", "1001", "0111", "0001", "0110"],
64 u: ["0000", "1001", "1001", "1001", "1001", "0111"],
65 ".": ["0", "0", "0", "0", "0", "1"],
66 r: ["0000", "1011", "1100", "1000", "1000", "1000"],
67 e: ["0000", "0110", "1001", "1111", "1000", "0110"],
68 };
69
70 const charWidths = { k: 4, y: 4, u: 4, ".": 1, r: 4, e: 4 };
71 const labelChars = ["k", "y", "u", ".", "r", "e"];
72
73 const TAG_W = 30, TAG_H = 11;
74 const TAG_X = BUTTON_W - TAG_W;
75 const TAG_Y = BUTTON_H - TAG_H;
76 const TEXT_X = TAG_X + 2;
77 const TEXT_Y = TAG_Y + 2;
78
79 function drawTag() {
80 ctx.fillStyle = "#000";
81 ctx.fillRect(TAG_X, TAG_Y, TAG_W, TAG_H);
82
83 ctx.fillStyle = "#fff";
84 let cursorX = TEXT_X;
85 labelChars.forEach((char) => {
86 glyphs[char].forEach((row, rowIndex) => {
87 [...row].forEach((bit, bitIndex) => {
88 if (bit === "1") ctx.fillRect(cursorX + bitIndex, TEXT_Y + rowIndex, 1, 1);
89 });
90 });
91 cursorX += charWidths[char] + 1;
92 });
93 }
94
95 function drawFrame() {
96 time += 0.038;
97 for (let y = 0; y < BUTTON_H; y++) {
98 for (let x = 0; x < BUTTON_W; x++) {
99 const brightness = dither(wave(x, y, time), x, y);
100 const bufferIndex = (y * BUTTON_W + x) * 4;
101 pixelBuffer[bufferIndex] = brightness;
102 pixelBuffer[bufferIndex + 1] = brightness;
103 pixelBuffer[bufferIndex + 2] = brightness;
104 pixelBuffer[bufferIndex + 3] = 255;
105 }
106 }
107
108 ctx.putImageData(new ImageData(pixelBuffer, BUTTON_W, BUTTON_H), 0, 0);
109 drawTag();
110
111 requestAnimationFrame(drawFrame);
112 }
113
114 drawFrame();
115 </script>
116 </body>
117</html>