260531

const animate = anime.animate;

const CANVAS_WIDTH = 800;
const CANVAS_HEIGHT = 800;

const SPHERE_COUNT = 5;
const STAGGER_DELAY = 220;

const JUMP_DURATION = 800;
const PAUSE_DURATION = 1000;
const ONE_JUMP_CYCLE = JUMP_DURATION + PAUSE_DURATION;
const WAVE_DURATION = ONE_JUMP_CYCLE + STAGGER_DELAY * (SPHERE_COUNT - 1);
const CAMERA_TRANSITION = 400;
const ONE_CAMERA_CYCLE = WAVE_DURATION + CAMERA_TRANSITION;

const SPHERE_RADIUS = 50;
const BOX_SIZE = SPHERE_RADIUS * 2;
const SPHERE_GAP = BOX_SIZE * 1.5;
const JUMP_HEIGHT = BOX_SIZE * 1.8;

let palette = [];
let sphereStates = [];
let camera = { progress: 0 };
let cameraAngles = [];
let isWavePlaying = false;

function setup() {
  const cameraTilt = TAU / 16;
  const cameraTurn = TAU / 16;
  cameraAngles = [
    { rotX: -cameraTilt, rotY: cameraTurn },
    { rotX: 0, rotY: 0 },
    { rotX: -cameraTilt, rotY: -cameraTurn },
  ];

  sphereStates = [];
  for (let i = 0; i < SPHERE_COUNT; i++) {
    sphereStates.push({ count: 0, arc: 0 });
  }

  createCanvas(CANVAS_WIDTH, CANVAS_HEIGHT, WEBGL);
  palette = colorArray[2].colors;
  background(palette[0]);
  camera.progress = 0;

  animate(camera, {
    progress: 3,
    ease: "linear",
    duration: ONE_CAMERA_CYCLE * 3,
    loop: true,
    onUpdate: () => {
      const waveNow = isWavePhase(camera.progress);
      if (waveNow && !isWavePlaying) startWave();
      isWavePlaying = waveNow;
    },
  });
  startWave();
}

function draw() {
  background(palette[0]);
  ortho();
  noStroke();
  strokeWeight(2);

  push();
  applyCamera(camera.progress);

  for (let i = 0; i < SPHERE_COUNT; i++) {
    const state = sphereStates[i];
    const centerX = (i - (SPHERE_COUNT - 1) / 2) * SPHERE_GAP;
    const boxHeight = state.count * BOX_SIZE;
    const sphereY = getSphereYOffset(state.arc);
    drawSphereAndBox(centerX, sphereY, boxHeight);
  }
  pop();
}

function isWavePhase(progress) {
  const fraction = progress - floor(progress);
  return fraction < WAVE_DURATION / ONE_CAMERA_CYCLE;
}

function getCameraRotation(progress) {
  const stepIndex = floor(progress) % 3;
  const fraction = progress - floor(progress);
  const fromAngle = cameraAngles[stepIndex];
  const toAngle = cameraAngles[(stepIndex + 1) % 3];
  const wavePart = WAVE_DURATION / ONE_CAMERA_CYCLE;

  if (fraction < wavePart) {
    return fromAngle;
  }

  const blend = (fraction - wavePart) / (1 - wavePart);
  return {
    rotX: lerp(fromAngle.rotX, toAngle.rotX, blend),
    rotY: lerp(fromAngle.rotY, toAngle.rotY, blend),
  };
}

function startWave() {
  for (let i = 0; i < SPHERE_COUNT; i++) {
    const state = sphereStates[i];
    state.count = 0;
    state.arc = 0;
    animate(state, {
      count: [
        { to: 1, ease: "outExpo", duration: JUMP_DURATION },
        { to: 0, ease: "inExpo", duration: PAUSE_DURATION },
      ],
      arc: [
        { to: 1, ease: "out(3)", duration: JUMP_DURATION * 0.5 },
        { to: 0, ease: "in(3)", duration: JUMP_DURATION * 0.5 },
      ],
      duration: ONE_JUMP_CYCLE,
      loop: false,
      delay: i * STAGGER_DELAY,
    });
  }
}

function applyCamera(progress) {
  const rotation = getCameraRotation(progress);
  rotateX(rotation.rotX);
  rotateY(rotation.rotY);
}

function getSphereYOffset(jumpAmount) {
  const restY = BOX_SIZE / 2;
  return restY - jumpAmount * JUMP_HEIGHT;
}

function drawSphereAndBox(centerX, sphereY, boxHeight) {
  push();
  translate(centerX, 0, 0);

  push();
  fill(palette[1]);
  translate(0, sphereY, 0);
  sphere(SPHERE_RADIUS);
  pop();

  push();
  noFill();
  stroke(palette[3]);
  translate(0, BOX_SIZE - boxHeight / 2, 0);
  box(BOX_SIZE, boxHeight, BOX_SIZE);
  pop();

  pop();
}

const colorArray = [
  {
    id: 0,
    colors: ["#253276", "#dfdad3", "#ffffff", "#000000"],
  },
  {
    id: 1,
    colors: [
      "#9dbdba",
      "#f8b042",
      "#e47763",
      "#253276",
      "#dfdad3",
      "#FFFFFF",
      "#000000",
    ],
  },
  {
    id: 2,
    colors: ["#758492", "#CCE7FF", "#ece6d7", "#ffffff", "#111111"],
  },
];
const animate = anime.animate;

