precision highp float;

#define OUT_COLOR outColor
out vec4 outColor;

uniform vec2 resolution;
uniform float time;

#define F4 0.309016994374947451
float mod289(float x) {
   return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec2 mod289(vec2 x) {
   return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec3 mod289(vec3 x) {
   return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 mod289(vec4 x) {
   return x - floor(x * (1.0 / 289.0)) * 289.0;
}
float permute(float x) {
   return mod289(((x * 34.0) + 1.0) * x);
}
vec3 permute(vec3 x) {
   return mod289(((x * 34.0) + 1.0) * x);
}
vec4 permute(vec4 x) {
   return mod289(((x * 34.0) + 1.0) * x);
}
float taylorInvSqrt(float r) {
   return 1.79284291400159 - 0.85373472095314 * r;
}
vec4 taylorInvSqrt(vec4 r) {
   return 1.79284291400159 - 0.85373472095314 * r;
}
float snoise2D(vec2 v) {
   const vec4 C = vec4(0.211324865405187,  // (3.0-sqrt(3.0))/6.0
   0.366025403784439,  // 0.5*(sqrt(3.0)-1.0)
   -0.577350269189626,  // -1.0 + 2.0 * C.x
   0.024390243902439); // 1.0 / 41.0
 // First corner
   vec2 i = floor(v + dot(v, C.yy));
   vec2 x0 = v - i + dot(i, C.xx);

 // Other corners
   vec2 i1;
 //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
 //i1.y = 1.0 - i1.x;
   i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
 // x0 = x0 - 0.0 + 0.0 * C.xx ;
 // x1 = x0 - i1 + 1.0 * C.xx ;
 // x2 = x0 - 1.0 + 2.0 * C.xx ;
   vec4 x12 = x0.xyxy + C.xxzz;
   x12.xy -= i1;

 // Permutations
   i = mod289(i); // Avoid truncation effects in permutation
   vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0));
   vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0);
   m = m * m;
   m = m * m;

 // Gradients: 41 points uniformly over a line, mapped onto a diamond.
 // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)

   vec3 x = 2.0 * fract(p * C.www) - 1.0;
   vec3 h = abs(x) - 0.5;
   vec3 ox = floor(x + 0.5);
   vec3 a0 = x - ox;

 // Normalise gradients implicitly by scaling m
 // Approximation of: m *= inversesqrt( a0*a0 + h*h );
   m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);

 // Compute final noise value at P
   vec3 g;
   g.x = a0.x * x0.x + h.x * x0.y;
   g.yz = a0.yz * x12.xz + h.yz * x12.yw;
   return 130.0 * dot(m, g);
}
float snoise3D(vec3 v) {
   const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0);
   const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);

 // First corner
   vec3 i = floor(v + dot(v, C.yyy));
   vec3 x0 = v - i + dot(i, C.xxx);

 // Other corners
   vec3 g = step(x0.yzx, x0.xyz);
   vec3 l = 1.0 - g;
   vec3 i1 = min(g.xyz, l.zxy);
   vec3 i2 = max(g.xyz, l.zxy);

 //   x0 = x0 - 0.0 + 0.0 * C.xxx;
 //   x1 = x0 - i1  + 1.0 * C.xxx;
 //   x2 = x0 - i2  + 2.0 * C.xxx;
 //   x3 = x0 - 1.0 + 3.0 * C.xxx;
   vec3 x1 = x0 - i1 + C.xxx;
   vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
   vec3 x3 = x0 - D.yyy;      // -1.0+3.0*C.x = -0.5 = -D.y

 // Permutations
   i = mod289(i);
   vec4 p = permute(permute(permute(i.z + vec4(0.0, i1.z, i2.z, 1.0)) + i.y + vec4(0.0, i1.y, i2.y, 1.0)) + i.x + vec4(0.0, i1.x, i2.x, 1.0));

 // Gradients: 7x7 points over a square, mapped onto an octahedron.
 // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
   float n_ = 0.142857142857; // 1.0/7.0
   vec3 ns = n_ * D.wyz - D.xzx;

   vec4 j = p - 49.0 * floor(p * ns.z * ns.z);  //  mod(p,7*7)

   vec4 x_ = floor(j * ns.z);
   vec4 y_ = floor(j - 7.0 * x_);    // mod(j,N)

   vec4 x = x_ * ns.x + ns.yyyy;
   vec4 y = y_ * ns.x + ns.yyyy;
   vec4 h = 1.0 - abs(x) - abs(y);

   vec4 b0 = vec4(x.xy, y.xy);
   vec4 b1 = vec4(x.zw, y.zw);

 //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
 //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
   vec4 s0 = floor(b0) * 2.0 + 1.0;
   vec4 s1 = floor(b1) * 2.0 + 1.0;
   vec4 sh = -step(h, vec4(0.0));

   vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;
   vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww;

   vec3 p0 = vec3(a0.xy, h.x);
   vec3 p1 = vec3(a0.zw, h.y);
   vec3 p2 = vec3(a1.xy, h.z);
   vec3 p3 = vec3(a1.zw, h.w);

 //Normalise gradients
   vec4 norm = taylorInvSqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
   p0 *= norm.x;
   p1 *= norm.y;
   p2 *= norm.z;
   p3 *= norm.w;

 // Mix final noise value
   vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
   m = m * m;
   return 42.0 * dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));
}
vec3 hsv(float h, float s, float v) {
   vec4 t = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
   vec3 p = abs(fract(vec3(h) + t.xyz) * 6.0 - vec3(t.w));
   return v * mix(vec3(t.x), clamp(p - vec3(t.x), 0.0, 1.0), s);
}
mat2 rotate2D(float r) {
   return mat2(cos(r), sin(r), -sin(r), cos(r));
}
mat3 rotate3D(float angle, vec3 axis) {
   vec3 a = normalize(axis);
   float s = sin(angle);
   float c = cos(angle);
   float r = 1.0 - c;
   return mat3(a.x * a.x * r + c, a.y * a.x * r + a.z * s, a.z * a.x * r - a.y * s, a.x * a.y * r - a.z * s, a.y * a.y * r + c, a.z * a.y * r + a.x * s, a.x * a.z * r + a.y * s, a.y * a.z * r - a.x * s, a.z * a.z * r + c);
}
const float PI = 3.141592653589793;
const float PI2 = PI * 2.0;
const float TAU = PI * 0.5;

// =====================

// Subtract: Obj1 - Obj2
float opS(float d1, float d2) {
   return max(d1, -d2);
}

// Union: Obj1 + Obj2
float opU(float d1, float d2) {
   return min(d1, d2);
}

// Intersection: Obj1 & Obj2
float opI(float d1, float d2) {
   return max(d1, d2);
}

float sdSphere(vec3 p, float s) {
   return length(p) - s;
}

// t: {radius, tube radius}
float sdTorus(vec3 p, vec2 t) {
   vec2 q = vec2(length(p.xz) - t.x, p.y);
   return length(q) - t.y;
}

float length_toPowNegative8(vec2 p) {
   p = p * p;
   p = p * p;
   p = p * p;
   return pow(p.x + p.y, 1.0 / 8.0);
}

float sdTorus82(vec3 p, vec2 t) {
   vec2 q = vec2(length(p.xz) - t.x, p.y);
   return length_toPowNegative8(q) - t.y;
}

float sdTorus88(vec3 p, vec2 t) {
   vec2 q = vec2(length_toPowNegative8(p.xz) - t.x, p.y);
   return length_toPowNegative8(q) - t.y;
}

float sd2Circle(vec2 p, float r) {
   return length(p) - r;
}

float sd2Circle(vec2 p, vec2 c, float r) {
   return length(c - p) - r;
}

float sd2Arc(vec2 p, vec2 o, float r1, float r2, float t) {
   if (t > 1. - 1e-3)
       return opS(sd2Circle(p, o, r1), sd2Circle(p, o, r2));

   float a = t * 2. * PI;
   float s = sin(a);
   float c = -cos(a);
   mat2 m = mat2(c, s, -s, c);
   float arc1 = opS(opS(sd2Circle(p, o, r1), sd2Circle(p, o, r2)), p.x);
   float arc2 = opS(opS(sd2Circle(p, o, r1), sd2Circle(p, o, r2)), (p * m).x);

   return mix(opI(arc1, arc2), opU(arc1, arc2), step(.5, t));
}

float sd2Square(vec2 p, vec2 pos, float h, float angle) {
   vec2 l = p - pos;
   l *= rotate2D(angle);
   vec2 d = abs(l) - h;
   float outside = length(max(d, 0.));
   float inside = min(max(d.x, d.y), 0.);
   return outside + inside;
}

float sd2Line(in vec2 p, in vec2 dir, in float s) {
   p -= .5;
   vec2 perpendicular = vec2(-dir.y, dir.x);

   if (dot(p, dir) > 0.) {
       return abs(dot(p, perpendicular)) - s;
   } else {
       return 1.;
   }
}

// symU and rotU apply to vectors that range from 0 to 1
void symU(inout vec2 u) {
   u.x = 1. - u.x;
}
void rotU(inout vec2 u) {
   u.xy = vec2(u.y, 1. - u.x);
}
// symV and rotV apply to unit vectors that range from -1 to 1
void symV(inout vec2 v) {
   v.x = -v.x;
}
void rotV(inout vec2 v) {
   v.xy = vec2(v.y, -v.x);
}

float sd2Hilbert(in vec2 p, float width, float mTime) {
   const float iter = 7.;

   float t = 0.0;

   vec2 U = p;
   vec2 I = vec2(1, 0);
   vec2 J = vec2(0, 1);
   vec2 L = -I;
   vec2 R;

   vec2 qU;

   for (float i = 0.; i < iter; i++) {
       qU = step(.5, U);         // select quadrant
       bvec2 q = bvec2(qU);          // convert to boolean

       float h = 1. / pow(4., i + 1.);
       t += h * (q.x ? (2. + qU.y) : (1. - qU.y));

       U = 2. * U - qU;          // go to new quadrant

       // qU:               q:
       //      0,1  | 1,1      f,t | t,t
       //     ------|-----    -----------
       //      0,0  | 1,0      f,f | t,f
       // L:                R:
       //       L  | -J       -J |  I
       //     -----|-----    ----------
       //       J  | -I        I |  J

           // node left segment
       L = q.x ? (q.y ? -J : -I) : (q.y ? L : J);

       R = (q.x == q.y) ? I : (q.y ? -J : J);    // node right segment

       if (q.x) { // sym
           symU(U);
           symV(L);
           symV(R);
           vec2 tmp = L;
           L = R;
           R = tmp;
       }
       if (q.y) { // rot+sym
           rotU(U);
           symU(U);
           rotV(L);
           symV(L);
           rotV(R);
           symV(R);
       }
   }

   float s = width * pow(2., iter);
   s *= mix(.5, 2.5, snoise2D(vec2(t, 0.) * pow(2., iter + 1.) + mTime * .4) * .5 + .5);

   return opU(sd2Line(U, L, s), sd2Line(U, R, s));
}

mat4 rotate_xz(float x) {
   return mat4(cos(x), 0.0, -sin(x), 0.0, 0.0, 1.0, 0.0, 0.0, sin(x), 0.0, cos(x), 0.0, 0.0, 0.0, 0.0, 1.0);
}

mat4 rotate_xy(float x) {
   return mat4(cos(x), -sin(x), 0.0, 0.0, sin(x), cos(x), 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0);
}

vec3 rotate(const vec3 p, mat4 m) {
   return vec3(m * vec4(p, 1.0));
}

float rand(vec2 seed) {
   return fract(sin(dot(seed.xy, vec2(12.9898, 78.233))) * 43758.5453);
}

vec2 rand22(vec2 p) {
   vec3 p3 = fract(vec3(p.xyx) * vec3(.1031, .1030, .0973));
   p3 += dot(p3, p3.yzx + 33.33);
   return fract((p3.xx + p3.yz) * p3.zy);
}

float rand(inout vec2 v, inout float state) {
   v = (1. / 4320.) * v + vec2(0.25, 0.);
   state = fract(dot(v * v, vec2(3571)));
   return fract(state * state * (3571. * 2.));
}

vec3 randomSpherePoint(vec3 rand) {
   float ang1 = (rand.x + 1.0) * PI; // [-1..1) -> [0..2*PI)
   float u = rand.y; // [-1..1), cos and acos(2v-1) cancel each other out, so we arrive at [-1..1)
   float u2 = u * u;
   float sqrt1MinusU2 = sqrt(1.0 - u2);
   float x = sqrt1MinusU2 * cos(ang1);
   float y = sqrt1MinusU2 * sin(ang1);
   float z = u;
   return vec3(x, y, z);
}

