Tunnel
An eight-sided wireframe wormhole where the tunnel’s center wanders on
a sum of sines, warping the perspective as if the tube itself were a
snake. Behind the wireframe: a starfield glimpsed through the gaps,
expanding gravity-wave rings, and 28 tachyon particles emitted from
the eye and stretched into cubic-eased trails.
The trick that makes the wireframe read as a tunnel is the map from
screen-radius to depth: z = 0.8 / (r + eps) — pixels near the middle
of the screen are far away, edges are near. Sliding z + iTime * 3
scrolls the whole thing toward you. The winding uses screen radius as
its interpolant so distant slices swing further than near ones, giving
the tube its serpentine bend.
[glsl]: shader source
// Eight-sided wireframe wormhole. Winds via nested sines on the tunnel
// center; palette-cycled rings and side edges; starfield through the
// gaps; gravity-wave ripples and tachyon particles from the eye.
// @slider tempo 0.1 3.0 1.0 0.05
uniform float tempo;
#define PI 3.14159265359
float hash1(float n){
return fract(sin(n) * 43758.5453);
}
float hash2v(vec2 p){
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
}
vec3 palette(float idx){
idx = fract(idx) * 4.0;
vec3 c1 = iAccent;
vec3 c2 = vec3(0.35, 0.75, 1.00);
vec3 c3 = vec3(1.00, 0.35, 0.55);
vec3 c4 = vec3(0.55, 0.25, 0.85);
if(idx < 1.0) return mix(c1, c2, idx);
else if(idx < 2.0) return mix(c2, c3, idx - 1.0);
else if(idx < 3.0) return mix(c3, c4, idx - 2.0);
else return mix(c4, c1, idx - 3.0);
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = (fragCoord - 0.5 * iResolution.xy) / min(iResolution.x, iResolution.y);
float t = iTime * 0.8 * tempo;
vec3 c1 = iAccent;
vec3 c2 = vec3(0.35, 0.75, 1.00);
vec3 c3 = vec3(1.00, 0.35, 0.55);
vec3 c4 = vec3(0.55, 0.25, 0.85);
// radius -> depth (edge = near, center = far), scroll toward the camera
float radius = length(uv);
float z = 0.8 / (radius + 0.001);
float zWorld = z + t * 3.0;
// tunnel center snakes through space: three sines each axis at
// irrational-ish frequencies so the loop never repeats cleanly
float windX = sin(zWorld * 0.15) * 0.4
+ sin(zWorld * 0.08 + 1.7) * 0.25
+ sin(zWorld * 0.23 + 3.1) * 0.1;
float windY = cos(zWorld * 0.12 + 0.5) * 0.35
+ cos(zWorld * 0.07 + 2.3) * 0.2
+ sin(zWorld * 0.19 + 4.0) * 0.1;
// offset by wind scaled by screen radius: far slices swing further
vec2 tunnelUV = uv - vec2(windX, windY) * radius;
float tunnelR = length(tunnelUV);
float angle = atan(tunnelUV.y, tunnelUV.x);
float zCurved = 0.8 / (tunnelR + 0.001);
float zFinal = zCurved + t * 3.0;
// octagonal cross section, twisted along depth
float sides = 8.0;
float sectorAngle = 2.0 * PI / sides;
float twist = zFinal * 0.3;
float twistedAngle = angle + twist;
float sector = mod(twistedAngle + PI, sectorAngle);
float sectorEdge = abs(sector - sectorAngle * 0.5);
// depth rings + side edges = the wireframe
float ringFreq = 2.0;
float rings = abs(fract(zFinal * ringFreq) - 0.5);
float ringLine = smoothstep(0.02, 0.0, rings);
float edgeWidth = 0.06 * tunnelR;
float sideLine = smoothstep(edgeWidth + 0.01, edgeWidth, sectorEdge);
float wire = max(ringLine, sideLine);
// palette rotation with ring depth, with side lines picking a different index
float ringIdx = mod(floor(zFinal * ringFreq), 4.0);
vec3 wireCol;
if(ringIdx < 1.0) wireCol = c1;
else if(ringIdx < 2.0) wireCol = c2;
else if(ringIdx < 3.0) wireCol = c3;
else wireCol = c4;
float sideIdx = mod(floor((twistedAngle + PI) / sectorAngle), 4.0);
vec3 sideCol;
if(sideIdx < 1.0) sideCol = c1;
else if(sideIdx < 2.0) sideCol = c2;
else if(sideIdx < 3.0) sideCol = c3;
else sideCol = c4;
vec3 col = mix(sideCol, wireCol, step(ringLine, sideLine)) * wire;
// neon halo along both edge families
float glowRing = exp(-rings * 40.0) * 0.3;
float glowSide = exp(-sectorEdge / max(edgeWidth, 0.001) * 2.0) * 0.2 * step(0.01, tunnelR);
col += wireCol * glowRing + sideCol * glowSide;
// fade near the eye so the mouth of the tube stays dark
float depthFade = smoothstep(0.0, 0.5, tunnelR);
col *= depthFade;
// wormhole eye glow: shifts hue as it travels
float centerGlow = exp(-tunnelR * 8.0) * 0.35;
vec3 portalColor = mix(c1, c3, sin(t * 0.7) * 0.5 + 0.5);
portalColor = mix(portalColor, c2, sin(t * 0.4 + 2.0) * 0.5 + 0.5);
col += portalColor * centerGlow;
// starfield visible through the gaps in the wireframe
if(wire < 0.1){
vec2 starUV = tunnelUV * 5.0 + vec2(windX, windY) * 0.5;
vec2 starCell = floor(starUV * 30.0);
float starHash = fract(sin(dot(starCell, vec2(127.1, 311.7))) * 43758.5453);
if(starHash > 0.97){
float twinkle = sin(t * 4.0 + starHash * 6.28) * 0.5 + 0.5;
float starBright = (0.4 + 0.6 * twinkle) * depthFade * 0.3;
col += vec3(starBright);
}
}
// gentle pulse tied to loop phase
float beatPulse = exp(-fract(t) * 4.0) * 0.15;
col += wireCol * beatPulse * wire;
// gravity waves — five concentric rings expanding outward, ea rippled
for(int w = 0; w < 5; w++){
float wavePhase = fract(t * 0.22 + float(w) * 0.2);
float waveRadius = wavePhase * wavePhase * 0.45;
float waveFade = pow(1.0 - wavePhase, 1.5);
float waveWidth = 0.004 + wavePhase * 0.018;
float wave = exp(-pow(tunnelR - waveRadius, 2.0) / (waveWidth * waveWidth)) * waveFade;
wave *= 0.8 + 0.2 * sin(angle * 8.0 + float(w) * 2.1 + t * 0.6);
vec3 waveCol = palette(float(w) * 0.2 + t * 0.06);
col += waveCol * wave * 0.55;
}
// tachyon particles — cubic acceleration turns them into stretched streaks
for(int i = 0; i < 28; i++){
float id = float(i);
float lifespan = 2.0 + hash1(id * 17.3) * 2.5;
float life = mod(t + hash1(id * 13.7) * lifespan, lifespan);
float progress = life / lifespan;
float pAngle = hash1(id * 7.3 + 0.5) * 2.0 * PI + t * 0.1 * (hash1(id * 3.1) - 0.5);
float speed = progress * progress * progress;
float pDist = speed * 0.5;
vec2 pPos = vec2(cos(pAngle), sin(pAngle)) * pDist;
vec2 toPixel = tunnelUV - pPos;
float d = length(toPixel);
// stretched motion trail behind the head
vec2 radDir = vec2(cos(pAngle), sin(pAngle));
float along = dot(toPixel, radDir);
float perp = length(toPixel - radDir * along);
float trailLen = 0.01 + speed * 0.12;
float inTrail = smoothstep(0.0, -trailLen, along) * step(along, 0.003);
float trailWidth = 0.003 * (1.0 - progress * 0.5);
float trail = exp(-perp / trailWidth) * inTrail * (1.0 - progress);
// bright particle head
float fade = 1.0 - progress * 0.4;
float brightness = fade * 0.0012 / (d * d + 0.00012);
brightness = min(brightness, 5.0);
vec3 pCol = palette(id / 28.0 + t * 0.04);
vec3 particleCol = pCol * (1.0 + smoothstep(1.0, 3.0, brightness) * 0.5);
col += particleCol * brightness * 0.35 + pCol * trail * 1.0;
}
// central emission point pulses
float tachyonCenterGlow = 0.002 / (tunnelR * tunnelR + 0.004);
float tachyonPulse = 0.7 + 0.3 * sin(t * 3.5);
col += palette(t * 0.12) * tachyonCenterGlow * 0.15 * tachyonPulse;
// scanlines + vignette
float scanline = 0.9 + 0.1 * sin(fragCoord.y * 3.0);
col *= scanline;
float vig = 1.0 - dot(uv, uv) * 0.3;
col *= vig;
fragColor = vec4(col * 0.5, 1.0);
}