const CANVAS_WIDTH = 800;
const CANVAS_HEIGHT = 800;

const SPHERE_COUNT = 5;
const STAGGER_DELAY = 220;

const JUMP_DURATION = 800;
const PAUSE_DURATION = 1000;
const ONE_JUMP_CYCLE = JUMP_DURATION + PAUSE_DURATION;
const WAVE_DURATION = ONE_JUMP_CYCLE + STAGGER_DELAY * (SPHERE_COUNT - 1);
const CAMERA_TRANSITION = 400;
const ONE_CAMERA_CYCLE = WAVE_DURATION + CAMERA_TRANSITION;

const SPHERE_RADIUS = 50;
const BOX_SIZE = SPHERE_RADIUS * 2;
const SPHERE_GAP = BOX_SIZE * 1.5;
const JUMP_HEIGHT = BOX_SIZE * 1.8;

let palette = [];
let sphereStates = [];
let camera = { progress: 0 };
let cameraAngles = [];
let isWavePlaying = false;

function setup() {
  const cameraTilt = TAU / 16;
  const cameraTurn = TAU / 16;
  cameraAngles = [
    { rotX: -cameraTilt, rotY: cameraTurn },
    { rotX: 0, rotY: 0 },
    { rotX: -cameraTilt, rotY: -cameraTurn },
  ];

  sphereStates = [];
  for (let i = 0; i < SPHERE_COUNT; i++) {
    sphereStates.push({ count: 0, arc: 0 });
  }

  createCanvas(CANVAS_WIDTH, CANVAS_HEIGHT, WEBGL);
  palette = colorArray[2].colors;
  background(palette[0]);
  camera.progress = 0;

  animate(camera, {
    progress: 3,
    ease: "linear",
    duration: ONE_CAMERA_CYCLE * 3,
    loop: true,
    onUpdate: () => {
      const waveNow = isWavePhase(camera.progress);
      if (waveNow && !isWavePlaying) startWave();
      isWavePlaying = waveNow;
    },
  });
  startWave();
}

function draw() {
  background(palette[0]);
  ortho();
  noStroke();
  strokeWeight(2);

  push();
  applyCamera(camera.progress);

  for (let i = 0; i < SPHERE_COUNT; i++) {
    const state = sphereStates[i];
    const centerX = (i - (SPHERE_COUNT - 1) / 2) * SPHERE_GAP;
    const boxHeight = state.count * BOX_SIZE;
    const sphereY = getSphereYOffset(state.arc);
    drawSphereAndBox(centerX, sphereY, boxHeight);
  }
  pop();
}

function isWavePhase(progress) {
  const fraction = progress - floor(progress);
  return fraction < WAVE_DURATION / ONE_CAMERA_CYCLE;
}

function getCameraRotation(progress) {
  const stepIndex = floor(progress) % 3;
  const fraction = progress - floor(progress);
  const fromAngle = cameraAngles[stepIndex];
  const toAngle = cameraAngles[(stepIndex + 1) % 3];
  const wavePart = WAVE_DURATION / ONE_CAMERA_CYCLE;

  if (fraction < wavePart) {
    return fromAngle;
  }

  const blend = (fraction - wavePart) / (1 - wavePart);
  return {
    rotX: lerp(fromAngle.rotX, toAngle.rotX, blend),
    rotY: lerp(fromAngle.rotY, toAngle.rotY, blend),
  };
}

function startWave() {
  for (let i = 0; i < SPHERE_COUNT; i++) {
    const state = sphereStates[i];
    state.count = 0;
    state.arc = 0;
    animate(state, {
      count: [
        { to: 1, ease: "outExpo", duration: JUMP_DURATION },
        { to: 0, ease: "inExpo", duration: PAUSE_DURATION },
      ],
      arc: [
        { to: 1, ease: "out(3)", duration: JUMP_DURATION * 0.5 },
        { to: 0, ease: "in(3)", duration: JUMP_DURATION * 0.5 },
      ],
      duration: ONE_JUMP_CYCLE,
      loop: false,
      delay: i * STAGGER_DELAY,
    });
  }
}

function applyCamera(progress) {
  const rotation = getCameraRotation(progress);
  rotateX(rotation.rotX);
  rotateY(rotation.rotY);
}

function getSphereYOffset(jumpAmount) {
  const restY = BOX_SIZE / 2;
  return restY - jumpAmount * JUMP_HEIGHT;
}

function drawSphereAndBox(centerX, sphereY, boxHeight) {
  push();
  translate(centerX, 0, 0);

  push();
  fill(palette[1]);
  translate(0, sphereY, 0);
  sphere(SPHERE_RADIUS);
  pop();

  push();
  noFill();
  stroke(palette[3]);
  translate(0, BOX_SIZE - boxHeight / 2, 0);
  box(BOX_SIZE, boxHeight, BOX_SIZE);
  pop();

  pop();
}

const colorArray = [
  {
    id: 0,
    colors: ["#253276", "#dfdad3", "#ffffff", "#000000"],
  },
  {
    id: 1,
    colors: [
      "#9dbdba",
      "#f8b042",
      "#e47763",
      "#253276",
      "#dfdad3",
      "#FFFFFF",
      "#000000",
    ],
  },
  {
    id: 2,
    colors: ["#758492", "#CCE7FF", "#ece6d7", "#ffffff", "#111111"],
  },
];

Last Updated:

260531