Hopf Vibratiok in pink
GLSL shader by merrypranxter · created 2026-02-15 · 10s loop · 2 passes
This page opens the shader in the ShaderKit browser GLSL editor: edit it live, fork it, or render it to video, GIF or images up to 8K. Also available as a full-screen view and an embeddable player.
Shader source (GLSL)
Common
#define pi acos(-1.)
#define deg pi/180. //1 degree
#define time iTime*2.*pi/10. //sin(time) loops 10 seconds
#define R iResolution.xy //shorthand
#define ar R.x/R.y //aspect ratio
#define M iMouse //shorthand
#define xm (M.xy/R) //normalized mouse
#define nm ((xm.xy-0.5)*vec2(ar,1.)+0.5) //aspect ratio correction
vec3 cs = vec3(1.,2.,3.);
mat2 r2d(float a) {
return mat2(cos(a),sin(a),-sin(a),cos(a));
}
Buffer A (iChannel0)
// ============================================================
// HOPF JEWEL v1.0
// Hopf fibration — 4D topology projected to 3D
// Interlocking circles, tube SDF, thin-film, GGX gloss
// ============================================================
const float PI = 3.14159265359;
const float TAU = 6.28318530718;
const float speed = 0.18;
const float tubeR = 0.045;
const float brightness = 2.0;
const float fiberN = 28.0;
// ---- ACES tonemap ----
vec3 ACESFilm(vec3 x) {
return clamp((x*(2.51*x+0.03))/(x*(2.43*x+0.59)+0.14), 0.0, 1.0);
}
// ---- Palettes ----
vec3 pal1(float t) {
vec3 a = vec3(0.5, 0.4, 0.6);
vec3 b = vec3(0.5, 0.4, 0.5);
vec3 c = vec3(1.0, 1.4, 0.9);
vec3 d = vec3(0.0, 0.2, 0.6);
return a + b * cos(TAU * (c * t + d));
}
vec3 pal2(float t) {
vec3 a = vec3(0.4, 0.2, 0.5);
vec3 b = vec3(0.5, 0.5, 0.4);
vec3 c = vec3(2.0, 1.5, 0.8);
vec3 d = vec3(0.1, 0.4, 0.9);
return a + b * cos(TAU * (c * t + d));
}
vec3 pal3(float t) {
vec3 a = vec3(0.6, 0.3, 0.4);
vec3 b = vec3(0.4, 0.5, 0.4);
vec3 c = vec3(0.5, 1.8, 2.0);
vec3 d = vec3(0.8, 0.1, 0.4);
return a + b * cos(TAU * (c * t + d));
}
// ---- Hopf fiber ----
// Given a point on S2 (unit sphere), returns the
// corresponding circle in 3D via Hopf projection
// p2: point on S2 parameterized by (theta, phi)
vec3 hopfFiber(float theta, float phi, float s) {
// Point on S2
float ct = cos(theta * 0.5);
float st = sin(theta * 0.5);
float cp = cos(phi);
float sp = sin(phi);
// Quaternion on S3
float q0 = ct;
float q1 = st * cp;
float q2 = st * sp;
float q3 = 0.0;
// Rotate by fiber parameter s
float cs = cos(s);
float ss = sin(s);
// Hopf map: S3 -> S2 composed with S3 rotation
float r0 = cs * q0 - ss * q3;
float r1 = cs * q1 - ss * q2;
float r2 = cs * q2 + ss * q1;
float r3 = cs * q3 + ss * q0;
// Stereographic projection from S3 to R3
float denom = max(1.0 - r0, 0.0001);
return vec3(r1, r2, r3) / denom;
}
// ---- SDF: distance to a Hopf fiber circle ----
// We find closest point on the circle by sampling
float hopfFiberDist(float theta, float phi, vec3 p, out float bestS) {
float minD = 1e10;
bestS = 0.0;
int steps = 32;
for(int i = 0; i < 32; i++) {
float s = float(i) / float(steps) * TAU;
vec3 fp = hopfFiber(theta, phi, s);
float d = length(p - fp);
if(d < minD) {
minD = d;
bestS = s;
}
}
return minD - tubeR;
}
// ---- Scene: collection of Hopf fibers ----
vec3 mapScene(vec3 p, float t, out float matID, out float fiber_s, out float fiber_phi) {
float minD = 1e10;
matID = 0.0;
fiber_s = 0.0;
fiber_phi = 0.0;
float N = fiberN;
for(int i = 0; i < 28; i++) {
float fi = float(i);
// Distribute fibers across S2
float phi = fi / N * TAU;
float theta = PI * (0.25 + 0.5 * (fi / N));
float bs = 0.0;
float d = hopfFiberDist(theta + t * 0.07, phi + t * 0.04, p, bs);
if(d < minD) {
minD = d;
matID = fi / N;
fiber_s = bs;
fiber_phi = phi;
}
}
return vec3(minD, 0.0, 0.0);
}
float mapDist(vec3 p, float t) {
float m = 0.0;
float fs = 0.0;
float fp = 0.0;
return mapScene(p, t, m, fs, fp).x;
}
vec3 calcNormal(vec3 p, float t) {
float e = 0.0008;
float d0 = mapDist(p, t);
float nx = mapDist(p + vec3(e,0,0), t) - d0;
float ny = mapDist(p + vec3(0,e,0), t) - d0;
float nz = mapDist(p + vec3(0,0,e), t) - d0;
return normalize(vec3(nx, ny, nz));
}
float calcAO(vec3 p, vec3 n, float t) {
float ao = 0.0;
float sc = 0.06;
for(int i = 1; i <= 4; i++) {
float fi = float(i);
float dv = mapDist(p + n * sc * fi, t);
ao += max(0.0, sc * fi - dv) / (sc * fi);
}
return clamp(1.0 - ao * 0.7, 0.0, 1.0);
}
// ---- GGX ----
float ggxD(float ndoth, float rough) {
float a2 = rough * rough * rough * rough;
float denom = ndoth * ndoth * (a2 - 1.0) + 1.0;
return a2 / max(PI * denom * denom, 0.0001);
}
float schlick(float cosT, float f0) {
float t1 = 1.0 - cosT;
float t2 = t1 * t1;
return f0 + (1.0 - f0) * t2 * t2 * t1;
}
// ---- Thin-film iridescence ----
vec3 iridFilm(float ndotv, float thick) {
float ph = thick * ndotv;
float r = cos(ph * 0.85) * 0.5 + 0.5;
float g = cos(ph * 1.00 + 2.09) * 0.5 + 0.5;
float b = cos(ph * 1.20 + 4.19) * 0.5 + 0.5;
return vec3(r, g, b);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
float t = iTime * speed;
// Cinematic camera orbiting the structure
float camA = t * 0.22;
float camB = sin(t * 0.15) * 0.35;
float camR = 5.5 + sin(t * 0.1) * 0.8;
vec3 ro = vec3(sin(camA) * camR, camB * 2.0, cos(camA) * camR);
vec3 tgt = vec3(0.0, 0.0, 0.0);
vec3 fw = normalize(tgt - ro);
vec3 rt = normalize(cross(fw, vec3(0,1,0)));
vec3 up = cross(rt, fw);
vec3 rd = normalize(fw + uv.x * rt * 1.1 + uv.y * up);
// Raymarch
float d = 0.1;
float glow = 0.0;
bool hit = false;
for(int i = 0; i < 120; i++) {
vec3 p = ro + rd * d;
float ds = mapDist(p, t);
float gw = 0.006 / (0.006 + abs(ds) * abs(ds) * 40.0);
glow += gw;
d += ds * 0.55;
if(ds < 0.0005) { hit = true; break; }
if(d > 20.0) { break; }
}
// Black void
vec3 col = vec3(0.005, 0.005, 0.012);
if(hit) {
vec3 p = ro + rd * d;
vec3 n = calcNormal(p, t);
float ao = calcAO(p, n, t);
float matID = 0.0;
float fiber_s = 0.0;
float fiber_phi = 0.0;
mapScene(p, t, matID, fiber_s, fiber_phi);
// Lights
vec3 ldir1 = normalize(vec3(3.0, 5.0, 2.0));
vec3 ldir2 = normalize(vec3(-2.0, -1.0, -3.0));
vec3 vdir = -rd;
float diff1 = max(dot(n, ldir1), 0.0);
float diff2 = max(dot(n, ldir2), 0.0) * 0.25;
float ndotv = max(dot(n, vdir), 0.0);
// GGX clear coat — polished metal/glass tube
vec3 h1 = normalize(ldir1 + vdir);
float ndoth1 = max(dot(n, h1), 0.0);
float spec1 = ggxD(ndoth1, 0.08) * schlick(ndotv, 0.06);
// GGX second lobe
vec3 h2 = normalize(ldir2 + vdir);
float ndoth2 = max(dot(n, h2), 0.0);
float spec2 = ggxD(ndoth2, 0.3) * schlick(ndotv, 0.04) * 0.3;
// Fresnel rim
float rimW = pow(1.0 - ndotv, 4.0);
// Thin-film — varies along fiber
float thick = 8.0 + sin(fiber_s * 3.0 + fiber_phi * 2.0) * 5.0;
vec3 filmC = iridFilm(ndotv, thick);
// Per-fiber color — each ring has its own palette position
float phase1 = matID + t * 0.06;
float phase2 = matID * 1.3 + fiber_s / TAU + t * 0.04;
float phase3 = matID * 0.7 + t * 0.08;
vec3 baseCol = pal1(phase1);
vec3 rimCol = pal2(phase3);
vec3 specCol = pal3(phase2);
// Compose
col = baseCol * (diff1 * 0.6 + diff2 + 0.08) * ao;
col = mix(col, col * filmC * 2.2, 0.45 * schlick(ndotv, 0.03));
col += vec3(0.95, 0.98, 1.00) * spec1 * 6.0;
col += specCol * spec2 * 3.0;
col += rimCol * rimW * 1.8 * ao;
// Tube interior glow — each ring glows its own color
float interior = exp(-d * 0.08) * (1.0 - ndotv);
col += pal2(phase1 + 0.3) * interior * 0.4;
}
// Volumetric inter-ring glow
float glowC = clamp(glow, 0.0, 1.0);
col += pal1(t * 0.07) * glowC * 1.5;
col += pal2(glowC + t * 0.05) * glowC * glowC * 3.0;
col += pal3(t * 0.04 + 0.5) * glowC * 0.8;
// Depth fog
float fogA = clamp(d / 20.0, 0.0, 1.0);
col = mix(col, vec3(0.005, 0.005, 0.012), fogA * 0.5);
// ACES + gamma
col = ACESFilm(col * 1.4);
col = pow(max(col, 0.0), vec3(0.4545));
// Vignette
col *= 1.0 - dot(uv * 0.9, uv * 0.9) * 0.5;
fragColor = vec4(col, 1.0);
}
Image
Not used
More shaders by merrypranxter
- PRISMACORE_003 — Retina Banquet
- PRISMACORE_002 — After Work Sky
- PRISMACORE_001 — Liquid Crystal Fever Dream
- Void Geode
- Project_2026-02-14_04-53-13
- Chromatic Plexus
- Signal Death
- Deep fried math
- Candy Interference
- Project_2026-02-12_21-16-32
- Project_2026-02-12_17-00-28
- Project_2026-02-12_16-17-50
Browse all public shaders · All shaders by merrypranxter · ShaderKit home
Unread comments
×Projects with new comments. Click to open and read.