0x8feee00f…f6b0sent to0xb8952959…ccf4·#23,948,153·0xac045f12…3a6adf
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Binary Partitions - Impulse & Interactive</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; cursor: crosshair; }
html, body {
width: 100%;
height: 100%;
background: #000;
overflow: hidden;
}
canvas {
display: block;
width: 100%;
height: 100%;
}
/* Simple instruction overlay */
#hint {
position: absolute;
bottom: 20px;
left: 20px;
color: #00ff00;
font-family: monospace;
font-size: 12px;
opacity: 0.5;
pointer-events: none;
z-index: 10;
}
</style>
</head>
<body>
<div id="hint">SYSTEM: SHAKE MOUSE TO DESTROY // RANDOM GLITCH: ACTIVE</div>
<canvas id="artCanvas"></canvas>
<script>
// --- Partition Data (Original) ---
const originalPartitions = [{"x":0,"y":0,"width":731.7712962962963,"height":177.64635903206445,"gradientConfig":{"type":"text","colors":["#ff00ea"],"angle":135.5864197530864,"rotationDirection":1,"textContent":"GURU"},"secondLayer":{"enabled":true,"type":"linear","colors":["#47c8ff"],"angle":180,"rotationDirection":-1,"blendMode":"exclusion"}},{"x":731.7712962962963,"y":0,"width":348.2287037037037,"height":310.9861111111111,"gradientConfig":{"type":"text","colors":["#000000","#4dff00"],"angle":26.933641975308642,"rotationDirection":1,"textContent":"GURU"},"secondLayer":{"enabled":true,"type":"linear","colors":["#70ade6"],"angle":270,"rotationDirection":1,"blendMode":"exclusion"}},{"x":0,"y":393.83503017427313,"width":129.8141199798049,"height":401.7399698257268,"gradientConfig":{"type":"text","colors":["#000000","#4dff00"],"angle":0.7808641975308641,"rotationDirection":1,"textContent":"GURU"},"secondLayer":{"enabled":true,"type":"linear","colors":["#a4afda"],"angle":0,"rotationDirection":1,"blendMode":"exclusion"}},{"x":731.7712962962963,"y":883.2546796767833,"width":348.2287037037037,"height":196.7453203232167,"gradientConfig":{"type":"text","colors":["#ffffff"],"angle":31.978395061728392,"rotationDirection":-1,"textContent":"GURU"},"secondLayer":{"enabled":true,"type":"linear","colors":["#ffffff"],"angle":90,"rotationDirection":1,"blendMode":"exclusion"}},{"x":129.8141199798049,"y":393.83503017427313,"width":218.40661945429682,"height":401.7399698257268,"gradientConfig":{"type":"text","colors":["#ff00ea"],"angle":70.58333333333333,"rotationDirection":1,"textContent":"GURU"},"secondLayer":{"enabled":true,"type":"linear","colors":["#ff0000"],"angle":90,"rotationDirection":-1,"blendMode":"exclusion"}},{"x":0,"y":795.5749999999999,"width":177.79144023157676,"height":284.425,"gradientConfig":{"type":"text","colors":["#4dff00"],"angle":214.37345679012344,"rotationDirection":1,"textContent":"GURU"},"secondLayer":{"enabled":true,"type":"linear","colors":["#989898"],"angle":180,"rotationDirection":-1,"blendMode":"exclusion"}},{"x":731.7712962962963,"y":310.9861111111111,"width":348.2287037037037,"height":195.52509425993802,"gradientConfig":{"type":"text","colors":["#ff0a0a","#ff00ea","#0084ff"],"angle":39.111111111111114,"rotationDirection":-1,"textContent":"GURU"},"secondLayer":{"enabled":true,"type":"linear","colors":["#c80e0e"],"angle":90,"rotationDirection":1,"blendMode":"exclusion"}},{"x":0,"y":177.64635903206445,"width":110.51289918886222,"height":216.18867114220868,"gradientConfig":{"type":"text","colors":["#ffffff","#ffffff","#949494"],"angle":125.70061728395062,"rotationDirection":-1,"textContent":"GURU"},"secondLayer":{"enabled":true,"type":"linear","colors":["#70ade6"],"angle":180,"rotationDirection":1,"blendMode":"exclusion"}},{"x":177.79144023157676,"y":795.5749999999999,"width":362.50059439518924,"height":284.425,"gradientConfig":{"type":"text","colors":["#ffffff"],"angle":337.63888888888886,"rotationDirection":-1,"textContent":"GURU"},"secondLayer":{"enabled":true,"type":"linear","colors":["#000000"],"angle":90,"rotationDirection":1,"blendMode":"exclusion"}},{"x":540.292034626766,"y":795.5749999999999,"width":191.47926166953033,"height":284.425,"gradientConfig":{"type":"text","colors":["#989898"],"angle":134.76234567901236,"rotationDirection":1,"textContent":"GURU"},"secondLayer":{"enabled":true,"type":"linear","colors":["#000000"],"angle":270,"rotationDirection":1,"blendMode":"exclusion"}},{"x":348.2207394341017,"y":393.83503017427313,"width":90.13734035765324,"height":401.7399698257268,"gradientConfig":{"type":"text","colors":["#ff00ea","#949494","#989898"],"angle":246.16666666666669,"rotationDirection":1,"textContent":"GURU"},"secondLayer":{"enabled":true,"type":"linear","colors":["#a4afda"],"angle":0,"rotationDirection":1,"blendMode":"exclusion"}},{"x":438.3580797917549,"y":393.83503017427313,"width":293.4132165045413,"height":401.7399698257268,"gradientConfig":{"type":"text","colors":["#989898","#1c1c1c"],"angle":144.18055555555554,"rotationDirection":-1,"textContent":"GURU"},"secondLayer":{"enabled":true,"type":"linear","colors":["#44ff00"],"angle":0,"rotationDirection":1,"blendMode":"exclusion"}},{"x":110.51289918886222,"y":177.64635903206445,"width":105.49302073859354,"height":216.18867114220868,"gradientConfig":{"type":"text","colors":["#1c1c1c","#989898","#ff0a0a"],"angle":299.1033950617284,"rotationDirection":1,"textContent":"GURU"},"secondLayer":{"enabled":true,"type":"linear","colors":["#70ade6"],"angle":0,"rotationDirection":1,"blendMode":"exclusion"}},{"x":216.00591992745575,"y":177.64635903206445,"width":515.7653763688405,"height":216.18867114220868,"gradientConfig":{"type":"text","colors":["#4dff00"],"angle":277.70833333333337,"rotationDirection":1,"textContent":"GURU"},"secondLayer":{"enabled":true,"type":"linear","colors":["#70ade6"],"angle":270,"rotationDirection":-1,"blendMode":"exclusion"}},{"x":731.7712962962963,"y":506.5112053710491,"width":230.64538880633032,"height":376.7434743057342,"gradientConfig":{"type":"text","colors":["#4dff00","#ffffff","#0084ff"],"angle":135.9645061728395,"rotationDirection":1,"textContent":"GURU"},"secondLayer":{"enabled":true,"type":"linear","colors":["#ff0000"],"angle":180,"rotationDirection":-1,"blendMode":"exclusion"}},{"x":962.4166851026266,"y":506.5112053710491,"width":117.58331489737336,"height":376.7434743057342,"gradientConfig":{"type":"text","colors":["#0084ff","#ffffff","#4dff00"],"angle":238.29475308641975,"rotationDirection":1,"textContent":"GURU"},"secondLayer":{"enabled":true,"type":"linear","colors":["#a4afda"],"angle":0,"rotationDirection":1,"blendMode":"exclusion"}}];
const config = {"width":1080,"height":1080,"animationSpeed":47,"loopDuration":10,"shrinkPercentage":100};
const canvas = document.getElementById('artCanvas');
const ctx = canvas.getContext('2d');
let rotation = 0;
let lastTime = 0;
let offscreenCache = null;
let scaleX = 1;
let scaleY = 1;
// --- Glitch State Variables ---
let currentChaos = 0; // Current intensity of glitch (0.0 to 1.0)
let nextBurstTime = 0; // Timestamp for next random glitch
let mouseVelocity = 0; // How fast mouse is moving
let lastMousePos = { x: 0, y: 0 };
let isGlitching = false; // Is a burst currently active?
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
scaleX = window.innerWidth / config.width;
scaleY = window.innerHeight / config.height;
offscreenCache = null;
}
function scalePartition(p) {
return {
x: p.x * scaleX,
y: p.y * scaleY,
width: p.width * scaleX,
height: p.height * scaleY,
gradientConfig: p.gradientConfig,
secondLayer: p.secondLayer
};
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
// --- Interaction: Mouse Velocity & Click ---
window.addEventListener('mousemove', (e) => {
const dx = e.clientX - lastMousePos.x;
const dy = e.clientY - lastMousePos.y;
const dist = Math.sqrt(dx*dx + dy*dy);
// Add speed to velocity (sensitivity adjusted by factor)
mouseVelocity += dist * 0.005;
// Cap max velocity
if (mouseVelocity > 2.0) mouseVelocity = 2.0;
lastMousePos = { x: e.clientX, y: e.clientY };
});
window.addEventListener('mousedown', () => {
// Click triggers an immediate massive burst
mouseVelocity = 3.0;
currentChaos = 1.0;
});
// --- Helper Functions ---
function hexToRgb(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)] : [0, 0, 0];
}
function rgbToHex(r, g, b) {
return '#' + [r, g, b].map(x => {
const hex = Math.round(x).toString(16);
return hex.length === 1 ? '0' + hex : hex;
}).join('');
}
function interpolateColor(color1, color2, t) {
const rgb1 = hexToRgb(color1);
const rgb2 = hexToRgb(color2);
return rgbToHex(
rgb1[0] + (rgb2[0] - rgb1[0]) * t,
rgb1[1] + (rgb2[1] - rgb1[1]) * t,
rgb1[2] + (rgb2[2] - rgb1[2]) * t
);
}
// --- Gradient & Pattern Drawing ---
function createLinearGradientPattern(ctx, colors, partitionLength) {
const offscreenCanvas = document.createElement('canvas');
const totalSegments = colors.length * 2;
const tileWidth = Math.ceil(partitionLength);
offscreenCanvas.width = tileWidth;
offscreenCanvas.height = 100;
const offscreenCtx = offscreenCanvas.getContext('2d');
if (!offscreenCtx) return null;
const gradient = offscreenCtx.createLinearGradient(0, 0, tileWidth, 0);
const segmentSize = 1 / totalSegments;
for (let i = 0; i < colors.length; i++) {
gradient.addColorStop(i * 2 * segmentSize, '#000000');
gradient.addColorStop((i * 2 + 1) * segmentSize, colors[i]);
}
gradient.addColorStop(1, '#000000');
offscreenCtx.fillStyle = gradient;
offscreenCtx.fillRect(0, 0, tileWidth, 100);
const pattern = ctx.createPattern(offscreenCanvas, 'repeat');
if (!pattern) return null;
return { pattern, tileWidth };
}
function createGradientForLayer(ctx, partition, layerConfig, rotation) {
const { type, colors, angle } = layerConfig;
const centerX = partition.x + partition.width / 2;
const centerY = partition.y + partition.height / 2;
let gradient;
if (type === 'linear') {
const angleRad = (angle * Math.PI) / 180;
const partitionLength = Math.abs(Math.cos(angleRad)) * partition.width + Math.abs(Math.sin(angleRad)) * partition.height;
const halfLength = partitionLength / 2;
const startX = centerX - halfLength * Math.cos(angleRad);
const startY = centerY - halfLength * Math.sin(angleRad);
const endX = centerX + halfLength * Math.cos(angleRad);
const endY = centerY + halfLength * Math.sin(angleRad);
gradient = ctx.createLinearGradient(startX, startY, endX, endY);
const totalSegments = colors.length * 2;
const rotationFraction = ((rotation % 360) / 360);
const allStops = [];
for (let i = 0; i < colors.length; i++) {
const blackPos = ((i * 2 / totalSegments - rotationFraction + 1) % 1);
const colorPos = (((i * 2 + 1) / totalSegments - rotationFraction + 1) % 1);
allStops.push({ position: blackPos, color: '#000000' });
allStops.push({ position: colorPos, color: colors[i] });
}
allStops.sort((a, b) => a.position - b.position);
const firstStop = allStops[0];
const lastStop = allStops[allStops.length - 1];
const totalGap = firstStop.position + (1 - lastStop.position);
if (firstStop.position > 0) {
const t = (1 - lastStop.position) / totalGap;
gradient.addColorStop(0, interpolateColor(lastStop.color, firstStop.color, t));
}
allStops.forEach(stop => gradient.addColorStop(stop.position, stop.color));
if (lastStop.position < 1) {
const t = (1 - lastStop.position) / totalGap;
gradient.addColorStop(1, interpolateColor(lastStop.color, firstStop.color, t));
}
return gradient;
} else if (type === 'conic') {
const normalizedRotation = rotation % 360;
const rotationDirection = layerConfig.rotationDirection || 1;
gradient = ctx.createConicGradient(((angle + normalizedRotation * rotationDirection) * Math.PI) / 180, centerX, centerY);
} else {
const maxSize = Math.max(partition.width, partition.height);
gradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, maxSize);
}
const segmentSize = 1 / (colors.length * 2);
colors.forEach((color, i) => {
gradient.addColorStop(i * segmentSize * 2, color);
gradient.addColorStop(i * segmentSize * 2 + segmentSize, '#000000');
});
gradient.addColorStop(1, colors[0]);
return gradient;
}
function calculateTextHeight(ctx, text, targetHeight) {
let minSize = 1;
let maxSize = targetHeight * 2;
let fontSize = targetHeight;
for (let i = 0; i < 20; i++) {
ctx.font = `bold ${fontSize}px Helvetica`;
const metrics = ctx.measureText(text);
const actualHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
if (Math.abs(actualHeight - targetHeight) < 1) break;
if (actualHeight < targetHeight) {
minSize = fontSize;
fontSize = (fontSize + maxSize) / 2;
} else {
maxSize = fontSize;
fontSize = (minSize + fontSize) / 2;
}
}
return fontSize;
}
function getOffscreenCanvas(width, height) {
if (!offscreenCache) {
offscreenCache = document.createElement('canvas');
}
if (offscreenCache.width < width) offscreenCache.width = width;
if (offscreenCache.height < height) offscreenCache.height = height;
const octx = offscreenCache.getContext('2d');
if (octx) octx.clearRect(0, 0, width, height);
return offscreenCache;
}
function drawGradientLayer(ctx, partition, layerConfig, rotation, partitionIndex, blendMode) {
ctx.globalCompositeOperation = blendMode;
const centerX = partition.x + partition.width / 2;
const centerY = partition.y + partition.height / 2;
const { type, colors, angle } = layerConfig;
if (type === 'linear') {
const angleRad = (angle * Math.PI) / 180;
const partitionLength = Math.abs(Math.cos(angleRad)) * partition.width + Math.abs(Math.sin(angleRad)) * partition.height;
const result = createLinearGradientPattern(ctx, colors, partitionLength);
if (result) {
const { pattern, tileWidth } = result;
const maxSize = Math.max(partition.width, partition.height) * 2;
if (pattern.setTransform) {
const offsetDistance = ((rotation / 360) * tileWidth) % tileWidth;
const offsetX = offsetDistance * Math.cos(angleRad);
const offsetY = offsetDistance * Math.sin(angleRad);
const matrix = new DOMMatrix();
matrix.translateSelf(offsetX, offsetY);
matrix.rotateSelf(angle);
pattern.setTransform(matrix);
ctx.fillStyle = pattern;
ctx.fillRect(centerX - maxSize, centerY - maxSize, maxSize * 2, maxSize * 2);
} else {
const gradient = createGradientForLayer(ctx, partition, layerConfig, rotation);
ctx.fillStyle = gradient;
ctx.fillRect(partition.x, partition.y, partition.width, partition.height);
}
}
} else if (type === 'radial') {
const maxSize = Math.max(partition.width, partition.height);
const selectedColor = colors[partitionIndex % colors.length];
const currentCycle = Math.floor(rotation / 360);
const maxIterations = 4;
const startCycle = Math.max(0, currentCycle - maxIterations + 1);
const offscreen = getOffscreenCanvas(partition.width, partition.height);
const offscreenCtx = offscreen.getContext('2d');
if (!offscreenCtx) return;
offscreenCtx.globalCompositeOperation = 'lighten';
const offscreenCenterX = partition.width / 2;
const offscreenCenterY = partition.height / 2;
for (let cycle = startCycle; cycle <= currentCycle; cycle++) {
const cycleAge = rotation / 360 - cycle;
const iterationSize = maxSize * cycleAge;
if (iterationSize > 0 && iterationSize <= maxSize * maxIterations) {
const gradient = offscreenCtx.createRadialGradient(offscreenCenterX, offscreenCenterY, 0, offscreenCenterX, offscreenCenterY, iterationSize);
gradient.addColorStop(0, '#000000');
gradient.addColorStop(0.5, selectedColor);
gradient.addColorStop(1, '#000000');
offscreenCtx.fillStyle = gradient;
offscreenCtx.fillRect(offscreenCenterX - iterationSize, offscreenCenterY - iterationSize, iterationSize * 2, iterationSize * 2);
}
}
ctx.drawImage(offscreen, partition.x, partition.y);
} else if (type === 'text') {
const textContent = layerConfig.textContent || '$$$';
const xCoord = Math.round(partition.x * 10000);
const yCoord = Math.round(partition.y * 10000);
const hash = Math.abs((xCoord * 73856093) ^ (yCoord * 19349663));
const selectedColor = colors[hash % colors.length];
const hash2 = Math.abs((xCoord * 19349663) ^ (yCoord * 73856093));
const randomStartOffset = (hash2 % 10000) / 10000;
const hash3 = Math.abs((xCoord * 50331653) ^ (yCoord * 25165843));
const direction = hash3 % 3;
ctx.save();
const partCenterX = partition.x + partition.width / 2;
const partCenterY = partition.y + partition.height / 2;
if (direction === 1) {
ctx.translate(partCenterX, partCenterY);
ctx.rotate(-Math.PI / 2);
ctx.translate(-partCenterX, -partCenterY);
} else if (direction === 2) {
ctx.translate(partCenterX, partCenterY);
ctx.rotate(Math.PI / 2);
ctx.translate(-partCenterX, -partCenterY);
}
const fillDimension = direction === 0 ? partition.height : partition.width;
const targetHeight = fillDimension * 0.95;
const fontSize = calculateTextHeight(ctx, textContent, targetHeight);
ctx.font = `bold ${fontSize}px Helvetica`;
ctx.fillStyle = selectedColor;
ctx.textBaseline = 'middle';
const wordWidth = ctx.measureText(textContent).width;
const normalizedRotation = rotation % 360;
const scrollProgress = (normalizedRotation / 360 + randomStartOffset) % 1;
const scrollOffset = -(scrollProgress * wordWidth) % wordWidth;
const scrollDimensionSize = direction === 0 ? partition.width : partition.height;
const textY = partition.y + partition.height * 0.55;
const textX = partition.x + scrollOffset;
const numCopies = Math.ceil(scrollDimensionSize / wordWidth) + 3;
for (let i = -1; i < numCopies; i++) {
ctx.fillText(textContent, textX + (i * wordWidth), textY);
}
ctx.restore();
} else {
ctx.save();
const normalizedRotation = rotation % 360;
const rotationDirection = layerConfig.rotationDirection || 1;
ctx.translate(centerX, centerY);
ctx.rotate((normalizedRotation * rotationDirection * Math.PI) / 180);
ctx.translate(-centerX, -centerY);
const gradient = createGradientForLayer(ctx, partition, layerConfig, rotation);
ctx.fillStyle = gradient;
const maxSize = Math.max(partition.width, partition.height) * 2;
ctx.fillRect(centerX - maxSize, centerY - maxSize, maxSize * 2, maxSize * 2);
ctx.restore();
}
}
function renderPartitions(rotation) {
// 1. Trails / Feedback (always slightly visible)
// When chaos is high, trails are stronger (less erasure)
const baseAlpha = 0.2;
// If chaos is high, we clear less, creating smear
const clearAlpha = baseAlpha - (currentChaos * 0.15);
ctx.globalCompositeOperation = 'source-over';
ctx.fillStyle = `rgba(0, 0, 0, ${Math.max(0.02, clearAlpha)})`;
ctx.fillRect(0, 0, canvas.width, canvas.height);
const scale = config.shrinkPercentage / 100;
// Render partitions
originalPartitions.forEach((originalPartition, partitionIndex) => {
// --- Jitter partitions during High Chaos ---
let jitterX = 0;
let jitterY = 0;
if (currentChaos > 0.3) {
jitterX = (Math.random() - 0.5) * 50 * currentChaos;
jitterY = (Math.random() - 0.5) * 50 * currentChaos;
}
const partition = scalePartition(originalPartition);
ctx.save();
const centerX = partition.x + partition.width / 2;
const centerY = partition.y + partition.height / 2;
ctx.translate(centerX + jitterX, centerY + jitterY);
ctx.scale(scale, scale);
ctx.translate(-centerX, -centerY);
ctx.beginPath();
ctx.rect(partition.x, partition.y, partition.width, partition.height);
ctx.clip();
const primaryLayerConfig = {
type: partition.gradientConfig.type,
colors: partition.gradientConfig.colors,
angle: partition.gradientConfig.angle,
rotationDirection: partition.gradientConfig.rotationDirection,
textContent: partition.gradientConfig.textContent,
};
drawGradientLayer(ctx, partition, primaryLayerConfig, rotation, partitionIndex, 'source-over');
if (partition.secondLayer.enabled) {
const secondLayerConfig = {
type: partition.secondLayer.type,
colors: partition.secondLayer.colors,
angle: partition.secondLayer.angle,
rotationDirection: partition.secondLayer.rotationDirection,
textContent: partition.secondLayer.textContent,
};
ctx.save();
drawGradientLayer(ctx, partition, secondLayerConfig, rotation, partitionIndex, partition.secondLayer.blendMode);
ctx.restore();
}
ctx.restore();
});
// --- 2. THE GLITCH / DATAMOSH LAYER ---
// If chaos is active (either from Burst or Mouse)
if (currentChaos > 0.05) {
// A. Horizontal Tearing (The "Datamosh" look)
// Number of tears depends on intensity
const numTears = Math.floor(currentChaos * 20);
for(let i = 0; i < numTears; i++) {
if (Math.random() < 0.8) {
const sliceY = Math.random() * canvas.height;
const sliceH = Math.random() * 50 + 5;
// Extreme horizontal stretching
const stretch = (Math.random() - 0.5) * 400 * currentChaos;
ctx.save();
ctx.globalCompositeOperation = 'source-over';
ctx.drawImage(
canvas,
0, sliceY, canvas.width, sliceH,
stretch, sliceY, canvas.width + Math.abs(stretch), sliceH
);
// Invert color occasionally (Digital artifact)
if (Math.random() < 0.2 * currentChaos) {
ctx.globalCompositeOperation = 'difference';
ctx.fillStyle = '#fff';
ctx.fillRect(stretch, sliceY, canvas.width, sliceH);
}
ctx.restore();
}
}
// B. RGB Shift (Chromatic Aberration)
// Scales with Chaos
ctx.save();
ctx.globalCompositeOperation = 'screen';
ctx.globalAlpha = 0.5 * currentChaos;
const shiftX = (Math.random() - 0.5) * 30 * currentChaos;
const shiftY = (Math.random() - 0.5) * 10 * currentChaos;
// Draw Red Channel offset
ctx.translate(shiftX, shiftY);
// Just drawing the canvas over itself with screen blend creates the ghosting
// If we had separate channels we'd color it red, but this is a cheap fast approximation
ctx.drawImage(canvas, 0, 0);
ctx.restore();
}
}
function animate(timestamp) {
if (!lastTime) lastTime = timestamp;
const deltaTime = timestamp - lastTime;
lastTime = timestamp;
const currentTime = timestamp / 1000;
// --- LOGIC: Update Chaos Level ---
// 1. Decay the mouse velocity
mouseVelocity *= 0.92; // Friction
if (mouseVelocity < 0.01) mouseVelocity = 0;
// 2. Random Bursts (The "Every few seconds" logic)
if (currentTime > nextBurstTime) {
// Trigger a burst
isGlitching = true;
// Duration of burst is short (0.1s to 0.4s)
const burstDuration = Math.random() * 0.3 + 0.1;
// Schedule end of burst
setTimeout(() => { isGlitching = false; }, burstDuration * 1000);
// Schedule next burst (Randomly between 2 and 6 seconds)
nextBurstTime = currentTime + (Math.random() * 4 + 2);
}
// 3. Combine Sources into currentChaos
let targetChaos = 0;
if (isGlitching) targetChaos = 1.0; // Max chaos during random burst
targetChaos += mouseVelocity; // Add mouse movement chaos
// Smoothly interpolate currentChaos towards target (Easing)
// This makes the onset fast but the falloff smooth
currentChaos += (targetChaos - currentChaos) * 0.2;
// Clamp
if (currentChaos > 1.0) currentChaos = 1.0;
if (currentChaos < 0.0) currentChaos = 0.0;
// --- Rotation Logic ---
const actualLoopDuration = config.loopDuration / (config.animationSpeed / 50);
const degreesPerSecond = 360 / actualLoopDuration;
// Chaos can also speed up or reverse rotation momentarily!
const chaosSpeedMod = 1 + (Math.random() - 0.5) * 10 * currentChaos;
rotation += (deltaTime / 1000) * degreesPerSecond * chaosSpeedMod;
renderPartitions(rotation);
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
</script>
</body>
</html>