vec3 rand_in_unit_sphere(vec2 co) {
   float s = co.x + co.y;
   vec3 sp = randomSpherePoint(vec3(rand(co, s), rand(co, s), rand(co, s)));
   float r = pow(rand(co, s), 1. / 3.);
   return sp * r;
}

float saturate(float x) {
   return clamp(x, 0., 1.);
}

float unlerp(float a, float b, float x) {
   return saturate((x - a) / (b - a));
}

float ease_in(float from, float to, float t) {
   return mix(from, to, pow(t, 4.));
}
float ease_out(float from, float to, float t) {
   return mix(to, from, pow(1. - t, 4.));
}
float ease_in_out(float from, float to, float t) {
   return mix(from, to, mix(4. * t * t * t, 1. - pow(-2. * t + 2., 3.) * .5, step(.5, t)));
}

//==== TIMELINE

int timelineStep = 0;
float mTime;
float phaseT;

#define NEXT(phase) phase + 1
const int P_START = 0;
const float P_START_T = 0.;
const float P_START_E = P_START_T + 2.;
const int P_CIRC = NEXT(P_START);
const float P_CIRC_T = P_START_E;
const float P_CIRC_E = P_CIRC_T + 1.;
const int P_BALL = NEXT(P_CIRC);
const float P_BALL_T = P_CIRC_E;
const float P_BALL_E = P_BALL_T + 60.;
const int P_GLITCH = NEXT(P_BALL);
const float P_GLITCH_T = P_BALL_E;
const float P_GLITCH_E = P_GLITCH_T + 25.;
const int P_END = NEXT(P_GLITCH);
const float P_END_T = P_GLITCH_E;
const float P_END_E = P_END_T + 2.;
const float END_T = P_END_E;

#define T_START_PROGRESS ease_in_out(0., 1., unlerp(.0, .9, phaseT))
#define T_START_FILL ease_in(1., 0., unlerp(.9, 1., phaseT))
#define T_CIRC_SIZE ease_in(1., 0., unlerp(.3, 1., phaseT))
#define T_CIRC_ROTATE sqrt(1. - pow(phaseT - 1., 2.))
#define T_BALL_SIZE ease_out(.8, 1., unlerp(0., .02, phaseT))
#define T_BALL_FLOOR_FADE ease_in(0., 1., saturate(phaseT / .1))
#define T_BALL_FLOOR_TINT unlerp(.7, .85, phaseT)
#define T_BALL_LIGHT_ROTATE smoothstep(0., .2, phaseT)
#define T_BALL_LIGHT_FADE ease_out(1., .2, unlerp(.02, .2, phaseT))
#define T_BALL_SQUARES_FADE smoothstep(.1, .2, phaseT)
#define T_BALL_CURVE_FADE unlerp(.2, .4, phaseT)
#define T_BALL_ROTATE ease_in_out(0., 1., unlerp(.5, 1., phaseT))
#define T_BALL_ROTATE_UV clamp(phaseT, 0., .75)
#define T_GLITCH_LIGHT_ROTATE (P_BALL_E + unlerp(.0, .025, phaseT) * .5)
#define T_GLITCH_BALL_JITTER (1. - abs(ease_out(-1., 1., saturate(phaseT / .05))))
#define T_GLITCH_MODEL mix( mix(.0, 1., unlerp(.25,.6,phaseT)), mix(.5, 1., unlerp(.25,.6,phaseT)), rand(vec2(phaseT)) )
#define T_GLITCH_RING_TWIST ease_in(0., 1., unlerp(.55, .6, phaseT))
#define T_GLITCH_RING_ROTATE clamp(phaseT, .0, .585)
#define T_GLITCH_UNLIT step(.6, phaseT)
#define T_GLITCH_BLOCK step(.6, phaseT)
#define T_GLITCH_PROB ease_in(.9, .6, unlerp(.65, 1., phaseT))
#define T_END_SHUTTER ease_out(1., 0., unlerp(.4, .6, phaseT))

#define SET_IF_PHASE(phase, start, end) if(mTime < end){ timelineStep = phase; phaseT = unlerp(start, end, mTime); }

mat4 transform;

