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);
}
// settings
theme:
fx: