Diagrams 2
const canvas = document.createElement('canvas')
canvas.width = 400
canvas.height = 400
document.body.append(canvas)
document.body.style.background = 'black'
Object.assign(document.body.style, {
position: 'absolute',
left: '50%',
top: '50%',
transform: 'translate(-50%, -50%)'
})
const ctx = canvas.getContext('2d')
const width = canvas.width;
const height = canvas.height;
// --- Global Variables ---
const num = 4;
const smp = new Array(num);
let isMousePressed = false;
let drag = false;
let mouseX = 0;
let mouseY = 0;
let theSize, t;
let col = 0;
const alpha = 255;
let curveRes = 0.01;
let pan;
const blank = new Int32Array(300 * 400);
// ===================================================================
// CLASS AND FUNCTION DEFINITIONS
// All classes and functions are defined here, before they are called.
// ===================================================================
// --- PModel Class (Shape logic) ---
class PModel {
constructor(p_col) {
this.pNum = 9;
this.theColor = p_col;
this.pLocX = new Float32Array(this.pNum);
this.pLocY = new Float32Array(this.pNum);
this.bLocX = new Float32Array(this.pNum);
this.bLocY = new Float32Array(this.pNum);
this.bx = new Float32Array(this.pNum);
this.by = new Float32Array(this.pNum);
this.down = new Array(this.pNum).fill(false);
this.outlineColor = convertPColor(0xff0000);
for (let i = 0; i < this.pNum; i++) {
this.pLocX[i] = this.bLocX[i] = this.bx[i] = Math.random() *
300; // Start in main area
this.pLocY[i] = this.bLocY[i] = this.by[i] = Math.random() * 400;
}
}
action(pixels) {
this.outlineColor = this.theColor;
for (let i = 0; i < this.pNum; i++) {
if (Math.floor(Math.random() * 300) === 1) {
if (this.bLocX[i] < 250) {
drawText(`E-${Math.floor(Math.random() * 11100)}`, this.bLocX[
i], this.bLocY[i]);
}
}
if (dist(this.bLocX[i], this.bLocY[i], mouseX, mouseY) < 10 && !
drag) {
this.outlineColor = convertPColor(0xff0000);
if (isMousePressed) {
this.down[i] = true;
drag = true;
}
} else {
if (!drag) {
this.down[i] = false;
}
}
if (this.down[i]) {
if (this.bLocX[i] < 250) {
drawText(`V-${Math.floor(Math.random() * 1100)}`, this.bLocX[i],
this.bLocY[i]);
}
this.outlineColor = convertPColor(0xff0000);
this.pLocX[i] = mouseX;
this.pLocY[i] = mouseY;
}
this.bLocX[i] -= this.bx[i];
this.bx[i] = ((this.bLocX[i] - this.pLocX[i]) / 17 + this.bx[i]) /
1.5;
this.bLocY[i] -= this.by[i];
this.by[i] = ((this.bLocY[i] - this.pLocY[i]) / 17 + this.by[i]) /
1.5;
}
this.ccc(0, 1, 40, 14);
this.ccc(0, 2, 40, 14);
this.ccc(0, 3, 40, 14);
this.ccc(0, 4, 40, 14);
this.ccc(0, 5, 40, 14);
this.ccc(0, 6, 40, 14);
this.ccc(0, 7, 40, 14);
this.ccc(0, 8, 40, 14);
this.ccc(1, 2, 40, 14);
this.ccc(2, 3, 40, 14);
this.ccc(3, 4, 40, 14);
this.ccc(4, 5, 40, 14);
this.ccc(5, 6, 40, 14);
this.ccc(6, 7, 40, 14);
this.ccc(8, 7, 40, 14);
this.ccc(8, 1, 40, 14);
this.ccc(1, 3, 80, 0);
this.ccc(3, 5, 80, 0);
this.ccc(5, 7, 80, 0);
this.ccc(7, 1, 80, 0);
for (let i = 0; i < this.pNum; i++) {
if (this.pLocX[i] < 0) this.pLocX[i] += 5;
if (this.pLocX[i] > 300) this.pLocX[i] -= 5;
if (this.pLocY[i] < 0) this.pLocY[i] += 5;
if (this.pLocY[i] > 400) this.pLocY[i] -= 5;
}
col = this.outlineColor;
apixel(pixels, this.bLocX[0], this.bLocY[0]);
curveRes = 0.01;
curvePoint(pixels, this.bLocX[1], this.bLocY[1], this.bLocX[2], this
.bLocY[2], this.bLocX[3], this.bLocY[3], this.bLocX[2], this
.bLocY[2]);
curvePoint(pixels, this.bLocX[3], this.bLocY[3], this.bLocX[4], this
.bLocY[4], this.bLocX[5], this.bLocY[5], this.bLocX[4], this
.bLocY[4]);
curvePoint(pixels, this.bLocX[5], this.bLocY[5], this.bLocX[6], this
.bLocY[6], this.bLocX[7], this.bLocY[7], this.bLocX[6], this
.bLocY[6]);
curvePoint(pixels, this.bLocX[7], this.bLocY[7], this.bLocX[8], this
.bLocY[8], this.bLocX[1], this.bLocY[1], this.bLocX[8], this
.bLocY[8]);
curveRes = 0.02;
col = this.theColor;
curvePoint(pixels, this.bLocX[1], this.bLocY[1], this.bLocX[0], this
.bLocY[0], this.bLocX[3], this.bLocY[3], this.bLocX[0], this
.bLocY[0]);
curvePoint(pixels, this.bLocX[3], this.bLocY[3], this.bLocX[0], this
.bLocY[0], this.bLocX[5], this.bLocY[5], this.bLocX[0], this
.bLocY[0]);
curvePoint(pixels, this.bLocX[5], this.bLocY[5], this.bLocX[0], this
.bLocY[0], this.bLocX[7], this.bLocY[7], this.bLocX[0], this
.bLocY[0]);
curvePoint(pixels, this.bLocX[7], this.bLocY[7], this.bLocX[0], this
.bLocY[0], this.bLocX[1], this.bLocY[1], this.bLocX[0], this
.bLocY[0]);
}
ccc(indexA, indexB, clength, off) {
if (dist(this.pLocX[indexA], this.pLocY[indexA], this.pLocX[indexB],
this.pLocY[indexB]) > clength) {
this.pLocX[indexA] += (this.pLocX[indexB] - this.pLocX[indexA]) / 6;
this.pLocY[indexA] += (this.pLocY[indexB] - this.pLocY[indexA]) / 6;
this.pLocX[indexB] += (this.pLocX[indexA] - this.pLocX[indexB]) / 6;
this.pLocY[indexB] += (this.pLocY[indexA] - this.pLocY[indexB]) / 6;
}
for (let i = 0; i < this.pNum; i++) {
if (i !== indexA) {
if (dist(this.pLocX[indexA], this.pLocY[indexA], this.pLocX[i],
this.pLocY[i]) < clength - off) {
this.pLocX[indexA] -= (this.pLocX[i] - this.pLocX[indexA]) / 6;
this.pLocY[indexA] -= (this.pLocY[i] - this.pLocY[indexA]) / 6;
}
}
}
}
}
// --- Panel Class (UI on the right) ---
class Panel {
constructor() {
this.h1 = 80;
this.h2 = 80;
this.h3 = 80;
this.h4 = 80;
this.dh1 = 80;
this.dh2 = 80;
this.dh3 = 80;
this.dh4 = 80;
this.numsA = 0;
}
// Static drawing for panel background (called once in setupPanel)
drawStatic() {
ctx.fillStyle = "rgb(115,115,115)";
ctx.fillRect(300, 0, 100, 400);
ctx.beginPath();
ctx.moveTo(300, 300);
ctx.lineTo(400, 400);
ctx.strokeStyle = "rgb(155,145,105)";
ctx.stroke();
/* ctx.strokeStyle = "red";
for (let i = 0; i < 40; i++) {
ctx.beginPath(); ctx.moveTo(101, i * 10); ctx.lineTo(400, 20 + i * 20); ctx.stroke();
}
ctx.stroke()*/
ctx.strokeStyle = "white";
ctx.strokeStyle = "rgb(205,205,205)";
ctx.strokeRect(309.5, 11.5, 82, 111);
ctx.strokeRect(359.5, 130.5, 32, 21);
ctx.strokeRect(359.5, 160.5, 32, 21);
ctx.strokeRect(359.5, 190.5, 32, 21);
}
action() {
ctx.strokeStyle = "rgb(135,125,85)";
ctx.fillStyle = "rgb(165,165,165)";
ctx.fillRect(310, 10, 80, 110);
ctx.beginPath();
ctx.moveTo(301.5, 0);
ctx.lineTo(301.5, 400);
ctx.stroke();
if (Math.floor(Math.random() * 60) === 1) this.dh1 = Math.random() *
75 + 5;
if (Math.floor(Math.random() * 60) === 1) this.dh2 = Math.random() *
75 + 5;
if (Math.floor(Math.random() * 60) === 1) this.dh3 = Math.random() *
75 + 5;
if (Math.floor(Math.random() * 60) === 1) this.dh4 = Math.random() *
75 + 5;
this.h1 += (this.dh1 - this.h1) * 0.09;
this.h2 += (this.dh2 - this.h2) * 0.09;
this.h3 += (this.dh3 - this.h3) * 0.09;
this.h4 += (this.dh4 - this.h4) * 0.09;
ctx.fillRect(310, 130, 10, this.h1);
ctx.fillRect(320, 130, 10, this.h2);
ctx.fillRect(330, 130, 10, this.h3);
ctx.fillRect(340, 130, 10, this.h4);
ctx.fillRect(360, 130, 30, 20);
ctx.fillRect(360, 160, 30, 20);
ctx.fillRect(360, 190, 30, 20);
ctx.strokeRect(310, 130, 10, this.h1);
ctx.fillRect(320, 130, 10, this.h2);
ctx.strokeRect(330, 130, 10, this.h3);
ctx.fillRect(340, 130, 10, this.h4);
ctx.strokeRect(360, 130, 30, 20);
ctx.fillRect(360, 160, 30, 20);
ctx.fillRect(360, 190, 30, 20);
if (Math.floor(Math.random() * 10) === 1) this.numsA = Math.floor(Math
.random() * 10000000);
drawText(`E-${this.numsA}`, 315, 116, "white");
}
}
// --- Main Setup and Loop ---
function setup() {
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, width, height);
theSize = width * height;
t = 0;
for (let i = 0; i < num; i++) {
if (i < num / 2) {
smp[i] = new PModel(convertPColor(0x330000));
} else {
smp[i] = new PModel(convertPColor(0xffffff));
}
}
setupPanel();
addEventListeners();
}
function loop() {
const imageData = ctx.getImageData(0, 0, width, height);
const pixels = new Uint32Array(imageData.data.buffer);
if (isMousePressed) {
for (let i = 0; i < 120000 - 300; i++) {
pixels[blank[i]] = blendC(pixels[blank[i]] << 8, ~pixels[blank[i +
300]], 40);
}
} else {
for (let i = 0; i < 120000 - 300; i++) {
pixels[blank[i]] = blendC(~pixels[blank[i]], pixels[blank[i + 300]] |
0x111111, 150);
}
}
if (t < 12) {
if (t !== 11) {
for (let i = 400; i < theSize; i++) {
pixels[i] = blendC(~pixels[i] & 0xDDDDDD, pixels[i - 400], 20);
}
} else {
for (let i = 400; i < theSize; i++) {
pixels[i] = blendC(0x000000, pixels[i], 50);
}
}
for (let i = 0; i < 120000; i++) {
pixels[blank[i]] = 0xFFFFFFFF;
}
}
t++;
for (let i = 0; i < num; i++) {
smp[i].action(pixels);
}
for (let a = 0; a < num; a++) {
for (let b = 0; b < num; b++) {
if (a === b) continue;
repel(a, b, 0, 0, 120);
if (Math.floor(Math.random() * 400) === 1) {
col = convertPColor(0xFFFFFFFF);
drawLine(pixels, smp[a].bLocX[0], smp[a].bLocY[0], smp[b].bLocX[0],
smp[b].bLocY[0]);
}
}
}
ctx.putImageData(imageData, 0, 0);
pan.action();
ctx.strokeStyle = "white";
ctx.strokeRect(0.5, 0.5, 399, 399);
ctx.fillStyle = 'rgba(110, 50, 0, 0.04)'
ctx.fillRect(300, 0, 100, 400);
requestAnimationFrame(loop);
}
// --- Utility & Drawing Functions ---
function repel(ind, ind2, indexA, indexB, clength) {
if (dist(smp[ind].pLocX[indexA], smp[ind].pLocY[indexA], smp[ind2].pLocX[
indexB], smp[ind2].pLocY[indexB]) < clength) {
smp[ind].pLocX[indexA] -= (smp[ind2].pLocX[indexB] - smp[ind].pLocX[
indexA]) / 2;
smp[ind].pLocY[indexA] -= (smp[ind2].pLocY[indexB] - smp[ind].pLocY[
indexA]) / 2;
}
}
function curvePoint(pixels, x1, y1, x2, y2, x3, y3, x4, y4) {
for (let a = 0; a < 1; a += curveRes) {
const b = 1 - a;
const pre1 = a * a * a;
const pre2 = 3 * (a * a) * b;
const pre3 = 3 * a * (b * b);
const pre4 = b * b * b;
const x = pre1 * x1 + pre2 * x2 + pre3 * x4 + pre4 * x3;
const y = pre1 * y1 + pre2 * y2 + pre3 * y4 + pre4 * y3;
apixel(pixels, x, y);
}
}
function blendC(c1, c2, amount) {
const r1 = c1 & 0xff;
const g1 = (c1 >> 8) & 0xff;
const b1 = (c1 >> 16) & 0xff;
const r2 = c2 & 0xff;
const g2 = (c2 >> 8) & 0xff;
const b2 = (c2 >> 16) & 0xff;
const r = (((amount * (r1 - r2)) >> 8) + r2);
const g = (((amount * (g1 - g2)) >> 8) + g2);
const b = (((amount * (b1 - b2)) >> 8) + b2);
return (0xff000000 | (b << 16) | (g << 8) | r) >>> 0;
}
function apixel(pixels, ax, ay) {
const x = Math.floor(ax);
const y = Math.floor(ay);
if (x <= 0 || x >= width - 2 || y <= 0 || y >= height - 2) return;
const fx = ax - x;
const fy = ay - y;
const nfx = 1 - fx;
const nfy = 1 - fy;
const loc = x + y * width;
const loc2 = loc + width;
const loc3 = loc + 1;
const loc4 = loc2 + 1;
pixels[loc] = blendC(col, pixels[loc], Math.floor(nfx * nfy * alpha));
pixels[loc3] = blendC(col, pixels[loc3], Math.floor(fx * nfy * alpha));
pixels[loc2] = blendC(col, pixels[loc2], Math.floor(nfx * fy * alpha));
pixels[loc4] = blendC(col, pixels[loc4], Math.floor(fx * fy * alpha));
}
function drawLine(pixels, x1, y1, x2, y2) {
const dx = x2 - x1;
const dy = y2 - y1;
let n = Math.abs(dx) > Math.abs(dy) ? Math.abs(dx) : Math.abs(dy);
if (n === 0) return;
const dt = 1.0 / n;
const dxdt = dx * dt;
const dydt = dy * dt;
let x = x1;
let y = y1;
while (n-- >= 0) {
if (x > 1 && x < width - 1 && y > 1 && y < height - 1) {
apixel(pixels, x, y);
}
x += dxdt;
y += dydt;
}
}
function setupPanel() {
pan = new Panel();
pan.drawStatic(); // Draw the static background of the panel once.
ctx.font = "12px sans-serif";
let index = 0;
for (let j = 0; j < 160000; j += 400) {
for (let i = 0; i < 300; i++) {
blank[index++] = i + j;
}
}
}
function dist(x1, y1, x2, y2) {
const dx = x1 - x2;
const dy = y1 - y2;
return Math.sqrt(dx * dx + dy * dy);
}
function convertPColor(pColor) {
const alpha = (pColor & 0xFF000000) >>> 0;
const red = (pColor & 0x00FF0000) >> 16;
const green = (pColor & 0x0000FF00);
const blue = (pColor & 0x000000FF) << 16;
return (alpha | blue | green | red) >>> 0;
}
function drawText(txt, x, y, color = "white") {
ctx.fillStyle = color;
ctx.fillText(txt, x, y);
}
function addEventListeners() {
canvas.addEventListener('mousedown', () => {
isMousePressed = true;
});
canvas.addEventListener('mouseup', () => {
isMousePressed = false;
drag = false;
});
canvas.addEventListener('mouseout', () => {
isMousePressed = false;
drag = false;
});
canvas.addEventListener('mousemove', (e) => {
const rect = canvas.getBoundingClientRect();
mouseX = e.clientX - rect.left;
mouseY = e.clientY - rect.top;
});
}
// ===================================================================
// START THE SKETCH
// This is the final step, ensuring everything above is defined.
// ===================================================================
setup();
requestAnimationFrame(loop);
This is a port of an old Processing experiment from the early 2000s that I ported to JS with Gemini 2.5 pro…