const N_FRAMES = 2*(N_WAIT+N_ANIM);
let palette = ["#00a000", "#00c000", "#00e000"];
let pointCouples = [], nPoints;
myFont = loadFont("FT88-Bold.ttf");
let txt = ["abcdefg", "hijklmn", "opqrstu", "vwxyz"];
let points1 = textToPoints(txt);
let points2 = textToPoints(txt.map(s => s.toUpperCase()));
let animType = ~~random(3);
for (let i = 0; i < points1.length; i++) {
let nPoints1 = points1[i].length;
let nPoints2 = points2[i].length;
if (nPoints1 < nPoints2) {
for (let j = 0; j < nPoints2-nPoints1; j++) {
points1[i].push(random(points1[i]));
for (let j = 0; j < nPoints1-nPoints2; j++) {
points2[i].push(random(points2[i]));
for (let p1 of points1[i]) {
let idx, minDist = Infinity;
for (let j = 0; j < points2[i].length; j++) {
let d = distSquared(p1, p2);
let p2 = points2[i].splice(idx, 1)[0];
let animVal = (p1.y + p2.y)/10;
if (animType == 1) animVal = width*height-mag((p1.x+p2.x)/2-width/2, (p1.y+p2.y)/2-height/2)/3;
else if (animType == 2) animVal = N_ANIM*(TAU-(atan2((p1.y+p2.y)/2-height/2, (p1.x+p2.x)/2-width/2)+PI));
pointCouples.push({p1: p1, p2: p2, animVal: animVal+random(-1, 1)*s});
nPoints = pointCouples.length;
for (let i = 0; i < nPoints; i++) {
let t = timeFunc(pointCouples[i].animVal);
let p1 = pointCouples[i].p1, p2 = pointCouples[i].p2;
let x = lerp(p1.x, p2.x, t);
let y = lerp(p1.y, p2.y, t);
for (let i = 0; i < nPoints; i++) {
let t = timeFunc(pointCouples[i].animVal);
let p1 = pointCouples[i].p1, p2 = pointCouples[i].p2;
let x = lerp(p1.x, p2.x, t);
let y = lerp(p1.y, p2.y, t);
function textToPoints(txt) {
let txtSize = pixSize*10;
if (l.length > m) m = l.length;
let colors = [...Array(m)].map((e) => Array(n));
for (let j = 0; j < n; j++) {
for (let i = 0; i < chars.length; i++) {
if (i > 0) neighborColors.push(colors[i-1][j]);
if (j > 0) neighborColors.push(colors[i][j-1]);
} while (neighborColors.indexOf(col) != -1)
let grph = makeLetterBuffer(ch, txtSize, w, h);
for (let x = 0; x < w; x += s) {
for (let y = 0; y < h; y += s) {
let index = 4*(round(y+s/2)*w + round(x+s/2));
let val = grph.pixels[index];
letterPoints.push({x: x0+x, y: y0+y, col: col});
points.push(letterPoints);
function makeLetterBuffer(ch, size, w, h) {
let grph = createGraphics(w, h);
grph.textFont(myFont, size);
grph.textAlign(LEFT, TOP);
function distSquared(p1, p2) {
return mag(p1.x-p2.x, p1.y-p2.y);
function timeFunc(animVal) {
let x = (frameCount+animVal)%N_FRAMES;
if (x < N_WAIT) return 0;
if (x < N_WAIT+N_ANIM) return easeOutBack((x-N_WAIT)/N_ANIM);
if (x < 2*N_WAIT+N_ANIM) return 1;
return 1-easeOutBack((x-2*N_WAIT-N_ANIM)/N_ANIM);
function easeOutBack(x) {
return 1 + c3 * Math.pow(x - 1, 3) + c1 * Math.pow(x - 1, 2);
saveGif("out.gif", N_FRAMES, {delay: 0, units: "frames"});