void setup() {
   mTime = mod(time, END_T);

   SET_IF_PHASE(P_START, P_START_T, P_START_E) else SET_IF_PHASE(P_CIRC, P_CIRC_T, P_CIRC_E) else SET_IF_PHASE(P_BALL, P_BALL_T, P_BALL_E) else SET_IF_PHASE(P_GLITCH, P_GLITCH_T, P_GLITCH_E) else SET_IF_PHASE(P_END, P_END_T, P_END_E);

   transform = rotate_xz(0.);

   if (timelineStep == P_BALL) {
       transform = rotate_xy(PI * T_BALL_ROTATE);
   }
}

//======================

float sdDist(vec3 position) {
   if (timelineStep == P_BALL) {
       return sdSphere(position, 1.65 * T_BALL_SIZE);
   } else if (timelineStep == P_GLITCH) {
       float m1 = sdSphere(position + .5 * rand_in_unit_sphere(vec2(mTime)) * T_GLITCH_BALL_JITTER, 1.65);
       mat3 r1 = rotate3D(TAU + T_GLITCH_RING_ROTATE * 100., vec3(.9, .5, .1));
       mat3 r2 = rotate3D(T_GLITCH_RING_ROTATE * 30., vec3(1.));
       mat4 r3 = rotate_xz(TAU * .5) * rotate_xy(TAU) * rotate_xz(TAU + TAU * sin(position.y) * T_GLITCH_RING_TWIST);
       float m2 = opU(opU(sdTorus(position * r1, vec2(.2, .05)), sdTorus82(position * r2, vec2(1.6, .1))), sdTorus88(rotate(position, r3), vec2(.6, .2)));
       return mix(m1, m2, step(.5, T_GLITCH_MODEL) * step(T_GLITCH_MODEL, .995));
   } else {
       return 0.;
   }
}

vec3 sdNorm(vec3 pos) {
   vec2 e = vec2(1.0, -1.0) * 0.5773 * 0.0001;
   return normalize(e.xyy * sdDist(pos + e.xyy) +
       e.yyx * sdDist(pos + e.yyx) +
       e.yxy * sdDist(pos + e.yxy) +
       e.xxx * sdDist(pos + e.xxx));
}

// true = reflect
bool lighting3d(in vec3 pos, in int i, out vec2 uv, out vec3 norm) {
   if (timelineStep == P_BALL) {
       vec3 v = normalize(pos);
       const float UVSCALE = 10.;
       uv = vec2(atan(v.x, -v.z), v.y) * UVSCALE;
       if (timelineStep == P_BALL)
           uv *= rotate2D(2. * PI2 * T_BALL_ROTATE_UV);
       uv = uv - mTime * sign(uv);

       norm = normalize(pos);
       vec3 tang = normalize(cross(norm, vec3(0., 1., 0.)));
       norm = rotate(norm, inverse(transform));
       tang = rotate(tang, inverse(transform));

       float randX = rand(vec2(round(uv.x), floor(uv.y)));
       float randY = rand(vec2(floor(uv.x), round(uv.y)) + vec2(29.273, 1.378));
       vec2 sideOn = step(.25, vec2(randX, randY));
       uv = fract(uv);
       const float W = .15;
       vec4 corners = vec4(step(uv.x + uv.y, 1.), step(1., uv.x + uv.y), step(uv.x - uv.y, 0.), step(0., uv.x - uv.y));
       vec4 sideFill = clamp(vec4(corners.xxyy * corners.zwwz + (1. - sideOn.yxyx)), vec4(0.), vec4(1.));
       vec4 sides = vec4(step(uv, vec2(W)), step(1. - W, uv)) * sideFill * sideOn.xyxy;
       if (sides.x + sides.y + sides.z + sides.w > 0.) {
           vec3 ex1 = normalize(vec3(1., 0., 1.));
           vec3 ex2 = normalize(vec3(-1., 0., 1.));
           vec3 ey1 = normalize(vec3(0., -1., 1.));
           vec3 ey2 = normalize(vec3(0., 1., 1.));
           vec3 ez = vec3(0., 0., 1.);

           vec4 uuvv = vec4(1. - uv / W, (uv - 1. + W) / W);
           float tx = dot(sides.xz, uuvv.xz);
           vec3 Nx = (sides.x + sides.z) * mix(ez, mix(ex1, ex2, sides.x), tx * tx);
           float ty = dot(sides.yw, uuvv.yw);
           vec3 Ny = (sides.y + sides.w) * mix(ez, mix(ey1, ey2, sides.y), ty * ty);

           vec3 bitan = normalize(cross(norm, tang));
           mat3 m = mat3(tang, bitan, norm);
           norm = m * normalize(Nx + Ny);
       }
       norm = normalize(norm + .2 * rand_in_unit_sphere(uv.xy));

       return true;
   } else {
       norm = sdNorm(pos);
       norm = rotate(norm, inverse(transform));
       return !(timelineStep == P_GLITCH && T_GLITCH_RING_TWIST > .99);
   }
}

