<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Universal Morphing Background - Positionable Element</title>
<style>
body {
margin: 0;
background-color: #000000;
font-family: sans-serif;
color: #c0c0c0;
/* Centering properties have been removed */
}
#animationWindow {
position: relative;
width: 1000px;
height: 1000px;
background-color: rgba(0, 0, 0, 0.8);
border: 1px solid #c0c0c0;
}
canvas {
display: block;
position: absolute;
top: 0;
left: 0;
}
</style>
</head>
<body>
<div id="animationWindow">
<canvas id="bgCanvas"></canvas>
</div>
<script>
const animationWindow = document.getElementById('animationWindow');
const canvas = document.getElementById('bgCanvas');
const ctx = canvas.getContext('2d');
let particles = [];
let shapes = [];
let currentShapeIndex, targetShapeIndex;
let currentShapeData, targetShapeData;
let progress = 0;
let morphSpeed = 0.004;
let angleX = 0;
let angleY = 0;
let particleCount = 25000;
class Particle {
constructor(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
this.projectedX = 0;
this.projectedY = 0;
this.size = 1;
}
project(x, y, z) {
const fov = 1000;
const dist = fov + z;
this.projectedX = (x * fov) / dist + canvas.width / 2;
this.projectedY = (y * fov) / dist + canvas.height / 2;
this.size = Math.max(0, (1 - z / 1000) * 1.5);
}
draw() {
if (this.projectedX < 0 || this.projectedX > canvas.width || this.projectedY < 0 || this.projectedY > canvas.height) {
return;
}
ctx.beginPath();
ctx.arc(this.projectedX, this.projectedY, this.size, 0, Math.PI * 2);
ctx.fillStyle = `rgba(192, 192, 192, ${this.size / 1.5})`;
ctx.fill();
}
}
// --- Shape Generation ---
function generateShapes() {
shapes.push(createTrefoilKnot);
shapes.push(createFigureEightKnot);
shapes.push(createCinquefoilKnot);
shapes.push(createGrannyKnot);
shapes.push(createSierpinskiTetrahedron);
shapes.push(createSierpinskiPyramid);
shapes.push(createMengerSponge);
shapes.push(createCantorDust3D);
shapes.push(createBlackHole);
shapes.push(createCube);
}
function createFigureEightKnot() {
const tempParticles = [];
const scaleFactor = Math.min(canvas.width, canvas.height) / 8;
for (let i = 0; i < particleCount; i++) {
const t = (i / particleCount) * Math.PI * 4;
const r = 2 + Math.sin(1.5 * t);
const x = Math.cos(t) * r * scaleFactor;
const y = Math.sin(t) * r * scaleFactor;
const z = Math.cos(1.5 * t) * scaleFactor * 2;
tempParticles.push({x, y, z});
}
return tempParticles;
}
function createTrefoilKnot() {
const tempParticles = [];
const scaleFactor = Math.min(canvas.width, canvas.height) / 8;
for (let i = 0; i < particleCount; i++) {
const t = (i / particleCount) * Math.PI * 2;
const x = (Math.sin(t) + 2 * Math.sin(2 * t)) * scaleFactor;
const y = (Math.cos(t) - 2 * Math.cos(2 * t)) * scaleFactor;
const z = -Math.sin(3 * t) * scaleFactor;
tempParticles.push({x, y, z});
}
return tempParticles;
}
function createCinquefoilKnot() {
const tempParticles = [];
const scaleFactor = Math.min(canvas.width, canvas.height) / 10;
for (let i = 0; i < particleCount; i++) {
const t = (i / particleCount) * Math.PI * 2;
const x = (2 + Math.cos(5 * t)) * Math.cos(2 * t) * scaleFactor;
const y = (2 + Math.cos(5 * t)) * Math.sin(2 * t) * scaleFactor;
const z = -Math.sin(5 * t) * scaleFactor;
tempParticles.push({x, y, z});
}
return tempParticles;
}
function createGrannyKnot() {
const tempParticles = [];
const scaleFactor = Math.min(canvas.width, canvas.height) / 5;
for (let i = 0; i < particleCount; i++) {
const t = (i / particleCount) * Math.PI * 2;
const x = (-Math.cos(t) - 0.5 * Math.cos(5*t) - 0.5 * Math.cos(7*t)) * scaleFactor;
const y = (Math.sin(t) - 0.5 * Math.sin(5*t) + 0.5 * Math.sin(7*t)) * scaleFactor;
const z = (Math.cos(2*t) + 0.5 * Math.cos(6*t)) * scaleFactor;
tempParticles.push({x, y, z});
}
return tempParticles;
}
function createSierpinskiTetrahedron() {
const tempParticles = [];
const scale = Math.min(canvas.width, canvas.height) / 2;
const v = [
{x: scale, y: scale, z: scale},
{x: -scale, y: -scale, z: scale},
{x: -scale, y: scale, z: -scale},
{x: scale, y: -scale, z: -scale}
];
let p = {x: 0, y: 0, z: 0};
for (let i = 0; i < particleCount; i++) {
const targetVertex = v[Math.floor(Math.random() * 4)];
p.x = (p.x + targetVertex.x) / 2;
p.y = (p.y + targetVertex.y) / 2;
p.z = (p.z + targetVertex.z) / 2;
tempParticles.push({x: p.x, y: p.y, z: p.z});
}
return tempParticles;
}
function createSierpinskiPyramid() {
const tempParticles = [];
const scale = Math.min(canvas.width, canvas.height) / 2;
const v = [
{x: 0, y: scale, z: 0},
{x: scale, y: -scale, z: scale},
{x: -scale, y: -scale, z: scale},
{x: 0, y: -scale, z: -scale}
];
let p = {x: 0, y: 0, z: 0};
for (let i = 0; i < particleCount; i++) {
const targetVertex = v[Math.floor(Math.random() * 4)];
p.x = (p.x + targetVertex.x) / 2;
p.y = (p.y + targetVertex.y) / 2;
p.z = (p.z + targetVertex.z) / 2;
tempParticles.push({x: p.x, y: p.y, z: p.z});
}
return tempParticles;
}
function createMengerSponge() {
const tempParticles = [];
const scaleFactor = Math.min(canvas.width, canvas.height) / 2;
let p = { x: Math.random() - 0.5, y: Math.random() - 0.5, z: Math.random() - 0.5 };
const translations = [];
for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1; j++) {
for (let k = -1; k <= 1; k++) {
if (Math.abs(i) + Math.abs(j) + Math.abs(k) > 1) {
translations.push({ x: i, y: j, z: k });
}
}
}
}
for (let i = 0; i < particleCount; i++) {
const t = translations[Math.floor(Math.random() * translations.length)];
p.x = (p.x + t.x) / 3;
p.y = (p.y + t.y) / 3;
p.z = (p.z + t.z) / 3;
tempParticles.push({ x: p.x * scaleFactor, y: p.y * scaleFactor, z: p.z * scaleFactor });
}
return tempParticles;
}
function createCantorDust3D() {
const tempParticles = [];
const scaleFactor = Math.min(canvas.width, canvas.height) / 2.5;
let p = { x: 0, y: 0, z: 0 };
const translations = [];
for (let i = -1; i <= 1; i += 2) {
for (let j = -1; j <= 1; j += 2) {
for (let k = -1; k <= 1; k += 2) {
translations.push({ x: i, y: j, z: k });
}
}
}
for (let i = 0; i < particleCount; i++) {
const t = translations[Math.floor(Math.random() * translations.length)];
p.x = (p.x + t.x) / 3;
p.y = (p.y + t.y) / 3;
p.z = (p.z + t.z) / 3;
tempParticles.push({ x: p.x * scaleFactor, y: p.y * scaleFactor, z: p.z * scaleFactor });
}
return tempParticles;
}
function createBlackHole() {
const tempParticles = [];
const scaleFactor = Math.min(canvas.width, canvas.height) / 4;
const diskRadius = 1.2 * scaleFactor;
for (let i = 0; i < particleCount; i++) {
const r = Math.cbrt(Math.random()) * diskRadius;
const theta = Math.random() * Math.PI * 2;
const x = r * Math.cos(theta);
const y = (Math.random() - 0.5) * r * 0.05;
const z = r * Math.sin(theta);
tempParticles.push({x, y, z});
}
return tempParticles;
}
function createCube() {
const tempParticles = [];
const size = Math.min(canvas.width, canvas.height) / 3;
for (let i = 0; i < particleCount; i++) {
const x = Math.random() * size - size / 2;
const y = Math.random() * size - size / 2;
const z = Math.random() * size - size / 2;
tempParticles.push({x, y, z});
}
return tempParticles;
}
// --- Core Logic ---
function morph() {
progress += morphSpeed;
if (progress >= 1) {
progress = 0;
currentShapeIndex = targetShapeIndex;
do {
targetShapeIndex = Math.floor(Math.random() * shapes.length);
} while (targetShapeIndex === currentShapeIndex);
currentShapeData = shapes[currentShapeIndex]();
targetShapeData = shapes[targetShapeIndex]();
}
for (let i = 0; i < particleCount; i++) {
const p = particles[i];
const start = currentShapeData[i];
const end = targetShapeData[i];
p.x = start.x + (end.x - start.x) * progress;
p.y = start.y + (end.y - start.y) * progress;
p.z = start.z + (end.z - start.z) * progress;
}
}
function animate() {
requestAnimationFrame(animate);
ctx.clearRect(0, 0, canvas.width, canvas.height);
morph();
angleX += 0.001;
angleY += 0.002;
const cosX = Math.cos(angleX);
const sinX = Math.sin(angleX);
const cosY = Math.cos(angleY);
const sinY = Math.sin(angleY);
particles.forEach(p => {
const x1 = p.x * cosY - p.z * sinY;
const z1 = p.x * sinY + p.z * cosY;
const y2 = p.y * cosX - z1 * sinX;
const z2 = p.y * sinX + z1 * cosX;
p.project(x1, y2, z2);
p.draw();
});
}
function initialize() {
canvas.width = animationWindow.clientWidth;
canvas.height = animationWindow.clientHeight;
particles = [];
shapes = [];
generateShapes();
currentShapeIndex = Math.floor(Math.random() * shapes.length);
do {
targetShapeIndex = Math.floor(Math.random() * shapes.length);
} while (targetShapeIndex === currentShapeIndex);
currentShapeData = shapes[currentShapeIndex]();
targetShapeData = shapes[targetShapeIndex]();
for (let i = 0; i < particleCount; i++) {
particles.push(new Particle(currentShapeData[i].x, currentShapeData[i].y, currentShapeData[i].z));
}
}
window.onload = () => {
initialize();
animate();
};
</script>
</body>
</html>