memoscan
← all memos

Memo 0x87c8cf1c…acd6eb on Ethereum

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Binary Partitions - Generative Art</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } html, body { width: 100%; height: 100%; background: #000; overflow: hidden; } canvas { display: block; width: 100%; height: 100%; } </style> </head> <body> <canvas id="artCanvas"></canvas> <script> const originalPartitions = [{"x":0,"y":0,"width":419.44362139917695,"height":107,"gradientConfig":{"type":"text","colors":["#ffffff","#ffffff"],"angle":343.5725308641975,"rotationDirection":1,"textContent":"READING THE SKY______"},"secondLayer":{"enabled":false,"type":"linear","colors":["#949494","#989898"],"angle":270,"rotationDirection":-1,"blendMode":"difference"}},{"x":419.44362139917695,"y":0,"width":263.1158198050207,"height":107,"gradientConfig":{"type":"text","colors":["#ffffff","#ffffff"],"angle":52.199074074074076,"rotationDirection":1,"textContent":"READING THE SKY______"},"secondLayer":{"enabled":false,"type":"linear","colors":["#292929","#292929"],"angle":270,"rotationDirection":-1,"blendMode":"difference"}},{"x":682.5594412041976,"y":0,"width":376.16962336630905,"height":107,"gradientConfig":{"type":"text","colors":["#ffffff"],"angle":262.71296296296293,"rotationDirection":-1,"textContent":"READING THE SKY______"},"secondLayer":{"enabled":false,"type":"linear","colors":["#000000","#4dff00"],"angle":0,"rotationDirection":-1,"blendMode":"difference"}},{"x":1058.7290645705066,"y":0,"width":247.81107020303077,"height":107,"gradientConfig":{"type":"text","colors":["#ffffff","#ffffff","#ffffff"],"angle":77.22993827160494,"rotationDirection":1,"textContent":"READING THE SKY______"},"secondLayer":{"enabled":false,"type":"linear","colors":["#4dff00","#ff00ea"],"angle":180,"rotationDirection":1,"blendMode":"difference"}},{"x":1742.0293183858098,"y":0,"width":177.97068161419008,"height":107,"gradientConfig":{"type":"text","colors":["#ffffff","#ffffff"],"angle":264.7453703703704,"rotationDirection":-1,"textContent":"READING THE SKY______"},"secondLayer":{"enabled":false,"type":"linear","colors":["#989898"],"angle":180,"rotationDirection":1,"blendMode":"difference"}},{"x":1306.5401347735374,"y":0,"width":284.7283091966772,"height":107,"gradientConfig":{"type":"text","colors":["#ffffff","#ffffff"],"angle":240.25925925925927,"rotationDirection":-1,"textContent":"READING THE SKY______"},"secondLayer":{"enabled":false,"type":"linear","colors":["#4dff00","#4dff00"],"angle":180,"rotationDirection":-1,"blendMode":"difference"}},{"x":1591.2684439702145,"y":0,"width":150.76087441559528,"height":107,"gradientConfig":{"type":"text","colors":["#ffffff","#ffffff","#ffffff"],"angle":327.55401234567904,"rotationDirection":1,"textContent":"READING THE SKY______"},"secondLayer":{"enabled":false,"type":"linear","colors":["#ff00ea","#4a4a4a"],"angle":270,"rotationDirection":1,"blendMode":"difference"}}]; const config = {"width":1920,"height":107,"animationSpeed":44,"loopDuration":20,"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; 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(); 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 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 ); } 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 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) { ctx.fillStyle = '#000000'; ctx.fillRect(0, 0, canvas.width, canvas.height); const scale = config.shrinkPercentage / 100; originalPartitions.forEach((originalPartition, partitionIndex) => { const partition = scalePartition(originalPartition); ctx.save(); const centerX = partition.x + partition.width / 2; const centerY = partition.y + partition.height / 2; ctx.translate(centerX, centerY); 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(); }); } function animate(timestamp) { if (!lastTime) lastTime = timestamp; const deltaTime = timestamp - lastTime; lastTime = timestamp; const actualLoopDuration = config.loopDuration / (config.animationSpeed / 50); const degreesPerSecond = 360 / actualLoopDuration; rotation += (deltaTime / 1000) * degreesPerSecond; renderPartitions(rotation); requestAnimationFrame(animate); } requestAnimationFrame(animate); </script> </body> </html>