const vec3 COLOR_GRN = vec3(0.2, .3, 0.);
const vec3 COLOR_RED = vec3(.3, 0.0, 0.1);
const vec3 COLOR_BLU = vec3(0., 0.2, 0.4);
bool lighting2d(in vec2 p, in int i, out vec3 color) {
   if (timelineStep == P_CIRC) {
       if (i == 0)
           color += vec3(.5);
       else if (i == 1)
           color += COLOR_GRN;
       else if (i == 2)
           color += COLOR_RED;
       else if (i == 3)
           color += COLOR_BLU;
   } else if (timelineStep == P_END) {
       color += vec3(fract(p.y * 64. + mTime * vec3(.45, .89, .1))) * step(abs(p.y), T_END_SHUTTER);
   } else {
       color = vec3(1.);
   }
   return false;
}

vec3 missColor(vec3 pos, vec3 dir) {
   const vec3 colorD = vec3(0.02, 0.02, 0.04);
   const vec3 colorL = vec3(1.0);

   vec3 color;
   float t2 = (pos.y - 500.0) / dir.y;
   if (t2 > 0.0) {
       if (timelineStep == P_BALL) {
           vec3 p = pos + t2 * dir * .99;
           vec2 uv = p.xz / vec2(300. * sign(p.x), 700.);
           float wave = sin(uv.y + uv.x - mTime * 10.) * .5 + .2;
           float fade = exp(-abs(p.z) / mix(5., 3000., T_BALL_FLOOR_FADE));
           color = mix(colorD, colorL, wave * fade);
           color *= hsv(fract(uv.y / 20. + mTime * .25), T_BALL_FLOOR_TINT, 1.);
       } else if (timelineStep == P_GLITCH) {
           vec3 p = pos + t2 * dir * .99;
           vec2 uv = p.xz / vec2(300. * sign(p.x), 700.);
           float wave = sin(uv.y + uv.x - mTime * 10.) * .5 + .2;
           float fade = exp(-abs(p.z) / 3000.);
           float w = mix(wave, step(0., wave), T_GLITCH_UNLIT);
           color = mix(colorD, colorL, w * fade);
       }
   } else if (timelineStep == P_BALL || timelineStep == P_GLITCH) {
       vec2 r = resolution;
       vec2 pix = (dir.xy / dir.z + vec2(.5 * r.x / r.y, .5)) * r.y;
       vec2 uvBg = (pix - r) / max(r.x, r.y);
       vec2 uv = uvBg * step(pos.z, -4.99);
       vec2 p = fract(uv);
       vec2 num = vec2(10., 10.);
       for (int i = 0; i < 100; i++) {
           float x = float(i) / num.x;
           float t = mTime * .5;
           float y = mod(float(i) - t, num.y);
           float y_id = y + t;
           vec2 seed = vec2(x, y_id);
           float s = float(i);
           vec2 pos = (vec2(x, y) + rand22(vec2(x, y_id) * .1)) * (1. / num.x) + vec2(0., .5);
           float r1 = rand(seed, s);
           float r2 = .5 - rand(seed, s);
           float g1 = sign(r2) * 10. * step(abs(r2), .1) * step(.5, float(timelineStep - P_BALL));
           float g2 = sign(r2) * 2. * sin(mTime * .5) * step(.2, abs(r2)) * step(.5, float(timelineStep - P_BALL));
           float d = sd2Square(p, pos, mix(.01, .02, r1), mTime * r2 + g1 + g2);
           color += step(d, 0.) * mix(vec3(.1) * T_BALL_SQUARES_FADE, vec3(.5), step(.5, float(timelineStep - P_BALL)));
       }

       if (timelineStep == P_BALL) {
           vec2 uvHb = (pix.xy * 2. - r) / min(r.x, r.y) * step(pos.z, -4.99);
           uvHb *= rotate2D(.05 * mTime);
           uvHb = uvHb * (.4 / max(r.x, r.y) * min(r.x, r.y)) + .5;
           float hb = sd2Hilbert(uvHb, 1. / max(r.x, r.y), mTime);
           color += step(hb, 0.) * vec3(.1) * T_BALL_CURVE_FADE;
           color += mix(colorD, colorL * .5, dir.y * dir.y);
       } else if (timelineStep == P_GLITCH) {
           color += mix(colorD, colorL * .5, dir.y * dir.y);
           color = mix(color, fract(dir * 4.).xyy * .7, T_GLITCH_UNLIT);
       }
   } else {
       color = mix(colorD, colorL, dir.y * dir.y);
   }
   return color;
}

