orchard
mirrorEvery site I host, in one repo, along with the Cloudflare Tunnel and Caddy that front them. It's all Go, Vite, and SQLite, and it runs on a desktop at home with nothing listening on an inbound port.
blogbuncaddycloudflare-tunneldockergogolanghomelabhtml-templatemonorepoself-hostedseosqlitestatic-sitetypstuptime-monitoringviteweb-analytics
1// Drifting points that draw a line to every neighbour within 150px, brighter
2// the closer they are, so constellations build and dissolve on their own.
3
4export const constellations = (cvs, { numStars = 50 } = {}) => {
5 const ctx = cvs.getContext("2d");
6 const starDistance = 150;
7
8 const resize = () => {
9 cvs.width = cvs.offsetWidth;
10 cvs.height = cvs.offsetHeight;
11 };
12 resize();
13 window.addEventListener("resize", resize);
14
15 let stars = [];
16 for (let i = 0; i < numStars; i++) {
17 stars.push({
18 loc: [cvs.width * Math.random(), cvs.height * Math.random()],
19 dir: [Math.random() > 0.5 ? 1 : -1, Math.random() > 0.5 ? 1 : -1],
20 });
21 }
22
23 let frame = null;
24 let running = false;
25
26 const draw = () => {
27 ctx.clearRect(0, 0, cvs.width, cvs.height);
28
29 stars.forEach((star) => {
30 ctx.beginPath();
31 ctx.arc(star.loc[0], star.loc[1], 2, 0, 2 * Math.PI);
32 ctx.fillStyle = "rgb(255, 255, 255)";
33 ctx.fill();
34 ctx.closePath();
35
36 stars.forEach((closeStar) => {
37 const distance = Math.hypot(
38 star.loc[0] - closeStar.loc[0],
39 star.loc[1] - closeStar.loc[1]
40 );
41 if (distance >= starDistance) return;
42 ctx.beginPath();
43 ctx.moveTo(star.loc[0], star.loc[1]);
44 ctx.lineTo(closeStar.loc[0], closeStar.loc[1]);
45 ctx.strokeStyle = `rgba(255, 255, 255, ${
46 (starDistance - distance) / starDistance
47 })`;
48 ctx.stroke();
49 ctx.closePath();
50 });
51 });
52
53 stars.forEach((star) => {
54 if (star.loc[0] < 0) star.dir[0] = 1;
55 else if (star.loc[0] > cvs.width) star.dir[0] = -1;
56 if (star.loc[1] < 0) star.dir[1] = 1;
57 else if (star.loc[1] > cvs.height) star.dir[1] = -1;
58
59 star.loc[0] += star.dir[0] * 0.5;
60 star.loc[1] += star.dir[1] * 0.5;
61 });
62
63 if (running) frame = window.requestAnimationFrame(draw);
64 };
65
66 return {
67 start() {
68 if (running) return;
69 running = true;
70 frame = window.requestAnimationFrame(draw);
71 },
72 stop() {
73 running = false;
74 if (frame) window.cancelAnimationFrame(frame);
75 frame = null;
76 },
77 };
78};