Upload files to "/"

This commit is contained in:
2026-05-18 20:14:54 +00:00
commit 0e457f3c40
5 changed files with 7575 additions and 0 deletions

2
README.md Normal file
View File

@@ -0,0 +1,2 @@
# FrontierAlgorithmics_Website
Website for Frontier Algorithmics

905
Shader Wallpapers.html Normal file
View File

@@ -0,0 +1,905 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Aurora · Shader Wallpapers</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet">
<style>
:root {
--ink: #ffffff;
--dim: rgba(255,255,255,0.55);
--faint: rgba(255,255,255,0.25);
--mono: 'JetBrains Mono', ui-monospace, monospace;
--sans: 'Inter', system-ui, sans-serif;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { width: 100%; height: 100%; overflow: hidden; background: #000; color: var(--ink); font-family: var(--sans); }
canvas#gl { position: fixed; inset: 0; width: 100%; height: 100%; display: block; cursor: crosshair; }
/* ── TOP-LEFT INDEX ── */
.top-left {
position: fixed; top: 28px; left: 32px; z-index: 10;
display: flex; flex-direction: column; gap: 6px;
pointer-events: none;
mix-blend-mode: difference;
}
.top-left .kicker {
font-family: var(--mono); font-size: 10px;
letter-spacing: 3px; text-transform: uppercase;
color: var(--dim);
}
.top-left .title {
font-family: var(--sans); font-weight: 300;
font-size: clamp(28px, 3.4vw, 44px);
letter-spacing: -1.2px; line-height: 1;
color: var(--ink);
}
.top-left .title .accent { font-style: italic; font-weight: 400; }
.top-left .meta {
font-family: var(--mono); font-size: 10px;
letter-spacing: 1.5px; color: var(--faint);
margin-top: 14px;
}
/* ── TOP-RIGHT INFO ── */
.top-right {
position: fixed; top: 28px; right: 32px; z-index: 10;
display: flex; flex-direction: column; gap: 8px; align-items: flex-end;
mix-blend-mode: difference; pointer-events: none;
}
.badge {
display: inline-flex; align-items: center; gap: 8px;
font-family: var(--mono); font-size: 10px;
letter-spacing: 2px; text-transform: uppercase;
color: var(--ink);
padding: 6px 12px;
border: 0.5px solid rgba(255,255,255,0.3);
background: rgba(0,0,0,0.001);
}
.badge .dot {
width: 6px; height: 6px; border-radius: 50%;
background: #66ffaa;
box-shadow: 0 0 8px #66ffaa;
animation: pulse 1.6s ease-in-out infinite;
}
@keyframes pulse { 50% { opacity: 0.3; } }
.hint {
font-family: var(--mono); font-size: 10px;
color: var(--dim); letter-spacing: 1px;
}
.hint kbd {
display: inline-block;
padding: 2px 6px;
border: 0.5px solid rgba(255,255,255,0.4);
font-family: var(--mono); font-size: 9px;
margin: 0 2px; color: var(--ink);
}
/* ── BOTTOM DOCK / SWITCHER ── */
.dock {
position: fixed; bottom: 28px; left: 50%;
transform: translateX(-50%);
z-index: 10;
display: flex; gap: 1px;
background: rgba(255,255,255,0.08);
padding: 1px;
backdrop-filter: blur(20px) saturate(140%);
-webkit-backdrop-filter: blur(20px) saturate(140%);
border: 0.5px solid rgba(255,255,255,0.18);
border-radius: 2px;
}
.dock-btn {
background: rgba(0,0,0,0.55);
border: none;
color: var(--dim);
font-family: var(--mono);
font-size: 10px;
letter-spacing: 2.5px;
text-transform: uppercase;
padding: 14px 22px;
cursor: pointer;
transition: color 0.25s, background 0.25s;
position: relative;
display: flex; align-items: center; gap: 10px;
}
.dock-btn::before {
content: ''; display: block; width: 6px; height: 6px;
background: currentColor; opacity: 0.5;
transition: opacity 0.25s, transform 0.25s;
}
.dock-btn[data-shape="circle"]::before { border-radius: 50%; }
.dock-btn[data-shape="diamond"]::before { transform: rotate(45deg); }
.dock-btn[data-shape="bar"]::before { width: 10px; height: 2px; }
.dock-btn[data-shape="ring"]::before { background: transparent; border: 1px solid currentColor; border-radius: 50%; }
.dock-btn[data-shape="cross"]::before {
width: 8px; height: 8px;
background: linear-gradient(currentColor, currentColor) center/100% 1px no-repeat,
linear-gradient(currentColor, currentColor) center/1px 100% no-repeat;
}
.dock-btn:hover { color: var(--ink); background: rgba(0,0,0,0.7); }
.dock-btn:hover::before { opacity: 1; }
.dock-btn.active { color: var(--ink); background: rgba(0,0,0,0.85); }
.dock-btn.active::before { opacity: 1; transform: scale(1.3); }
.dock-btn[data-shape="diamond"].active::before { transform: rotate(45deg) scale(1.3); }
/* ── BOTTOM-LEFT STATS ── */
.bottom-left {
position: fixed; bottom: 28px; left: 32px; z-index: 10;
display: flex; flex-direction: column; gap: 6px;
font-family: var(--mono); font-size: 10px;
letter-spacing: 1.5px;
mix-blend-mode: difference;
color: var(--dim);
pointer-events: none;
}
.bottom-left .row { display: flex; gap: 14px; }
.bottom-left .row .k { color: var(--faint); }
.bottom-left .row .v { color: var(--ink); min-width: 50px; }
/* ── BOTTOM-RIGHT FPS / DESC ── */
.bottom-right {
position: fixed; bottom: 28px; right: 32px; z-index: 10;
text-align: right; max-width: 320px;
mix-blend-mode: difference;
pointer-events: none;
}
.desc-name {
font-family: var(--sans); font-weight: 500;
font-size: 13px; color: var(--ink);
letter-spacing: 0.5px; margin-bottom: 4px;
}
.desc-body {
font-family: var(--mono); font-size: 10px;
color: var(--dim); line-height: 1.6;
letter-spacing: 0.5px;
}
/* ── INTRO PULSE ON SWITCH ── */
.name-flash {
position: fixed; top: 50%; left: 50%;
transform: translate(-50%, -50%);
pointer-events: none; z-index: 20;
font-family: var(--sans); font-weight: 200;
font-size: clamp(60px, 12vw, 180px);
letter-spacing: -4px; color: rgba(255,255,255,0.95);
mix-blend-mode: difference;
opacity: 0; transition: opacity 0.4s, transform 0.6s;
}
.name-flash.show { opacity: 1; }
@media (max-width: 720px) {
.dock { flex-wrap: wrap; max-width: calc(100vw - 40px); }
.dock-btn { padding: 12px 14px; font-size: 9px; }
.top-right .hint, .bottom-right { display: none; }
.bottom-left { display: none; }
.top-left .title { font-size: 24px; }
}
</style>
</head>
<body>
<canvas id="gl"></canvas>
<!-- TOP LEFT -->
<div class="top-left">
<div class="kicker">Frontier Algorithmics · Lab</div>
<div class="title">Aurora<span class="accent"> · Field</span></div>
<div class="meta" id="meta-coord">42.0235 N · 11.1812 E</div>
</div>
<!-- TOP RIGHT -->
<div class="top-right">
<div class="badge"><span class="dot"></span><span>Live · GLSL</span></div>
<div class="hint">Move <kbd>cursor</kbd> · Click for ripple</div>
</div>
<!-- BOTTOM LEFT -->
<div class="bottom-left">
<div class="row"><span class="k">FPS</span><span class="v" id="fps">--</span></div>
<div class="row"><span class="k">CURSOR</span><span class="v" id="curs">0,0</span></div>
<div class="row"><span class="k">SHADER</span><span class="v" id="shname">aurora</span></div>
</div>
<!-- BOTTOM DOCK -->
<div class="dock" id="dock">
<button class="dock-btn active" data-shape="diamond" data-shader="aurora">Aurora</button>
<button class="dock-btn" data-shape="circle" data-shader="plasma">Plasma</button>
<button class="dock-btn" data-shape="ring" data-shader="mercury">Mercury</button>
<button class="dock-btn" data-shape="cross" data-shader="crystal">Crystal</button>
<button class="dock-btn" data-shape="bar" data-shader="waves">Waves</button>
</div>
<!-- BOTTOM RIGHT -->
<div class="bottom-right">
<div class="desc-name" id="desc-name">Aurora Field</div>
<div class="desc-body" id="desc-body">FBM-warped ribbon bands drifting over a deep-night gradient. Cursor displaces field, click discharges a luminous burst.</div>
</div>
<!-- FULLSCREEN FLASH -->
<div class="name-flash" id="flash">aurora</div>
<!-- ─────────── VERTEX SHADER (shared) ─────────── -->
<script id="vs" type="x-shader/x-vertex">
attribute vec2 a_pos;
void main() { gl_Position = vec4(a_pos, 0.0, 1.0); }
</script>
<!-- ─────────── COMMON FRAGMENT HEADER ─────────── -->
<script id="frag-header" type="x-shader/x-fragment">
precision highp float;
uniform vec2 uRes;
uniform float uTime;
uniform vec2 uMouse; // pixels, top-left origin
uniform vec2 uMouseSmooth; // eased mouse
uniform vec4 uClicks[6]; // xy = pos px, z = startTime, w = active flag
uniform float uShaderTime; // time since this shader was activated
// ── Hash & noise utilities ──
float hash21(vec2 p) {
p = fract(p * vec2(123.34, 456.21));
p += dot(p, p + 45.32);
return fract(p.x * p.y);
}
vec2 hash22(vec2 p) {
vec3 p3 = fract(vec3(p.xyx) * vec3(0.1031, 0.1030, 0.0973));
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.xx + p3.yz) * p3.zy);
}
float vnoise(vec2 p) {
vec2 i = floor(p), f = fract(p);
vec2 u = f*f*(3.0-2.0*f);
float a = hash21(i);
float b = hash21(i + vec2(1.0, 0.0));
float c = hash21(i + vec2(0.0, 1.0));
float d = hash21(i + vec2(1.0, 1.0));
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
}
float fbm(vec2 p) {
float v = 0.0, a = 0.5;
mat2 rot = mat2(0.8, -0.6, 0.6, 0.8);
for (int i = 0; i < 5; i++) {
v += a * vnoise(p);
p = rot * p * 2.02;
a *= 0.5;
}
return v;
}
// Click ripple decay (returns 0..1, peaks then decays)
float clickRipple(vec2 frag, vec4 click) {
if (click.w < 0.5) return 0.0;
float age = uTime - click.z;
if (age < 0.0 || age > 2.4) return 0.0;
float d = distance(frag, click.xy);
float radius = age * 600.0;
float thickness = 60.0 + age * 80.0;
float ring = exp(-pow((d - radius) / thickness, 2.0));
return ring * exp(-age * 1.3);
}
float clickEnergy(vec2 frag) {
float e = 0.0;
e += clickRipple(frag, uClicks[0]);
e += clickRipple(frag, uClicks[1]);
e += clickRipple(frag, uClicks[2]);
e += clickRipple(frag, uClicks[3]);
e += clickRipple(frag, uClicks[4]);
e += clickRipple(frag, uClicks[5]);
return e;
}
</script>
<!-- ─────────── 1. AURORA ─────────── -->
<script id="fs-aurora" type="x-shader/x-fragment">
void main() {
vec2 frag = gl_FragCoord.xy;
vec2 uv = frag / uRes.xy;
vec2 m = uMouseSmooth / uRes.xy;
// Deep-night background gradient (top: cold violet, bottom: black-teal)
vec3 sky = mix(
vec3(0.005, 0.01, 0.06),
vec3(0.04, 0.02, 0.10),
pow(1.0 - uv.y, 1.5)
);
// Stars
float sH = hash21(floor(frag));
float starMask = step(0.9985, sH);
float twinkle = 0.5 + 0.5 * sin(uTime * 2.0 + sH * 200.0);
sky += vec3(1.0) * starMask * twinkle * (1.0 - smoothstep(0.0, 0.55, uv.y));
// Aurora bands — multiple stacked, animated, mouse-displaced
vec3 aurora = vec3(0.0);
float mouseInf = exp(-distance(uv, m) * 2.2);
for (int i = 0; i < 5; i++) {
float fi = float(i);
float seed = fi * 17.3;
// Domain warp using FBM
vec2 q = vec2(uv.x * 1.6 + uTime * 0.08 + seed, uv.y * 0.9);
float n1 = fbm(q + vec2(0.0, uTime * 0.07));
float n2 = fbm(q * 1.5 + n1 + vec2(uTime * 0.05, 0.0));
// Base band centerline (varies with x)
float center = 0.18 + fi * 0.16 + sin(uTime * 0.25 + fi * 1.3) * 0.04;
center += (n1 - 0.5) * 0.28;
// Mouse pushes nearby bands upward
center += mouseInf * (m.y - center) * 0.25;
// Vertical profile: thin band that softens at top
float band = exp(-pow((uv.y - center) * (9.0 - fi * 0.8), 2.0));
// Vertical streak detail (the iconic vertical strands)
float strand = fbm(vec2(q.x * 18.0 + uTime * 0.2, q.y * 4.0 + n2 * 2.0));
strand = smoothstep(0.35, 0.85, strand);
band *= 0.55 + 0.7 * strand;
// Color shifts across bands and across x
vec3 cA = vec3(0.10, 0.95, 0.55); // emerald
vec3 cB = vec3(0.20, 0.45, 1.00); // cyan-blue
vec3 cC = vec3(0.90, 0.30, 0.85); // magenta
float mixA = sin(uTime * 0.3 + fi * 1.5 + uv.x * 1.2) * 0.5 + 0.5;
float mixB = cos(uTime * 0.22 + fi * 0.9) * 0.5 + 0.5;
vec3 bandCol = mix(mix(cA, cB, mixA), cC, mixB * 0.35);
aurora += band * bandCol * (1.1 - fi * 0.08);
}
// Reflective glow on lower edge
float ground = smoothstep(0.0, 0.25, uv.y);
aurora *= ground;
// Click ripple: brightens region, slight chromatic separation
float ce = clickEnergy(frag);
vec3 burst = vec3(0.5, 1.2, 1.5) * ce;
vec3 col = sky + aurora * 1.15 + burst;
// Subtle vignette
vec2 vuv = uv - 0.5;
col *= 1.0 - dot(vuv, vuv) * 0.6;
gl_FragColor = vec4(col, 1.0);
}
</script>
<!-- ─────────── 2. PLASMA SUN ─────────── -->
<script id="fs-plasma" type="x-shader/x-fragment">
void main() {
vec2 frag = gl_FragCoord.xy;
vec2 uv = (frag - 0.5 * uRes) / min(uRes.x, uRes.y);
vec2 m = (uMouseSmooth - 0.5 * uRes) / min(uRes.x, uRes.y);
// Orb center follows mouse, with slow return-to-center
vec2 center = m * 0.7;
vec2 p = uv - center;
float r = length(p);
float a = atan(p.y, p.x);
// Domain-warped corona using polar coords
float corona = 0.0;
for (int i = 0; i < 4; i++) {
float fi = float(i);
float swirl = fbm(vec2(a * (3.0 + fi) + uTime * (0.2 + fi * 0.1), r * 4.0 - uTime * 0.4));
corona += swirl / (fi + 1.0);
}
corona /= 1.8;
// Core
float core = exp(-pow(r * 3.5, 2.0));
// Outer corona pulsing
float outer = exp(-r * 2.8) * (0.6 + 0.4 * corona);
// Solar arc filaments
float fil = smoothstep(0.55, 0.85, fbm(vec2(a * 6.0, r * 8.0 + uTime * 0.5)));
fil *= smoothstep(0.05, 0.25, r) * smoothstep(0.7, 0.2, r);
// Background space
vec3 bg = mix(vec3(0.02, 0.005, 0.04), vec3(0.0, 0.0, 0.0), smoothstep(0.0, 1.2, r));
// Distant nebula
float neb = fbm(uv * 1.5 + uTime * 0.02);
bg += vec3(0.15, 0.05, 0.30) * neb * 0.18;
// Sun palette: deep orange → yellow → white core
vec3 sunCol = mix(
vec3(1.0, 0.25, 0.05), // outer red
vec3(1.0, 0.85, 0.4), // mid amber
smoothstep(0.0, 0.35, core + outer * 0.5)
);
sunCol = mix(sunCol, vec3(1.0, 1.0, 0.95), pow(core, 2.5));
// Color shift hue toward magenta when cursor far
float hue = clamp(length(m), 0.0, 1.0);
sunCol = mix(sunCol, sunCol.bgr * vec3(1.2, 0.6, 1.0), hue * 0.25);
vec3 col = bg
+ sunCol * core * 1.5
+ sunCol * outer * 1.0
+ vec3(1.0, 0.6, 0.2) * fil * 0.9;
// Click ripple → shockwave
float ce = clickEnergy(frag);
col += vec3(1.4, 1.0, 0.6) * ce * 1.5;
// Lens dust speckle
float dust = step(0.9992, hash21(floor(frag * 0.8)));
col += vec3(dust) * 0.6;
// Tonemap
col = col / (1.0 + col);
col = pow(col, vec3(0.92));
gl_FragColor = vec4(col, 1.0);
}
</script>
<!-- ─────────── 3. MERCURY (chrome metaballs) ─────────── -->
<script id="fs-mercury" type="x-shader/x-fragment">
// 2D metaball SDF approximation
float metaball(vec2 p, vec2 c, float r) {
return r / max(length(p - c), 0.0001);
}
void main() {
vec2 frag = gl_FragCoord.xy;
vec2 uv = (frag - 0.5 * uRes) / min(uRes.x, uRes.y);
vec2 m = (uMouseSmooth - 0.5 * uRes) / min(uRes.x, uRes.y);
// Five drifting metaballs + one tied to the cursor
vec2 b0 = vec2(sin(uTime * 0.31) * 0.45, cos(uTime * 0.27) * 0.32);
vec2 b1 = vec2(cos(uTime * 0.41) * 0.5, sin(uTime * 0.23) * 0.28);
vec2 b2 = vec2(sin(uTime * 0.19 + 2.0) * 0.55, cos(uTime * 0.35 + 1.0) * 0.4);
vec2 b3 = vec2(cos(uTime * 0.29 + 4.0) * 0.4, sin(uTime * 0.17 + 3.0) * 0.5);
vec2 b4 = m;
vec2 b5 = -m * 0.6;
float f = 0.0;
f += metaball(uv, b0, 0.18);
f += metaball(uv, b1, 0.20);
f += metaball(uv, b2, 0.14);
f += metaball(uv, b3, 0.16);
f += metaball(uv, b4, 0.26); // cursor-attached blob — biggest
f += metaball(uv, b5, 0.12);
// Smoothstep into a "surface"
float surface = smoothstep(2.4, 3.0, f);
float edge = smoothstep(2.0, 2.4, f) - smoothstep(2.4, 3.0, f);
// Approximate a normal by finite-differencing the field
vec2 e = vec2(0.0035, 0.0);
float fx = (
metaball(uv + e.xy, b0, 0.18) + metaball(uv + e.xy, b1, 0.20)
+ metaball(uv + e.xy, b2, 0.14) + metaball(uv + e.xy, b3, 0.16)
+ metaball(uv + e.xy, b4, 0.26) + metaball(uv + e.xy, b5, 0.12)
) - f;
float fy = (
metaball(uv + e.yx, b0, 0.18) + metaball(uv + e.yx, b1, 0.20)
+ metaball(uv + e.yx, b2, 0.14) + metaball(uv + e.yx, b3, 0.16)
+ metaball(uv + e.yx, b4, 0.26) + metaball(uv + e.yx, b5, 0.12)
) - f;
vec2 n = normalize(vec2(fx, fy) + vec2(1e-5));
// Iridescent fresnel based on "view angle" (here: how grazing the normal is)
float fres = 1.0 - abs(n.y);
fres = pow(fres, 1.5);
// Reflected environment: spectral bands as a function of normal angle
float ang = atan(n.y, n.x);
vec3 envA = vec3(0.20, 0.55, 1.0); // cyan
vec3 envB = vec3(0.95, 0.25, 0.85); // magenta
vec3 envC = vec3(0.30, 1.0, 0.60); // mint
vec3 envD = vec3(1.0, 0.8, 0.25); // gold
vec3 chrome = mix(envA, envB, sin(ang * 2.0 + uTime * 0.4) * 0.5 + 0.5);
chrome = mix(chrome, envC, sin(ang * 3.0 + uTime * 0.7) * 0.5 + 0.5 * 0.4);
chrome = mix(chrome, envD, fres);
// Specular highlight where normal points toward "light"
float spec = pow(max(dot(n, normalize(vec2(0.6, 0.8))), 0.0), 16.0);
chrome += vec3(spec) * 0.9;
// Background: subtle gradient with vignette
vec3 bg = mix(vec3(0.02, 0.03, 0.05), vec3(0.05, 0.02, 0.07), uv.y * 0.5 + 0.5);
// Soft glow under metaballs
bg += smoothstep(2.0, 3.0, f) * chrome * 0.15;
vec3 col = bg;
col = mix(col, chrome, surface);
col += edge * mix(envA, envB, sin(uTime * 0.6) * 0.5 + 0.5) * 0.6;
// Click → push surface
float ce = clickEnergy(frag);
col += vec3(0.9, 0.95, 1.0) * ce * 1.2;
// Tonemap
col = col / (1.0 + col * 0.6);
gl_FragColor = vec4(col, 1.0);
}
</script>
<!-- ─────────── 4. CRYSTAL (voronoi lattice) ─────────── -->
<script id="fs-crystal" type="x-shader/x-fragment">
// Voronoi distance to 1st and 2nd nearest
vec2 voronoi(vec2 p, out vec2 closestId) {
vec2 i = floor(p), f = fract(p);
float d1 = 1e9, d2 = 1e9;
vec2 ic = vec2(0.0);
for (int y = -1; y <= 1; y++) {
for (int x = -1; x <= 1; x++) {
vec2 g = vec2(float(x), float(y));
vec2 o = hash22(i + g);
o = 0.5 + 0.5 * sin(uTime * 0.6 + 6.28 * o);
vec2 r = g + o - f;
float d = dot(r, r);
if (d < d1) { d2 = d1; d1 = d; ic = i + g; }
else if (d < d2) { d2 = d; }
}
}
closestId = ic;
return vec2(sqrt(d1), sqrt(d2));
}
void main() {
vec2 frag = gl_FragCoord.xy;
vec2 uv = frag / min(uRes.x, uRes.y);
vec2 m = uMouseSmooth / min(uRes.x, uRes.y);
// Mouse warps the lattice (radial pull)
vec2 d = uv - m;
float warpStrength = 0.6 * exp(-length(d) * 2.0);
vec2 uvw = uv + d * warpStrength;
// Click pulses: temporary scale push
float ce = clickEnergy(frag);
uvw *= 1.0 - ce * 0.05;
vec2 ic;
vec2 v = voronoi(uvw * 4.0, ic);
float edge = v.y - v.x;
float cellId = hash21(ic);
// Cell base color — prismatic palette per cell
vec3 cA = vec3(0.05, 0.15, 0.40);
vec3 cB = vec3(0.30, 0.10, 0.50);
vec3 cC = vec3(0.10, 0.50, 0.55);
vec3 cD = vec3(0.55, 0.20, 0.35);
vec3 base = mix(mix(cA, cB, cellId), mix(cC, cD, fract(cellId * 7.1)), fract(cellId * 3.3));
// Inside-cell gradient — darken away from center
float inner = smoothstep(0.0, 0.6, v.x);
vec3 fill = base * (1.0 - inner * 0.75);
// Glowing prismatic edges
float edgeMask = 1.0 - smoothstep(0.0, 0.04, edge);
vec3 edgeCol = mix(vec3(0.4, 0.9, 1.0), vec3(1.0, 0.3, 0.8),
sin(uTime * 0.4 + cellId * 10.0) * 0.5 + 0.5);
// Hover highlight on cell under cursor
vec2 mIc;
voronoi(m * min(uRes.x, uRes.y) / min(uRes.x, uRes.y) * 4.0, mIc);
float hovered = step(distance(ic, mIc), 0.1);
edgeCol = mix(edgeCol, vec3(1.0, 1.0, 0.85), hovered * 0.8);
// Refractive caustic — fine secondary lines inside cells
float caustic = sin((v.x * 30.0) + uTime * 1.2 + cellId * 20.0);
caustic = smoothstep(0.4, 0.95, caustic) * (1.0 - edgeMask);
vec3 causticCol = mix(vec3(0.6, 0.8, 1.0), vec3(1.0, 0.7, 0.9), cellId);
vec3 col = fill + edgeCol * edgeMask * 1.4 + causticCol * caustic * 0.15;
// Hovered cell brightens overall
col += hovered * vec3(0.15, 0.18, 0.22) * (1.0 - edgeMask);
// Click → ripple of brightness
col += vec3(0.6, 0.9, 1.0) * ce * 0.9;
// Vignette
vec2 vuv = frag / uRes - 0.5;
col *= 1.0 - dot(vuv, vuv) * 0.7;
gl_FragColor = vec4(col, 1.0);
}
</script>
<!-- ─────────── 5. WAVES (interference) ─────────── -->
<script id="fs-waves" type="x-shader/x-fragment">
void main() {
vec2 frag = gl_FragCoord.xy;
vec2 uv = frag / uRes;
vec2 m = uMouseSmooth / uRes;
// Drifting wave sources (4 of them) + cursor source + click sources
vec2 sources[5];
sources[0] = vec2(0.2 + 0.05 * sin(uTime * 0.3), 0.3 + 0.04 * cos(uTime * 0.4));
sources[1] = vec2(0.75 + 0.06 * cos(uTime * 0.25), 0.25 + 0.05 * sin(uTime * 0.35));
sources[2] = vec2(0.3 + 0.07 * sin(uTime * 0.2 + 1.0), 0.78 + 0.04 * cos(uTime * 0.22));
sources[3] = vec2(0.78 + 0.05 * cos(uTime * 0.28 + 2.0), 0.72 + 0.06 * sin(uTime * 0.31));
sources[4] = m;
float field = 0.0;
for (int i = 0; i < 5; i++) {
float dist = distance(uv, sources[i]);
// Wave with falloff so sources don't dominate at distance
float falloff = 1.0 / (1.0 + dist * 4.0);
float speed = (i == 4) ? 5.0 : 3.5 + float(i) * 0.4;
float freq = (i == 4) ? 55.0 : 36.0 + float(i) * 4.0;
field += sin(dist * freq - uTime * speed) * falloff;
}
// Click sources add expanding wavefronts
for (int i = 0; i < 6; i++) {
if (uClicks[i].w < 0.5) continue;
float age = uTime - uClicks[i].z;
if (age < 0.0 || age > 4.0) continue;
vec2 cp = uClicks[i].xy / uRes;
float d = distance(uv, cp);
float radius = age * 0.35;
float thickness = 0.04 + age * 0.05;
float front = exp(-pow((d - radius) / thickness, 2.0));
field += front * exp(-age * 0.7) * 3.0;
}
// Color from field amplitude
float v = field * 0.18;
vec3 cDeep = vec3(0.02, 0.04, 0.12);
vec3 cMid = vec3(0.20, 0.55, 0.95);
vec3 cHigh = vec3(0.85, 0.95, 1.00);
vec3 cCrest = vec3(1.0, 0.8, 0.5);
vec3 col = cDeep;
col = mix(col, cMid, smoothstep(-0.2, 0.4, v));
col = mix(col, cHigh, smoothstep(0.4, 0.8, v));
col = mix(col, cCrest, smoothstep(0.8, 1.2, v));
// Iso-lines for that schlieren / oscilloscope look
float iso = abs(fract(field * 1.0) - 0.5);
float lines = 1.0 - smoothstep(0.0, 0.03, iso);
col += lines * vec3(0.05, 0.15, 0.25);
// Subtle radial highlight at cursor source
float curDist = distance(uv, m);
col += exp(-curDist * 10.0) * vec3(0.4, 0.6, 1.0) * 0.3;
// Vignette
vec2 vuv = uv - 0.5;
col *= 1.0 - dot(vuv, vuv) * 0.5;
gl_FragColor = vec4(col, 1.0);
}
</script>
<!-- ─────────── RENDERER ─────────── -->
<script>
(function() {
const canvas = document.getElementById('gl');
const gl = canvas.getContext('webgl', { antialias: false, alpha: false, preserveDrawingBuffer: false });
if (!gl) {
document.body.innerHTML = '<div style="color:#fff;font-family:monospace;padding:40px;">WebGL not available.</div>';
return;
}
const SHADERS = ['aurora','plasma','mercury','crystal','waves'];
const DESC = {
aurora: { name: 'Aurora Field', body: 'FBM-warped ribbon bands drifting over a deep-night gradient. Cursor displaces field, click discharges a luminous burst.' },
plasma: { name: 'Plasma Sun', body: 'A breathing star anchored to the cursor. Corona swirls via polar-domain noise; click triggers a solar shockwave.' },
mercury: { name: 'Liquid Mercury', body: 'Iridescent metaball field with one blob attached to the cursor. Chrome reflections shift by surface angle.' },
crystal: { name: 'Crystal Lattice', body: 'Animated Voronoi cells with prismatic edges. Cursor warps the lattice radially; the cell beneath it highlights.' },
waves: { name: 'Wave Interference',body: 'Superposed sine emitters with the cursor as a roaming source. Click drops a real wavefront that propagates outward.' },
};
// Build vertex shader once
const VS = document.getElementById('vs').textContent;
const HEADER = document.getElementById('frag-header').textContent;
function compile(type, src) {
const s = gl.createShader(type);
gl.shaderSource(s, src);
gl.compileShader(s);
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
console.error('Shader compile error:', gl.getShaderInfoLog(s), src);
return null;
}
return s;
}
function makeProgram(fsSrc) {
const vs = compile(gl.VERTEX_SHADER, VS);
const fs = compile(gl.FRAGMENT_SHADER, HEADER + '\n' + fsSrc);
if (!vs || !fs) return null;
const p = gl.createProgram();
gl.attachShader(p, vs); gl.attachShader(p, fs);
gl.linkProgram(p);
if (!gl.getProgramParameter(p, gl.LINK_STATUS)) {
console.error('Link error:', gl.getProgramInfoLog(p));
return null;
}
return p;
}
// Fullscreen quad
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1,-1, 1,-1, -1, 1,
-1, 1, 1,-1, 1, 1,
]), gl.STATIC_DRAW);
// Compile all programs lazily
const programs = {};
const uniformCache = {};
function getProgram(name) {
if (programs[name]) return programs[name];
const fsEl = document.getElementById('fs-' + name);
if (!fsEl) return null;
const p = makeProgram(fsEl.textContent);
programs[name] = p;
uniformCache[name] = {
uRes: gl.getUniformLocation(p, 'uRes'),
uTime: gl.getUniformLocation(p, 'uTime'),
uMouse: gl.getUniformLocation(p, 'uMouse'),
uMouseSmooth: gl.getUniformLocation(p, 'uMouseSmooth'),
uClicks: gl.getUniformLocation(p, 'uClicks[0]'),
uShaderTime: gl.getUniformLocation(p, 'uShaderTime'),
a_pos: gl.getAttribLocation(p, 'a_pos'),
};
return p;
}
// Resize
function resize() {
const dpr = Math.min(window.devicePixelRatio || 1, 2);
const w = Math.floor(window.innerWidth * dpr);
const h = Math.floor(window.innerHeight * dpr);
if (canvas.width !== w || canvas.height !== h) {
canvas.width = w; canvas.height = h;
gl.viewport(0, 0, w, h);
}
}
window.addEventListener('resize', resize);
resize();
// Mouse state
const dprFor = () => Math.min(window.devicePixelRatio || 1, 2);
const mouse = { x: window.innerWidth * 0.5, y: window.innerHeight * 0.5 };
const mouseSmooth = { x: mouse.x, y: mouse.y };
window.addEventListener('mousemove', e => {
mouse.x = e.clientX;
mouse.y = e.clientY;
});
window.addEventListener('touchmove', e => {
if (e.touches.length) {
mouse.x = e.touches[0].clientX;
mouse.y = e.touches[0].clientY;
}
}, { passive: true });
// Clicks — keep last 6, each vec4(x, y, startTime, active)
const clicks = new Float32Array(6 * 4);
let clickIdx = 0;
function recordClick(x, y) {
const base = clickIdx * 4;
clicks[base + 0] = x;
clicks[base + 1] = y;
clicks[base + 2] = (performance.now() - startMs) / 1000;
clicks[base + 3] = 1.0;
clickIdx = (clickIdx + 1) % 6;
}
canvas.addEventListener('mousedown', e => {
const dpr = dprFor();
recordClick(e.clientX * dpr, (window.innerHeight - e.clientY) * dpr);
});
canvas.addEventListener('touchstart', e => {
if (e.touches.length) {
const dpr = dprFor();
recordClick(e.touches[0].clientX * dpr, (window.innerHeight - e.touches[0].clientY) * dpr);
}
}, { passive: true });
// UI / switching
const dock = document.getElementById('dock');
const fpsEl = document.getElementById('fps');
const cursEl = document.getElementById('curs');
const shNameEl = document.getElementById('shname');
const descName = document.getElementById('desc-name');
const descBody = document.getElementById('desc-body');
const titleEl = document.querySelector('.top-left .title');
const flashEl = document.getElementById('flash');
let current = 'aurora';
let shaderStartMs = performance.now();
function setShader(name) {
if (!SHADERS.includes(name) || name === current) {
// still allow refresh of UI
if (name !== current) return;
}
current = name;
shaderStartMs = performance.now();
getProgram(name); // compile if needed
// UI
dock.querySelectorAll('.dock-btn').forEach(btn => {
btn.classList.toggle('active', btn.dataset.shader === name);
});
shNameEl.textContent = name;
descName.textContent = DESC[name].name;
descBody.textContent = DESC[name].body;
titleEl.innerHTML = DESC[name].name.split(' ')[0] +
'<span class="accent"> · ' + (DESC[name].name.split(' ')[1] || 'Field') + '</span>';
// Flash overlay
flashEl.textContent = name;
flashEl.classList.remove('show');
// force reflow
void flashEl.offsetWidth;
flashEl.classList.add('show');
setTimeout(() => flashEl.classList.remove('show'), 650);
}
dock.querySelectorAll('.dock-btn').forEach(btn => {
btn.addEventListener('click', () => setShader(btn.dataset.shader));
});
// Keyboard shortcuts 15
window.addEventListener('keydown', e => {
const i = parseInt(e.key, 10);
if (i >= 1 && i <= 5) setShader(SHADERS[i - 1]);
});
// Prime aurora
getProgram('aurora');
// Render loop
const startMs = performance.now();
let lastFrame = startMs;
let fpsAccum = 0, fpsFrames = 0, fpsLast = startMs;
function frame() {
const now = performance.now();
const dt = (now - lastFrame) / 1000;
lastFrame = now;
const t = (now - startMs) / 1000;
const tShader = (now - shaderStartMs) / 1000;
// FPS
fpsAccum += dt; fpsFrames++;
if (now - fpsLast > 500) {
fpsEl.textContent = (fpsFrames / fpsAccum).toFixed(0);
fpsAccum = 0; fpsFrames = 0; fpsLast = now;
}
// Smooth mouse
mouseSmooth.x += (mouse.x - mouseSmooth.x) * Math.min(1, dt * 6);
mouseSmooth.y += (mouse.y - mouseSmooth.y) * Math.min(1, dt * 6);
cursEl.textContent = Math.round(mouse.x) + ',' + Math.round(mouse.y);
resize();
const dpr = dprFor();
const W = canvas.width, H = canvas.height;
const p = getProgram(current);
if (!p) { requestAnimationFrame(frame); return; }
const u = uniformCache[current];
gl.useProgram(p);
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.enableVertexAttribArray(u.a_pos);
gl.vertexAttribPointer(u.a_pos, 2, gl.FLOAT, false, 0, 0);
gl.uniform2f(u.uRes, W, H);
gl.uniform1f(u.uTime, t);
gl.uniform1f(u.uShaderTime, tShader);
// Mouse in GL coords (Y inverted relative to CSS)
gl.uniform2f(u.uMouse, mouse.x * dpr, (window.innerHeight - mouse.y) * dpr);
gl.uniform2f(u.uMouseSmooth, mouseSmooth.x * dpr, (window.innerHeight - mouseSmooth.y) * dpr);
gl.uniform4fv(u.uClicks, clicks);
gl.drawArrays(gl.TRIANGLES, 0, 6);
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
})();
</script>
</body>
</html>

6620
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

45
tailwind.config.mjs Normal file
View File

@@ -0,0 +1,45 @@
import plugin from 'tailwindcss/plugin';
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{astro,html,js,ts}'],
theme: {
extend: {
colors: {
'fa-navy': '#1A1F5C',
'fa-navy-deep': '#0F1340',
'fa-blue': '#6B8FD4',
'fa-blue-light':'#A8C0E8',
'fa-blue-pale': '#EEF3FB',
'fa-off-white': '#F7F8FC',
'fa-mid': '#6B7280',
},
fontFamily: {
syne: ['Syne', 'sans-serif'],
dm: ['DM Sans', 'sans-serif'],
},
screens: {
'xs': '480px',
'sm': '720px',
'md': '900px',
'lg': '1100px',
'xl': '1280px',
'2xl': '1500px',
},
letterSpacing: {
'widest2': '2px',
'widest3': '3px',
},
},
},
plugins: [
plugin(function ({ addUtilities }) {
addUtilities({
'.px-content': {
paddingLeft: 'var(--content-pad)',
paddingRight: 'var(--content-pad)',
},
});
}),
],
};

3
tsconfig.json Normal file
View File

@@ -0,0 +1,3 @@
{
"extends": "astro/tsconfigs/base"
}