// ==============

float intersect(in vec3 rO, in vec3 rD, in float maxT, out vec3 tRay) {
   float dist, t;
   float res = -1.0;
   t = 0.01;

   for (int i = 0; i < 64; i++) {
       if (t > maxT)
           break;
       vec3 p = rO + t * rD;
       p = rotate(p, transform);

       dist = sdDist(p);

       if (dist < 0.0001) {
           tRay = p;
           res = t;
           break;
       }
       t += dist;
   }

   return res;
}

float intersect2d(vec2 p, int i) {
   if (timelineStep == P_START) {
       if (i == 0) {
           float t = T_START_PROGRESS;
           float r = T_START_FILL;
           return sd2Arc(p, vec2(0.), .2, .18 * r, saturate(t));
       }
   } else if (timelineStep == P_CIRC) {
       if (i == 0) {
           float t = T_CIRC_SIZE;
           return sd2Circle(p, .2 * t);
       } else if (i < 4) {
           float t = T_CIRC_ROTATE;
           float a = PI / 1.5 * float(i) + 2. * PI * t;
           float x = sin(a);
           float y = cos(a);
           return sd2Circle(p, .4 * t * vec2(x, y * T_CIRC_SIZE), .2);
       }
   } else if (timelineStep == P_END) {
       if (i == 0)
           return sd2Circle(p, .35);
       else if (i == 1)
           return -1.;
   }
   return 0.;
}

vec3 lights(in vec3 pos, in vec3 dir) {
   vec3 c = vec3(0.);
   if (timelineStep == P_BALL) {
       vec3 _a;
       vec3 up = T_BALL_LIGHT_ROTATE * vec3(0., 1.2, 0.);
       vec3 l1dir = rotate(vec3(3., 0., 0.), rotate_xz(PI / 6. + PI / 1.5 * 1. + 2. * mTime * T_BALL_LIGHT_ROTATE)) + up - pos;
       vec3 l2dir = rotate(vec3(3., 0., 0.), rotate_xz(PI / 6. + PI / 1.5 * 2. + 2. * mTime * T_BALL_LIGHT_ROTATE)) + up - pos;
       vec3 l3dir = rotate(vec3(3., 0., 0.), rotate_xz(PI / 6. + PI / 1.5 * 3. + 2. * mTime * T_BALL_LIGHT_ROTATE)) + up - pos;
       float v1 = step(intersect(pos, l1dir, 5., _a), 0.);
       float v2 = step(intersect(pos, l2dir, 5., _a), 0.);
       float v3 = step(intersect(pos, l3dir, 5., _a), 0.);
       float l1 = unlerp(.5, 1., dot(dir, normalize(l1dir))) * v1;
       float l2 = unlerp(.5, 1., dot(dir, normalize(l2dir))) * v2;
       float l3 = unlerp(.5, 1., dot(dir, normalize(l3dir))) * v3;
       c += l1 * COLOR_RED + l2 * COLOR_BLU + l3 * COLOR_GRN;
       vec3 scale = vec3(1., 10., 1.);
       l1 = saturate(dot(dir, normalize(scale * l1dir))) * v1;
       l2 = saturate(dot(dir, normalize(scale * l2dir))) * v2;
       l3 = saturate(dot(dir, normalize(scale * l3dir))) * v3;
       c += l1 * COLOR_RED + l2 * COLOR_BLU + l3 * COLOR_GRN;
       c *= 3. * T_BALL_LIGHT_FADE;
   } else if (timelineStep == P_GLITCH) {
       vec3 up = vec3(0., 1.2, 0.);
       vec3 l1dir = rotate(vec3(3., 0., 0.), rotate_xz(PI / 6. + PI / 1.5 * 1. + 2. * T_GLITCH_LIGHT_ROTATE)) + up - pos;
       vec3 l2dir = rotate(vec3(3., 0., 0.), rotate_xz(PI / 6. + PI / 1.5 * 2. + 2. * T_GLITCH_LIGHT_ROTATE)) + up - pos;
       vec3 l3dir = rotate(vec3(3., 0., 0.), rotate_xz(PI / 6. + PI / 1.5 * 3. + 2. * T_GLITCH_LIGHT_ROTATE)) + up - pos;
       vec3 scale = vec3(1., 10., 1.);
       float l1 = saturate(dot(dir, normalize(scale * l1dir)));
       float l2 = saturate(dot(dir, normalize(scale * l2dir)));
       float l3 = saturate(dot(dir, normalize(scale * l3dir)));
       c += l1 * COLOR_RED + l2 * COLOR_BLU + l3 * COLOR_GRN;
       c *= 2.;
   }
   return c;
}

