PageSourceSearch

https://self0s.org/resonance.js

js self0s.org collected 2026-10-02 02:04:47 UTC 7,073 bytes, 150 lines download raw bytes

1// selfOS — resonance: everything is a birth.
2// One continuous loop, each transition a birth into a new axis. Two circles
3// converge and the lines bolden into a 12-spoke wheel (the spokes streak inward
4// from rim to hub); it tilts into depth and spins, decelerating to edge-on — a
5// line — which contracts to a point where the star is born in a flash; the
6// star's rays reach out and become the circle of the next chapter. Candlelight
7// on black, additive. (Caption: "The awareness to know what you want, and the
8// focus to achieve it.")
9(function () {
10  const canvas = document.getElementById("resonanceCanvas");
11  if (!canvas) return;
12  const ctx = canvas.getContext("2d");
13  const C = (a) => "rgba(255,248,220," + a + ")";
14  const reduce = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
15  const TAU = Math.PI * 2;
16  let W = 0, H = 0, DPR = 1;
17
18  function resize() {
19    DPR = Math.min(window.devicePixelRatio || 1, 2);
20    const r = canvas.getBoundingClientRect();
21    W = Math.max(1, r.width); H = Math.max(1, r.height);
22    canvas.width = Math.round(W * DPR);
23    canvas.height = Math.round(H * DPR);
24  }
25  const clamp = (x) => (x < 0 ? 0 : x > 1 ? 1 : x);
26  const ease = (x) => { x = clamp(x); return x * x * (3 - 2 * x); };
27  const ss = (x) => { x = clamp(x); return x * x * x * (x * (x * 6 - 15) + 10); };
28  const loc = (t, a, b) => clamp((t - a) / (b - a), 0, 1);
29
30  function ringE(cx, cy, rx, ry, rot, a, lw, glow) {
31    if (a <= 0.004) return; rx = Math.max(0.1, rx); ry = Math.max(0.1, ry);
32    ctx.beginPath(); ctx.ellipse(cx, cy, rx, ry, rot, 0, TAU);
33    ctx.strokeStyle = C(a); ctx.lineWidth = lw; ctx.shadowColor = C(glow); ctx.shadowBlur = 11; ctx.stroke();
34  }
35  function seg(x1, y1, x2, y2, a, lw) {
36    if (a <= 0.01) return;
37    ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2);
38    ctx.strokeStyle = C(a); ctx.lineWidth = lw; ctx.lineCap = "round"; ctx.shadowColor = C(a * 0.8); ctx.shadowBlur = 6; ctx.stroke();
39  }
40  function dot(x, y, r, a) {
41    if (a <= 0.01) return;
42    ctx.save(); ctx.fillStyle = C(Math.min(1, a)); ctx.shadowColor = C(Math.min(1, a)); ctx.shadowBlur = r * 3.4;
43    ctx.beginPath(); ctx.arc(x, y, r, 0, TAU); ctx.fill(); ctx.restore();
44  }
45  // 4-point + sparkle flash: fine points, tiny core
46  function sparkle(cx, cy, len, coreR, a) {
47    if (a <= 0.01 || len <= 0.5) return;
48    ctx.save(); ctx.globalCompositeOperation = "lighter";
49    ctx.fillStyle = C(Math.min(1, a)); ctx.shadowColor = C(Math.min(1, a)); ctx.shadowBlur = coreR * 5;
50    const w = Math.max(0.5, coreR * 0.45);
51    for (let i = 0; i < 4; i++) {
52      ctx.save(); ctx.translate(cx, cy); ctx.rotate(i * Math.PI / 2);
53      ctx.beginPath(); ctx.moveTo(0, -len); ctx.lineTo(w, 0); ctx.lineTo(0, coreR * 0.5); ctx.lineTo(-w, 0); ctx.closePath(); ctx.fill();
54      ctx.restore();
55    }
56    ctx.globalAlpha = 0.45;
57    for (let i = 0; i < 4; i++) {
58      ctx.save(); ctx.translate(cx, cy); ctx.rotate((i + 0.5) * Math.PI / 2);
59      ctx.beginPath(); ctx.moveTo(0, -len * 0.5); ctx.lineTo(w * 0.7, 0); ctx.lineTo(0, coreR * 0.4); ctx.lineTo(-w * 0.7, 0); ctx.closePath(); ctx.fill();
60      ctx.restore();
61    }
62    ctx.globalAlpha = 1;
63    ctx.beginPath(); ctx.arc(cx, cy, coreR, 0, TAU); ctx.fill();
64    ctx.restore();
65  }
66
67  const RA = 0.30, SPIN = 2.5, PULSEK = 1.7, SPINEND = 0.55;
68
69  function render(t) {
70    ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
71    ctx.clearRect(0, 0, W, H);
72    ctx.globalCompositeOperation = "lighter";
73    const cx = W / 2, cy = H / 2, M = Math.min(W, H);
74    const Rmax = M * 0.46, Rmeet = Rmax * 0.60, sR = M * 0.052;
75
76    const sp = ss(loc(t, 0.12, SPINEND));
77    const psi = TAU * SPIN * sp;
78    const tilt = (Math.PI / 2) * ease(loc(t, 0.12, SPINEND));
79    const pulse = ease(loc(t, 0.50, 0.565)) * (1 - ease(loc(t, 0.565, 0.66))) * PULSEK;
80    const contract = ease(loc(t, 0.55, 0.72));
81
82    const conv = ease(loc(t, 0, 0.12));
83    let R = (t < 0.12) ? (Rmax - (Rmax - Rmeet) * conv) : Rmeet * (1 - contract);
84    R *= (1 + 0.06 * pulse);
85    const ry = R * Math.cos(tilt);
86
87    // birth of the wheel: the lines breathe bolder as the circles meet
88    const present = ease(loc(t, 0.10, 0.24));
89    const swell = ease(loc(t, 0.06, 0.12)) * (1 - ease(loc(t, 0.12, 0.27)));
90    const lwMain = 1.1 + 0.5 * present + 0.8 * swell;
91    const aMain = RA + 0.16 * swell;
92    const glowMain = 0.6 + 0.25 * swell;
93
94    if (t < 0.74) {
95      if (ry > 0.8) ringE(cx, cy, R, ry, 0, aMain, lwMain, glowMain);
96      else seg(cx - R, cy, cx + R, cy, RA + 0.6 * pulse, 1.6 + 1.7 * pulse);
97    }
98    // inner converging circle — meets the outer, boldens with it, then merges away
99    if (t < 0.16) {
100      const e = conv, innerFade = 1 - ease(loc(t, 0.105, 0.15));
101      ringE(cx, cy, Rmeet * e, Rmeet * e, 0, aMain * e * innerFade, 1.1 + 0.8 * swell, glowMain);
102    }
103    // spokes streak inward from the rim to the hub — birth of the radial axis
104    if (t >= 0.12 && t < 0.74) {
105      const grow = ease(loc(t, 0.13, 0.225));
106      const spokeA = 0.42 * ease(loc(t, 0.13, 0.24));
107      const dotFade = 1 - ease(loc(t, 0.46, SPINEND));
108      for (let k = 0; k < 12; k++) {
109        const b = (k / 12) * TAU + psi;
110        const ox = cx + R * Math.cos(b), oy = cy + ry * Math.sin(b);
111        const gx = ox + grow * (cx - ox), gy = oy + grow * (cy - oy);
112        const depth = 0.5 + 0.5 * Math.sin(b);
113        const sel = Math.pow(Math.max(0, Math.sin(b)), 4);
114        seg(ox, oy, gx, gy, spokeA * (0.2 + 0.35 * depth) + sel * 0.5, sel > 0.5 ? 2.4 : 1.2);
115        if (grow > 0.94 && sel > 0.6) dot(ox, oy, sR * 0.26, sel * 0.6 * dotFade);
116      }
117    }
118
119    // birth of the star at the contraction point; its rays reach out and become the circle
120    if (t >= 0.62) {
121      const boom = ease(loc(t, 0.655, 0.73)) * (1 - ease(loc(t, 0.80, 0.93)));
122      const ex = ease(loc(t, 0.73, 0.98));
123      const len = sR * 1.15 + (Rmax * 0.9 - sR * 1.15) * ex;   // rays reach out to the ring
124      sparkle(cx, cy, len, sR * 0.12, Math.min(1, boom + 0.1));
125      if (t >= 0.78) {
126        const x = ease(loc(t, 0.78, 1.0));
127        const rbs = ease(loc(t, 0.78, 0.86)) * (1 - ease(loc(t, 0.86, 1.0)));
128        ringE(cx, cy, x * Rmax, x * Rmax, 0, RA * (0.3 + 0.7 * x) + 0.18 * rbs, 1.0 + 0.7 * rbs, 0.6 + 0.3 * rbs);
129      }
130    }
131  }
132
133  const T = 9.5;
134  let t0 = null, raf = null;
135  function loop(ms) {
136    if (t0 === null) t0 = ms;
137    render(((ms - t0) / 1000 % T) / T);
138    raf = requestAnimationFrame(loop);
139  }
140  function startA() { if (raf == null && !reduce) raf = requestAnimationFrame(loop); }
141  function stopA() { if (raf != null) { cancelAnimationFrame(raf); raf = null; } }
142  function still() { render(0.30); }   // a representative wheel for reduced motion
143
144  resize();
145  window.addEventListener("resize", () => { resize(); if (reduce) still(); });
146  if (reduce) { still(); return; }
147  if ("IntersectionObserver" in window) {
148    new IntersectionObserver((es) => es.forEach((e) => (e.isIntersecting ? startA() : stopA())), { threshold: 0.05 }).observe(canvas);
149  } else startA();
150})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.