_='i g)g[i.Xch(/^..|[A-Z]|\\d\\w$/g).jo("")g[i];D=[];fx x*x*(3-2*xJ;Lx,y,t(1-tKx+t*y};x=16;xt=Nndom(K6.#D.push({x:y:s(tJZ@Flo32I@Ut16*2x=W=G8;xy=W;ya=xYh=yYl=~~a+(am=~~h+(hn=lo=mj=(a-=l`k=(h-=m`pl+mqn+mrl+osn+oV=x+y*W,U=V*3x!y!L(La+ph,qj+qhL(ra+rk,sj+skf(h)max(y,x)<G7&&(T=2*U>TV1W>T+3V+W+1p=crP(Ss,tHc=crS shOc,scoOcS,cJ,O"tribute 3 v;uniform flo t;Posit%=4(X3(Q,??-?.7KX3(-s s QKv,Qc=.3-v;3O"FNgColor=4(c.zzz,Q2liPusPv=geAL,"v"enVAA(vy=34962,++,Z,IveAP(v,#5G6,0,0,0en(2929RHun1f(geUL,"t"xdrE(4,98304,5G#0x+=.01;requestAniX%FNme(RJ,R(varyg highp 3 c;void ma(Hgl_=funct%(),>++TI[T+2V+biB(y,crB()buD(y0.,,Z[U++Mh. (tArNy(49152for(*4],at);inHreturn=D[.x*.y*vec--;)cos with(g)}",3563<0&&-1]=(p,f(a),y+81!/W-.5#3,%ion>,I[?.7,@=new G12H){J)}K)*NraOS(Q1.XmY/32,`)-1,+1&#';for(Y in $='`YXQONKJHG@?>%#! ')with(_.split($[Y]))_=join(pop());eval(_)
for(i in g) g[i.match(/^..|[A-Z]|\d\w$/g).join("")]=g[i]
D = []
// Bilinear interpolating polynomial. Usually 6x^5 -15x^4 + 10x^3 is
// better, but we don't need a zero second derivative right now so we use
// ye olde -2x^3 + 3x^2 to save a bit of space
//f = function(x) { return x * x * x * (x * (6 * x - 15) + 10); },
f = function(x) { return x * x * (3 - 2 * x); }
// Lerp(x, y, t) function
L = function(x,y,t) { return (1-t) * x + t * y; }
// Generate pseudorandom lattice gradients from a
// random unit length polar coordinate
for(x = 16; x--;)
t = Math.random() * 6.3,
D.push({x:Math.cos(t), y:Math.sin(t)})
// Arrays that will contain vertices and indices for WebGL
Z = new Float32Array(49152)
I = new Uint16Array(49152*2)
// At any point in the evaluation loop, the following holds:
// Distances of the evaluated point from the lattice points:
// a -> from left, h -> from top, j -> from right, k -> from bottom
// are stored in the variables a, h, j, k
// Lattice points:
// (l,m) -> top left, (n, m) -> top right
// (l,o) -> bottom left, (n, o) -> bottom right,
// are stored in the variables l, m, n, o
// Lattice gradients are stored in the variables p, q, r, s
for(x = W = 128; x--;) for(y = W; y--;)
// Map the intervals [0, W] x [0, H] to [0, S] x [0, S]
// Let a and h be the mapped x and y
a = x / 32,
h = y / 32,
// Let (l, m) be the floor of (a, h)
// Let (n, o) be the ceil of (a, h)
l = ~~a + (a < 0 && -1),
m = ~~h + (h < 0 && -1),
n = l + 1 & 3,
o = m + 1 & 3,
// Let (a, h) be the fractional part of the former (a, h)
// Let (j, k) be the distance from the former (a, h) to (n, o)
j = (a -= l) - 1,
k = (h -= m) - 1,
// Let p, q, r, s be the gradients on the four lattice points
// surrounding the evaluation point
p = D[l + m * 4],
q = D[n + m * 4],
r = D[l + o * 4],
s = D[n + o * 4],
// Let U be the index into the ImageData buffer at (x, y).
// Fill RGB with the value of the noise at (x, y) and the A
// component with 255.
V = x + y * W,
U = V * 3,
Z[U++] = x / W - .5,
Z[U++] = y / W - .5,
// Let now p, q, r, s be the inner products of the lattice gradients
// with the distance of each of those from the evaluation point.
// Noise value is calculated by bilinearly interpolating:
// - between p and q at value f(a),
// - between r and s at value f(a), and
// - between the result of the two interpolations at value f(h)
// which is the evaluation of the interpolating polynomial at h.
Z[U++] = L(
L(p.x * a + p.y * h, q.x * j + q.y * h, f(a)),
L(r.x * a + r.y * k, s.x * j + s.y * k, f(a)),
f(h)
),
// Setup 6 indices for the two triangles making up a quadrilateral
Math.max(y,x) < 127 && (
T = 2 * U,
I[T] = V,
I[++T] = I[T + 2] = V + 1,
I[++T] = I[T + 2] = V + W,
I[T + 3] = V + W + 1
)
with(g)
p = crP(),
// Create, compile and attach shaders
S = function (s,t) { with(g) c = crS(t), shS(c,s), coS(c), atS(p,c) },
// I originally tried computing the components of the vector directly
// (e.g. vec3(v.x, C * v.y - S * v.z, S * v.y + C * v.z)) but in the end
// I found out that, while seemingly wasteful, the two matrix multiplications
// introduced a bit of redundancy that let me shave off a few bytes from the
// vertex shader. Trivia: the two versions were exactly the same length anyway.
// Adding +.3 to the color was a tough choice, but without it the result was
// quite ugly, so I sacrificed these 3 bytes.
S('attribute vec3 v;uniform float t;varying highp vec3 c;void main(){gl_Position=vec4(mat3(1.,0.,0.,0.,.7,.7,0.,-.7,.7)*mat3(cos(t),-sin(t),0.,sin(t),cos(t),0.,0.,0.,1.)*v,1.);c=.3-v;}',35633),
// `c` was originally a float, changed it to a vec3 so I could write
// c.zzz instead of vec4(c,c,c,1.). Used vec3 so I could avoid
// extracting the z component from v in the vertex shader, saving a
// couple of bytes. Big thanks to @kuvos for the tip!
// The hairiest thing is the need to cast to vec4; in Chrome ignoring
// it works without a problem, but other browsers need the w
// component set or most of them get mad. Tricks I tried on the vertex
// shader to somehow include the w=1. component into a vec4-typed c
// (eg. assigning gl_Position=c=...) resulted in one or two extra bytes
// needed, so I had to discard them.
S('varying highp vec3 c;void main(){gl_FragColor=vec4(c.zzz,1.);}',35632),
// Link and use program
liP(p),
usP(p),
v = geAL(p, 'v'),
enVAA(v),
// Bind buffers and send data to the GPU.
// It took quite a bit of experimentation to reduce the byte count here.
y = 34962,
biB(y, crB()),
buD(y++, Z, y+81),
biB(y, crB()),
buD(y, I, y+81),
veAP(v, 3, 5126, 0, 0, 0),
en(2929),
// I used x which was 0 after the Perlin noise evaluation loop. Not only
// looping backwards let me save the comparison bytes, it also gave me
// this little present that I didn't notice until the end so I don't have
// to reassing 0 to some other variable. x is the rotation angle about the
// Y axis which is passed as a uniform to the shader program.
R = function() {
un1f(geUL(p, 't'), x)
drE(4, 98304, 5123, 0)
x += .01
requestAnimationFrame(R);
},
R()