vec3 trace(vec2 p, vec3 _pos, vec3 _dir) {
   vec3 color = vec3(0.0);
   vec3 w = vec3(1.0);
   vec3 pos = _pos, dir = _dir;

   bool trace3d = timelineStep == P_BALL || timelineStep == P_GLITCH;

   for (int i = 0; i < 4; i++) {
       if (trace3d) {
           vec3 view;
           float t = intersect(pos, dir, 50.0, view);
           if (t > 0.0) {
               vec3 inter = pos + t * dir * .99;

               pos = inter;

               vec3 nor;
               vec2 uv;
               bool refl = lighting3d(view, 0, uv, nor);
               color += lights(pos, nor);
               if (!refl) {
                   return w * color;
               }
               dir = reflect(dir, nor);
               w *= vec3(.9, .88, .85);
           } else {
               color += missColor(pos, dir);
               return w * color;
           }
       } else {
           for (int j = 0; j < 20; j++) {
               float t = intersect2d(p, j);
               if (t < 0. && lighting2d(p, j, color)) {
                   break;
               }
           }
           return w * color;
       }
   }

   return vec3(0.);
}

vec3 jitter_cam(vec2 pix, vec3 dir) {
   vec2 block = floor(pix / 32.);
   float line = floor(pix.y / 64.);
   if (timelineStep == P_GLITCH) {
       float s = floor(mTime / .1);
       float r1 = snoise3D(vec3(block * 25., s)) * saturate(1.25 - length(dir.xy));
       float r2 = rand(vec2(floor(mTime / .2), line));
       if (r2 > .9) {
           dir.xy += .02 * rand(vec2(line)) * T_GLITCH_BLOCK;
       } else if (r1 == .7) {
           dir.xy += .05 * rand22(block.yx) * T_GLITCH_BLOCK;
       }
   }
   return normalize(dir);
}

void glitch_color(vec2 pix, inout vec3 color) {
   vec2 block = floor(pix / 32.);
   if (timelineStep == P_GLITCH) {
       float s = floor(mTime / .01);
       float r = snoise3D(vec3(block * vec2(.05, 2.), s));
       if (r > T_GLITCH_PROB)
           color *= vec3(1., 1., 1.) - vec3(1., 0., 1.) * T_GLITCH_BLOCK;
       else if (rand(vec2(floor(mTime / .5))) > .9)
           color = color.rrr;
   }
}

void main() {
   vec2 r = resolution, p = gl_FragCoord.xy / r.y - vec2(.5 * r.x / r.y, .5);

   setup();

   vec3 vdir = normalize(vec3(p, 1.0));
   vec3 pcam = vec3(0.0, 0.0, -5.0);

   // glitch
   vdir = jitter_cam(gl_FragCoord.xy, vdir);
   vec3 color = trace(p, pcam, vdir);
   glitch_color(gl_FragCoord.xy, color);
   OUT_COLOR = vec4(color, 1);
}