import { mountFlex } from "https://cdn.jsdelivr.net/npm/p5.flex@0.1.1/src/p5.flex.min.mjs"
const [WIDTH, HEIGHT] = [400, 400]
const bg = [p.random(255), p.random(255), p.random(255)]
const ambLight = [bg[0] + 50, bg[1] + 50, bg[2] + 50]
const dirLight = [255, 255, 255]
const a54 = p.PI / 3.333333333
let seed = p.random(2024)
let cardioidParam = [-1, -1, true]
const pos = Array.from({ length: oddr + evenr })
p.createCanvas(WIDTH, HEIGHT, p.WEBGL)
p.flex({ container: { padding: "20px" } })
p.ortho(-WIDTH * 2, WIDTH * 2, -HEIGHT * 2, HEIGHT * 2, 0.001, 10000)
for (let j = -(oddr - 1) / 2; j <= (oddr - 1) / 2; j++) {
for (let i = -(oddc - 1) / 2; i <= (oddc - 1) / 2; i++) {
pos[id] = [size * (i + j), size * (-i + j), size * 2 * -j]
for (let j = -evenr / 2; j < evenr / 2; j++) {
for (let i = -evenc / 2 + 1; i <= evenc / 2; i++) {
pos[id] = [size * (i + j), size * (-i + j + 1), -j * (size * 2) - size]
const t = (p.frameCount - 1) / loopFrames
const lp = cardioid(t, ...cardioidParam)
p.ambientLight(...ambLight)
p.directionalLight(...dirLight, lp.x, lp.y, -1)
const rx = p.map(p.cos(p.TWO_PI * t), -1, 1, 0.001, a54)
const rz = p.map(p.cos(p.TWO_PI * t), -1, 1, 0.001, a45)
pos.forEach((po, index) => {
p.rotateX(rx * p.random([-1, 1]))
p.rotateZ(rz * p.random([-1, 1]))
p.cos(p.TWO_PI * t) * 0.5,
p.sin(p.TWO_PI * t) * 0.5
p.box(size * p.min(p.map(n, 0, 1, 0.1, 1.5), 1) * 0.95)
if (p.frameCount % loopFrames === 0) {
cardioidParam = getCardioidParam()
const getCardioidParam = () => {
return [p.random([-1, 1]), p.random([-1, 1]), p.random([false, true])]
const translate = (x, y, z) => {
const cardioid = (t, v, h, q) => ({
x: h * 2 * (1 - p.cos(p.TWO_PI * t)) * (q ? p.sin(p.TWO_PI * t) : p.cos(p.TWO_PI * t)),
y: v * 2 * (1 - p.cos(p.TWO_PI * t)) * (q ? p.cos(p.TWO_PI * t) : p.sin(p.TWO_PI * t)),