const lineWeight = .10065;
const bezControlPoints = [[0, -.75], [.75, -.75], [.75, .25], [0, .25]];
const stemPoints = [[-.75, .5], [0, -.5]];
const dashPoints = [[-.75, -.25], [-.5, .25]];
const colours = [[5/255, 100/255, 1], [30/255, 50/255, 170/255], [130/255, 175/255, 1]];
invCubic: (t=>1-(1-t)*(1-t)*(1-t)),
smoothStep: (t=>t*t*(3-2*t)),
smootherStep: (t=>t*t*t*(t*(t*6-15)+10)),
loop: (t=>1-abs((0.5-t)*2)),
nTimes: (n=>(t=>fract(t*n))),
const comp = (...fns)=>fns.reduce((a,b)=>(x=>a(b(x))),(x=>x));
const dirVec = ([x1,y1],[x2,y2])=>[x2-x1,y2-y1];
const dist = ([x1,y1],[x2,y2])=>Math.hypot(x2-x1,y2-y1);
const normalise = (x,y)=>{let l = Math.sqrt((x*x)+(y*y)); return l==0?[x,y]:[x/l, y/l];}
const flip = (x,y)=>[-y,x];
const bezP = (a,b,c,d,t) => Array.isArray(a)?a.map((x,i)=>bezP(a[i], b[i], c[i], d[i], t)):Math.pow(1-t,3)*a+3*Math.pow(1-t,2)*t*b+3*(1-t)*(t*t)*c+Math.pow(t,3)*d;
const bezT = (a,b,c,d,t) => Array.isArray(a)?a.map((x,i)=>bezT(a[i], b[i], c[i], d[i], t)):-3*Math.pow(1-t,2)*a+3*Math.pow(1-t,2)*b-6*t*(1-t)*b-3*Math.pow(t,2)*c+6*t*(1-t)*c+3*Math.pow(t,2)*d;
const lerpVec = ([x1,y1],[x2,y2],t) => [x1+t*(x2-x1),y1+t*(y2-y1)];
const moveVec = ([x1,y1],[x2,y2],f)=>[x1+x2*f,y1+y2*f];
const addVec = (a,b)=>moveVec(a,b,1);
const invVec = ([x1,y1])=>[-x1,-y1];
[...Array(pathRes)].map((x,i)=>({
point: bezP(...bezControlPoints, i/(pathRes-1)),
normal: flip(...normalise(...bezT(...bezControlPoints, i/(pathRes-1)))),
[...Array(pathRes)].map((x,i)=>({
point:lerpVec(...stemPoints, i/(pathRes-1)),
normal: flip(...normalise(...dirVec(...stemPoints))),
[...Array(pathRes)].map((x,i)=>({
point:lerpVec(...dashPoints, i/(pathRes-1)),
normal: flip(...normalise(...dirVec(...dashPoints))),
const genBounds = (p,w)=>p.reduce((acc,v)=>[moveVec(v.point, v.normal, w), ...acc, moveVec(v.point, v.normal, -w)], []);
const bounds = paths.map(p=>genBounds(p,lineWeight));
const collide = ([x,y])=>{
if(x>1||x<=-1||y>1||y<=-1) { return [2,2,2]; }
let d = dim*pixelDensity();
let off = floor((floor((-y*.5+.5)*d) * d + floor((x*.5+.5)*d))*4);
return [collidePixels[off], collidePixels[off+1], collidePixels[off+2]].map(i=>i/255*2-1);
const drawGrid = (ctx)=>{
for(let i = -1; i < 1; i+=.25) {
ctx.background(242/255,247/255,255);
ctx.fill(...colours[bi]);
b.forEach((p,i)=>ctx.vertex(...p.map((v,j)=>v)))
ctx.background(242/255,247/255,255);
ctx.fill(...colours[bi]);
b.forEach((p,i)=>ctx.vertex(...p.map((v,j)=>v+(sin(p[0]*p[1]*PI*8*(comp(t.smoothStep, t.loop)(gt)*2-1))*0.1))))
ctx.background(242/255,247/255,255);
for(let k = 0; k < lerp(0, 20, comp(t.invCubic, t.loop)(gt)); k++) {
ctx.scale(lerp(1.00,0.95,t.loop(gt)));
ctx.rotate(lerp(0,2,t.smoothStep(gt))*PI);
ctx.fill(...colours[bi]);
b.forEach((p,i)=>ctx.vertex(...p.map((v,j)=>v)))
ctx.background(242/255,247/255,255);
for(let k = 0; k < 30; k++) {
ctx.scale(1-0.05*(sin(gt*PI*k)*.5+.5));
ctx.stroke(...colours[bi]);
b.forEach((p,i)=>ctx.line(...p, ...b[(i+1)%b.length]));
ctx.background(242/255,247/255,255);
for(let k = 60; k > 0; k--) {
ctx.scale(Math.pow(lerp(.95,.97,gt),k));
ctx.translate(lerp(-0.01, -0.04, gt)*k, lerp(-0.04, -0.01, gt)*k);
ctx.fill(...colours[k%3]);
b.slice().reverse().forEach((p,i)=>ctx.vertex(...p.map((v,j)=>v)))
ctx.background(242/255,247/255,255);
ctx.scale(lerp(0.1,0.5,comp(t.smoothStep, t.loop)(gt)));
for(let k = 0; k < lerp(80,30,comp(t.smoothStep, t.loop)(gt)); k++) {
ctx.translate(0.005*(sin(gt*k)*.5+.5), 0.005*(cos(gt*k)*.5+.5));
ctx.stroke(...colours[bi]);
b.forEach((p,i)=>ctx.line(...p.map(x=>x+sin(gt*PI+x)*.1), ...b[(i+1)%b.length]));
ctx.background(242/255,247/255,255);
ctx.stroke(...colours[bi]);
b.forEach((p,i)=>ctx.line(...p, ...b[(b.length-i+1)%b.length]));
b.forEach((p,i)=>ctx.line(...p, ...b[(b.length-i-1)%b.length]));
ctx.background(242/255,247/255,255);
for(let k = 0; k < 10; k++) {
ctx.translate(0.05*sin(gt*k),0.05*sin(gt*k));
ctx.stroke(...colours[bi]);
b.forEach((p,i)=>ctx.curve(...b[(b.length-i+1)%b.length], ...p, ...b[(i+1)%b.length], ...b[(b.length-i-1)%b.length]))
let boids = [...Array(300)].map(()=>({
point: random([[0,0], [1,0], [0,1], [1,1]]),
heading: [random(),random()],
colour: colours[Math.random()*colours.length | 0],
let sepF = lerp(.7,.8,noise(loop,1));
let aliF = lerp(.4,.8,noise(loop,2));
let cohF = lerp(.4,.8,noise(loop,3));
ctx.background(242/255,247/255,255, 0.005);
near = boids.filter(a=>dist(a.point,b.point)<0.1);
sepDir = normalise(...(near.reduce((acc,a)=>addVec(acc,dirVec(a.point,b.point)), [0,0])));
aliDir = normalise(...near.reduce((acc,a)=>addVec(acc,a.heading), [0,0]));
cohDir = normalise(...dirVec(b.point, near.reduce((acc,a)=>lerpVec(acc,a.point, 0.5), near[0].point)));
b.heading = moveVec(b.heading, sepDir, sepF);
b.heading = moveVec(b.heading, aliDir, aliF);
b.heading = moveVec(b.heading, cohDir, cohF);
b.heading = normalise(...b.heading);
collisions = collide(moveVec(b.point, b.heading, speed));
if(collisions.some(x=>x<0)) { b.heading = invVec(b.heading); }
b.point = moveVec(b.point, b.heading, speed);
b.point = b.point.map(i=>i>1||i<-1?-i:i);
ctx.ellipse(...b.point, 0.06, 0.06);
let boids = [...Array(300)].map(()=>({
point: random(paths.flat()).point,
heading: [random(),random()],
colour: colours[Math.random()*colours.length | 0],
ctx.background(242/255,247/255,255,0.01);
near = boids.filter(a=>dist(a.point,b.point)<0.05);
sepDir = normalise(...(near.reduce((acc,a)=>addVec(acc,dirVec(a.point,b.point)), [0,0])));
aliDir = normalise(...near.reduce((acc,a)=>addVec(acc,a.heading), [0,0]));
cohDir = normalise(...dirVec(b.point, near.reduce((acc,a)=>lerpVec(acc,a.point, 0.5), near[0].point)));
b.heading = moveVec(b.heading, sepDir, sepF);
b.heading = moveVec(b.heading, aliDir, aliF);
b.heading = moveVec(b.heading, cohDir, cohF);
collisions = collide(moveVec(b.point, b.heading, speed));
if(collisions.every(x=>x>0)) { b.heading = invVec(b.heading); }
b.point = moveVec(b.point, b.heading, speed);
b.point = b.point.map(i=>i>1||i<-1?-i:i);
ctx.ellipse(...b.point, 0.01, 0.01);
ctx.background(242/255,247/255,255, 0.05);
for(let i = -.95; i < .95; i+=0.1) {
for(let j = -.95; j < .95; j+= 0.01) {
p = lerpVec([-.95,i], [.95,i], map(j, -.95, .95, 0, 1));
let c = Math.min(...collide(p));
p[1] = p[1] + sin(p[0]*lerp(20,40, gt))*(c<0?t.loop(gt)*.1:t.loop(1-gt)*.01);
ctx.background(242/255,247/255,255, 0.05);
for(let i = -.95; i < .95; i+=0.05) {
for(let j = -.95; j < .95; j+= 0.01) {
p = lerpVec([-.95,i], [.95,i], map(j, -.95, .95, 0, 1));
let c = Math.min(...col);
ctx.stroke(...colours[col.indexOf(min(...col))])
p[0] = p[0] + sin(p[1]*10)*(c>0?t.loop(gt)*.1:t.loop(1-gt)*-.01);
p[1] = p[1] + sin(p[0]*10)*(c>0?t.loop(gt)*.1:t.loop(1-gt)*-.01);
ctx.background(242/255,247/255,255);
for(let i = 0; i <= 1; i+=0.05) {
for(let j = 0.05; j <= 1; j+= 0.05) {
let p = lerpVec([a, -1], [b, 1], j);
let q = lerpVec([a, -1], [b, 1], j+0.05);
colour[0] = col[0]>0?col[0]:-col[0]/lineWeight;
colour[1] = col[1]>0?col[1]:-col[1]/lineWeight;
colour[2] = col[2]>0?col[2]:-col[2]/lineWeight;
p[1] = p[1] + (sin(mc*20*gt)*.5+.5)*0.05;
p[0] = p[0] + (cos(mc*20*gt)*.5+.5)*0.05;
q[0] = q[0] + (cos(q[0]*2*gt)*.5+.5)*0.5;
p[0] = p[0] + (sin(q[1]*2*gt)*.5+.5)*0.05;
let boids = [...Array(3000)].map(()=>({
point: [random(2)-1,random(2)-1],
let nmag = lerp(1,100,noise(loop));
let doff = lerp(0,2,noise(loop+gt));
ctx.background(242/255,247/255,255, 0.01);
collisions = collide(moveVec(b.point, b.heading, speed));
let d = noise(...b.point.map(x=>x*nmag))+doff;
b.heading[0] = b.heading[0] + cos((d*2-1)*PI)*.1;
b.heading[1] = b.heading[1] + sin((d*2-1)*PI)*.1;
b.heading = normalise(...b.heading);
b.point = moveVec(b.point, b.heading, speed);
b.point = b.point.some(i=>i>1||i<-1)||collisions.some(x=>x<0)?[random(2)-1,random(2)-1]:b.point;
ctx.fill(...colours[i%3]);
ctx.ellipse(...b.point, 0.005, 0.005);
let boids = [...Array(3000)].map(()=>({
point: [random(2)-1,random(2)-1],
let nmag = lerp(1,100,noise(loop));
let doff = lerp(0,2,noise(loop+gt));
ctx.background(242/255,247/255,255, 0.01);
collisions = collide(moveVec(b.point, b.heading, speed));
let d = noise(...b.point.map(x=>x*nmag))+doff;
b.heading[0] = b.heading[0] + cos((d*2-1)*PI)*.1;
b.heading[1] = b.heading[1] + sin((d*2-1)*PI)*.1;
b.heading = normalise(...b.heading);
b.point = moveVec(b.point, b.heading, speed);
b.point = b.point.some(i=>i>1||i<-1)||collisions.every(x=>x>0)?[random(2)-1,random(2)-1]:b.point;
ctx.fill(...colours[i%3]);
ctx.ellipse(...b.point, 0.005, 0.005);
ctx.background(242/255,247/255,255);
for(let i = -1; i <= 1; i+=0.075) {
for(let j = -1; j <= 1; j+= lerp(0.05, 0.075, sin(gt*PI*4)*.5+.5)) {
let sep = lerp(0, 0.03, sin(gt*i*20*PI+gt*j*50*PI)*.5+.5)
let col = collide([i,j]);
ctx.fill(...colours[1].map(x=>col[1]>0?1-col[1]:x));
ctx.ellipse(i+sep,j+sep, col[1]>0?0.05*-col[1]:0.05);
ctx.fill(...colours[2].map(x=>col[2]>0?1-col[2]:x));
ctx.ellipse(i+sep,j-sep, col[2]>0?0.05*-col[2]:0.05);
ctx.fill(...colours[0].map(x=>col[0]>0?1-col[0]:x));
ctx.ellipse(i-sep,j-sep, col[0]>0?0.05*-col[0]:0.05);
attribute vec3 aPosition;
gl_Position = vec4(aPosition*2.-1., 1.0);
uniform sampler2D texA, texB;
#define PI 3.14159265358979323846
//noise from gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83
return fract(sin(dot(c.xy ,vec2(12.9898,78.233))) * 43758.5453);
float noise(vec2 p, float freq ){
vec2 xy = mod(p,unit)/unit;
// xy = 3.*xy*xy-2.*xy*xy*xy;
float a = rand((ij+vec2(0.,0.)));
float b = rand((ij+vec2(1.,0.)));
float c = rand((ij+vec2(0.,1.)));
float d = rand((ij+vec2(1.,1.)));
float x1 = mix(a, b, xy.x);
float x2 = mix(c, d, xy.x);
return mix(x1, x2, xy.y);
float pNoise(vec2 p, int res){
for (int i = 0; i<50; i++){
if (iCount == res) break;
//somewhat inspired by shadertoy.com/view/ldXGW4
//but adapted to flicker between two textures and also split the image when it flickers instead of just scrolling
float xoff = pNoise(vec2(t*15.,uv.y*20.),1)*.01 + pNoise(vec2(t, uv.y*500.),1)*.01;
float yflick = abs(sin(t)*4.)*(1.-step(pNoise(vec2(t*2.5,1.),1),.2))*(1.-step(pNoise(vec2(t*5.5,uv.y),1),.1))*.5;
float yflip = abs((ft)*4.)*(1.-step(pNoise(vec2(ft*15.0,1.),1),.1))*(1.-step(pNoise(vec2(ft*10.,1.),1),.1))*.5;
bool flip = auv.y+yflip>1.0;
auv.y = uv.y+yflick+yflip;
c = vec3(texture2D(texB, auv+vec2(.01,0)).x, texture2D(texB, auv).y, texture2D(texB, auv-vec2(.01,0)).z);
c = vec3(texture2D(texA, auv+vec2(.01,0)).x, texture2D(texA, auv).y, texture2D(texA, auv-vec2(.01,0)).z);
c -= sin(uv.y*1200.0)*0.1;
gl_FragColor = vec4(c,1);;
#define PI 3.14159265358979323846
uniform vec2 polyA[N], polyB[N], polyC[N];
//adapted from iquilezles.org to work with glsl es < 3
float polygon(vec2 p, vec2[N] v )
float d = dot(p-v[0],p-v[0]);
vec2 b = w - e*clamp( dot(w,e)/dot(e,e), 0.0, 1.0 );
bvec3 c = bvec3(p.y>=v[0].y,p.y<v[N-1].y,e.x*w.y>e.y*w.x);
if( all(c) || all(not(c)) ) s*=-1.0;
vec2 b = w - e*clamp( dot(w,e)/dot(e,e), 0.0, 1.0 );
bvec3 c = bvec3(p.y>=v[i].y,p.y<v[i-1].y,e.x*w.y>e.y*w.x);
if( all(c) || all(not(c)) ) s*=-1.0;
float r = polygon(p, polyA);
float g = polygon(p, polyB);
float b = polygon(p, polyC);
r = r<0.?(1.+r)*.5:r*.5+.5;
g = g<0.?(1.+g)*.5:g*.5+.5;
b = b<0.?(1.+b)*.5:b*.5+.5;
// r = r<0.?(1.+r/`+lineWeight+`)*.5:r*.5+.5;
// g = g<0.?(1.+g/`+lineWeight+`)*.5:g*.5+.5;
// b = b<0.?(1.+b/`+lineWeight+`)*.5:b*.5+.5;
gl_FragColor = vec4(r,g,b,1);;
let outShader, collideShader;
if(!unseenOutputs.length) {
unseenOutputs = [...Array(outputs.length)].map((_,i)=>i);
unseenOutputs.splice(unseenOutputs.indexOf(currentOutput),1);
if(gt+step*deltaTime > 1) {
currentOutput = nextOutput;
nextOutput = random(unseenOutputs);
unseenOutputs.splice(unseenOutputs.indexOf(nextOutput),1);
gt = (gt+step*deltaTime)%1;
outputs[currentOutput](ctx[currentOutput]);
outputs[nextOutput](ctx[nextOutput]);
outShader.setUniform("texA", ctx[currentOutput]);
outShader.setUniform("texB", ctx[nextOutput]);
outShader.setUniform("t", gt);
outShader.setUniform("ft", gt>.75?(gt-.75)*4:0);
createCanvas(dim, dim, WEBGL);
setAttributes('antialias', true);
outShader = createShader(...outShaders);
collideShader = createShader(...collideShaders);
collideCtx = createGraphics(dim, dim, WEBGL);
collideShader._renderer = collideCtx._renderer;
collideCtx.colorMode(RGB, 1);
collideCtx.scale(dim/2.);
collideCtx.shader(collideShader);
collideShader.setUniform("polyA", bounds[0].flat());
collideShader.setUniform("polyB", bounds[1].flat());
collideShader.setUniform("polyC", bounds[2].flat());
collidePixels = collideCtx.pixels;
ctx = Array(outputs.length).fill(0).map(_=>createGraphics(dim, dim));
unseenOutputs = [...Array(outputs.length)].map((_,i)=>i);
unseenOutputs.splice(unseenOutputs.indexOf(currentOutput),1);
nextOutput = Math.random()*outputs.length|0;
unseenOutputs.splice(unseenOutputs.indexOf(nextOutput),1);