0x26cd…87d9

All memos sent from and to 0x26cd…87d9.

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <style> *{margin:0;padding:0;box-sizing:border-box} html,body{height:100%;overflow:hidden} body{background:#000;height:100%;display:flex;align-items:center;justify-content:center;font-family:system-ui,sans-serif} #c{position:relative;height:100%;display:flex;align-items:center;justify-content:center} video{max-height:100vh;max-width:100vw;object-fit:contain} .hidden{display:none!important} #l{color:#fff;font-size:14px;text-align:center;padding:40px} .progress-wrap{width:200px;height:4px;background:#333;border-radius:2px;margin:16px auto 0;overflow:hidden} .progress-bar{height:100%;background:#0052FF;width:0%;transition:width 0.3s} #play-btn{display:none;width:80px;height:80px;border-radius:50%;background:rgba(0,82,255,0.9);border:none;cursor:pointer;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:10} #play-btn:hover{background:#0052FF} #play-btn svg{fill:#fff;width:32px;height:32px;margin-left:4px} </style> </head> <body> <div id="c"> <div id="l"> <div>Loading from Base...</div> <div class="progress-wrap"><div class="progress-bar" id="progress"></div></div> </div> <button id="play-btn" onclick="startPlayback()"> <svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg> </button> <video id="v0" class="hidden" playsinline></video> <video id="v1" class="hidden" playsinline></video> <video id="v2" class="hidden" playsinline></video> <video id="v3" class="hidden" playsinline></video> <video id="v4" class="hidden" playsinline></video> <video id="v5" class="hidden" playsinline></video> <video id="v6" class="hidden" playsinline></video> <video id="v7" class="hidden" playsinline></video> <video id="v8" class="hidden" playsinline></video> <video id="v9" class="hidden" playsinline></video> <video id="v10" class="hidden" playsinline></video> </div> <script> const T=['0x4ae8bc89b1572caab629f4b98de81be6aec6901a10f6d248686848b6cb7f8ccf','0x427bc464296f3874d24af645be6391cd962a29a57c129a5d7048d08b14ee97d4','0x38e7f3c188f4b4d3e2444d90446eb9eddf898dd98c76eeee431ce652691fe5da','0x34c7ca1eed01dd0ec9c2f114e1fe68acd32b46d2f714fbbdb71ad8bb21910ed8','0x5f2a75503bfeb2f7a04090f388abe8047054995b044010bae9e1a8f1684231a4','0xc60c78380f3b6fb961a5611b20bafcdd290a163215d91616770f75044c65433e','0xeeee59c81b9f77e9e51dddb0874de9e93f0ac38cb96fcb5d18eeb0e2848d9c99','0x81fb6c3ea72e806bd66f00b323b477f1652da2056856e27b031bbfc53325428f','0x0103cb3d295009a87f3a48f27643d6ff4be662fef05e67196bf21911af205809','0x78480c0f270a991ca638c43b91f84b77d14b2f63723cf3a76c3bca2e0c1006b4','0x82bf5fda6ad091a26d44e84c406b5683cd0ac7c208a0ffbbb153df8eab8f0180']; const R='https://mainnet.base.org'; const videos=[]; let current=0; let loaded=0; let ready=false; async function fetchTx(h){ const r=await fetch(R,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({jsonrpc:'2.0',id:1,method:'eth_getTransactionByHash',params:[h]})}); const j=await r.json(); const x=j.result.input.slice(2); let s='';for(let i=0;i<x.length;i+=2)s+=String.fromCharCode(parseInt(x.substr(i,2),16)); return s; } function updateProgress(){ const pct=Math.round((loaded/T.length)*100); document.getElementById('progress').style.width=pct+'%'; } function onVideoReady(){ loaded++; updateProgress(); if(loaded===T.length){ ready=true; document.getElementById('l').classList.add('hidden'); document.getElementById('play-btn').style.display='block'; videos[0].classList.remove('hidden'); } } function startPlayback(){ document.getElementById('play-btn').style.display='none'; videos[0].play(); } function togglePlayback(e){ const v=videos[current]; if(v.paused){v.play();}else{v.pause();} } function playNext(){ if(current>=videos.length-1)return; // stop at end, no loop videos[current].classList.add('hidden'); current++; videos[current].classList.remove('hidden'); videos[current].currentTime=0; videos[current].play(); } async function init(){ const data=await Promise.all(T.map(fetchTx)); for(let i=0;i<T.length;i++){ const v=document.getElementById('v'+i); videos.push(v); v.src=data[i]; v.load(); v.oncanplaythrough=()=>{ if(!v._ready){v._ready=true;onVideoReady();} }; v.onended=playNext; v.onclick=togglePlayback; } } init(); </script> </body> </html>
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <style> *{margin:0;padding:0;box-sizing:border-box} html,body{height:100%;overflow:hidden} body{background:#000;height:100%;display:flex;align-items:center;justify-content:center;font-family:system-ui,sans-serif} #c{position:relative;height:100%;display:flex;align-items:center;justify-content:center} video{max-height:100vh;max-width:100vw;object-fit:contain} .hidden{display:none!important} #l{color:#fff;font-size:14px;text-align:center;padding:40px} .progress-wrap{width:200px;height:4px;background:#333;border-radius:2px;margin:16px auto 0;overflow:hidden} .progress-bar{height:100%;background:#0052FF;width:0%;transition:width 0.3s} #play-btn{display:none;width:80px;height:80px;border-radius:50%;background:rgba(0,82,255,0.9);border:none;cursor:pointer;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:10} #play-btn:hover{background:#0052FF} #play-btn svg{fill:#fff;width:32px;height:32px;margin-left:4px} </style> </head> <body> <div id="c"> <div id="l"> <div>Loading from Base...</div> <div class="progress-wrap"><div class="progress-bar" id="progress"></div></div> </div> <button id="play-btn" onclick="startPlayback()"> <svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg> </button> <video id="v0" class="hidden" playsinline></video> <video id="v1" class="hidden" playsinline></video> <video id="v2" class="hidden" playsinline></video> <video id="v3" class="hidden" playsinline></video> <video id="v4" class="hidden" playsinline></video> <video id="v5" class="hidden" playsinline></video> <video id="v6" class="hidden" playsinline></video> <video id="v7" class="hidden" playsinline></video> <video id="v8" class="hidden" playsinline></video> <video id="v9" class="hidden" playsinline></video> <video id="v10" class="hidden" playsinline></video> </div> <script> const T=['0x4ae8bc89b1572caab629f4b98de81be6aec6901a10f6d248686848b6cb7f8ccf','0x427bc464296f3874d24af645be6391cd962a29a57c129a5d7048d08b14ee97d4','0x38e7f3c188f4b4d3e2444d90446eb9eddf898dd98c76eeee431ce652691fe5da','0x34c7ca1eed01dd0ec9c2f114e1fe68acd32b46d2f714fbbdb71ad8bb21910ed8','0x5f2a75503bfeb2f7a04090f388abe8047054995b044010bae9e1a8f1684231a4','0xc60c78380f3b6fb961a5611b20bafcdd290a163215d91616770f75044c65433e','0xeeee59c81b9f77e9e51dddb0874de9e93f0ac38cb96fcb5d18eeb0e2848d9c99','0x81fb6c3ea72e806bd66f00b323b477f1652da2056856e27b031bbfc53325428f','0x0103cb3d295009a87f3a48f27643d6ff4be662fef05e67196bf21911af205809','0x78480c0f270a991ca638c43b91f84b77d14b2f63723cf3a76c3bca2e0c1006b4','0x82bf5fda6ad091a26d44e84c406b5683cd0ac7c208a0ffbbb153df8eab8f0180']; const R='https://mainnet.base.org'; const videos=[]; let current=0; let loaded=0; let ready=false; async function fetchTx(h){ const r=await fetch(R,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({jsonrpc:'2.0',id:1,method:'eth_getTransactionByHash',params:[h]})}); const j=await r.json(); const x=j.result.input.slice(2); let s='';for(let i=0;i<x.length;i+=2)s+=String.fromCharCode(parseInt(x.substr(i,2),16)); return s; } function updateProgress(){ const pct=Math.round((loaded/T.length)*100); document.getElementById('progress').style.width=pct+'%'; } function onVideoReady(){ loaded++; updateProgress(); if(loaded===T.length){ ready=true; document.getElementById('l').classList.add('hidden'); document.getElementById('play-btn').style.display='block'; videos[0].classList.remove('hidden'); } } function startPlayback(){ document.getElementById('play-btn').style.display='none'; videos[0].play(); } function togglePlayback(e){ const v=videos[current]; if(v.paused){v.play();}else{v.pause();} } function playNext(){ if(current>=videos.length-1)return; // stop at end, no loop videos[current].classList.add('hidden'); current++; videos[current].classList.remove('hidden'); videos[current].currentTime=0; videos[current].play(); } async function init(){ const data=await Promise.all(T.map(fetchTx)); for(let i=0;i<T.length;i++){ const v=document.getElementById('v'+i); videos.push(v); v.src=data[i]; v.load(); v.oncanplaythrough=()=>{ if(!v._ready){v._ready=true;onVideoReady();} }; v.onended=playNext; v.onclick=togglePlayback; } } init(); </script> </body> </html>
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <title>Smear Art</title> <style>*{margin:0;padding:0;box-sizing:border-box}body,html{width:100%;height:100%;overflow:hidden;background:#000}#container{position:relative;width:600px;height:600px;margin:0 auto;background:#000}.partition{position:absolute;overflow:hidden}.partition canvas{width:100%;height:100%;display:block;image-rendering:pixelated;image-rendering:crisp-edges;position:relative;z-index:10}</style> </head> <body> <div id="container"></div> <script> const container = document.getElementById('container'); const partitions = [{"x":-2,"y":0,"w":308,"h":600,"pattern":"random-blocks","color1":"#ceff00","color2":"#000000","cellSize":10,"speed":5,"smearAmount":2,"direction":{"dx":-1,"dy":0,"name":"left"},"enableDirectionChange":true,"directionChangeInterval":3861.3076612313293,"sourceInterval":1000,"smearMode":"replace","trailIntensity":0.85,"opacity":1},{"x":303,"y":3,"w":311,"h":605,"pattern":"cellular-automata","color1":"#f865b0","color2":"#000000","cellSize":10,"speed":5,"smearAmount":2,"direction":{"dx":1,"dy":0,"name":"right"},"enableDirectionChange":false,"directionChangeInterval":3000,"sourceInterval":1000,"smearMode":"trail","trailIntensity":0.98,"opacity":1},{"x":163,"y":-2,"w":222,"h":611,"pattern":"perlin","color1":"#f4faff","color2":"#000000","cellSize":10,"speed":5,"smearAmount":2,"direction":{"dx":-1,"dy":1,"name":"down-left"},"enableDirectionChange":true,"directionChangeInterval":2805.7492425761707,"sourceInterval":1000,"smearMode":"replace","trailIntensity":0.85,"opacity":0.81}]; class SmearPartition { constructor(config) { this.config = config; this.div = document.createElement('div'); this.div.className = 'partition'; this.div.style.left = config.x + 'px'; this.div.style.top = config.y + 'px'; this.div.style.width = config.w + 'px'; this.div.style.height = config.h + 'px'; this.canvas = document.createElement('canvas'); this.canvas.width = Math.floor(config.w); this.canvas.height = Math.floor(config.h); this.ctx = this.canvas.getContext('2d'); this.ctx.imageSmoothingEnabled = false; this.div.appendChild(this.canvas); container.appendChild(this.div); this.direction = config.direction; this.enableDirectionChange = config.enableDirectionChange || false; this.directionChangeInterval = config.directionChangeInterval || 3000; this.lastDirectionChange = 0; this.lastSourceTime = 0; this.imageData = null; this.opacity = config.opacity !== undefined ? config.opacity : 1.0; this.fgRGB = this.hexToRGB(config.color1); this.bgRGB = this.hexToRGB(config.color2 || '#000000'); this.sourceParams = this.generateSourceParams(); this.gradientCache = null; this.caState = null; this.initPattern(); this.start(); } generateSourceParams() { return { steps: 3 + Math.floor(Math.random() * 6), stripeWidth: 2 + Math.floor(Math.random() * 8), checkSize: 1 + Math.floor(Math.random() * 4), phase: 0, dashLength: 3 + Math.floor(Math.random() * 10), gapLength: 2 + Math.floor(Math.random() * 6), density: 0.05 + Math.random() * 0.25, frequency: 1 + Math.random() * 4, sinePhase: Math.random() * Math.PI * 2, seed1: Math.random() * 100, seed2: Math.random() * 100, seed3: Math.random() * 100, counter: Math.floor(Math.random() * 65536), caRule: Math.random() > 0.5 ? 30 : 110 }; } hexToRGB(hex) { const r = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return r ? { r: parseInt(r[1], 16), g: parseInt(r[2], 16), b: parseInt(r[3], 16) } : { r: 255, g: 255, b: 255 }; } lerpColor(t) { return { r: Math.round(this.bgRGB.r + (this.fgRGB.r - this.bgRGB.r) * t), g: Math.round(this.bgRGB.g + (this.fgRGB.g - this.bgRGB.g) * t), b: Math.round(this.bgRGB.b + (this.fgRGB.b - this.bgRGB.b) * t) }; } getSourceValue(pos, length) { const t = pos / length; const cfg = this.config; switch(cfg.pattern) { case 'noise': return Math.random() > 0.5 ? this.fgRGB : this.bgRGB; case 'linear-gradient': return this.lerpColor(t); case 'stepped-gradient': const steps = this.sourceParams.steps || 5; return this.lerpColor(Math.floor(t * steps) / (steps - 1)); case 'random-gradient': if (!this.gradientCache || this.gradientCache.length !== length) { this.generateRandomGradient(length); } return this.lerpColor(this.gradientCache[pos]); case 'stripes': return (Math.floor(pos / (cfg.cellSize || 10)) % 2 === 0) ? this.fgRGB : this.bgRGB; case 'checkerboard': return ((Math.floor(pos / (cfg.cellSize || 10)) + (this.sourceParams.phase || 0)) % 2 === 0) ? this.fgRGB : this.bgRGB; case 'random-blocks': const blockIndex = Math.floor(pos / (cfg.cellSize || 10)); const blockRandom = Math.sin(blockIndex * 12.9898 + this.sourceParams.seed1) * 43758.5453; return (blockRandom - Math.floor(blockRandom)) > 0.5 ? this.fgRGB : this.bgRGB; case 'dashes': const cycle = (this.sourceParams.dashLength || 6) + (this.sourceParams.gapLength || 3); return (pos % cycle < (this.sourceParams.dashLength || 6)) ? this.fgRGB : this.bgRGB; case 'dots': return Math.random() < (this.sourceParams.density || 0.15) ? this.fgRGB : this.bgRGB; case 'sine': const sv = (Math.sin((t * (this.sourceParams.frequency || 2) * Math.PI * 2) + (this.sourceParams.sinePhase || 0)) + 1) / 2; return this.lerpColor(sv); case 'perlin': const p1 = Math.sin(t * 3.7 + this.sourceParams.seed1) * 0.5; const p2 = Math.sin(t * 7.3 + this.sourceParams.seed2) * 0.25; const p3 = Math.sin(t * 13.1 + this.sourceParams.seed3) * 0.125; return this.lerpColor(Math.max(0, Math.min(1, (p1 + p2 + p3 + 0.875) / 1.75))); case 'binary-counter': return ((this.sourceParams.counter >> (pos % 16)) & 1) ? this.fgRGB : this.bgRGB; case 'cellular-automata': if (!this.caState || this.caState.length !== length) { this.initCellularAutomata(length); } return this.caState[pos] ? this.fgRGB : this.bgRGB; default: return Math.random() > 0.5 ? this.fgRGB : this.bgRGB; } } generateRandomGradient(length) { const np = 3 + Math.floor(Math.random() * 4); const cp = [0]; for (let i = 1; i < np - 1; i++) cp.push(Math.random()); cp.push(1); cp.sort((a, b) => a - b); const cv = cp.map(() => Math.random()); this.gradientCache = []; for (let i = 0; i < length; i++) { const t = i / length; let si = 0; for (let j = 0; j < cp.length - 1; j++) { if (t >= cp[j] && t <= cp[j + 1]) { si = j; break; } } const st = (t - cp[si]) / (cp[si + 1] - cp[si]); this.gradientCache.push(cv[si] + (cv[si + 1] - cv[si]) * st); } } initCellularAutomata(length) { this.caState = []; for (let i = 0; i < length; i++) { this.caState.push(Math.random() > 0.5); } } evolveCellularAutomata() { if (!this.caState) return; const rule = this.sourceParams.caRule || 30; const os = [...this.caState]; const len = this.caState.length; for (let i = 0; i < len; i++) { const p = (os[(i - 1 + len) % len] ? 4 : 0) + (os[i] ? 2 : 0) + (os[(i + 1) % len] ? 1 : 0); this.caState[i] = ((rule >> p) & 1) === 1; } } updateSourceParams() { const cfg = this.config; switch(cfg.pattern) { case 'checkerboard': this.sourceParams.phase = (this.sourceParams.phase + 1) % 2; break; case 'sine': this.sourceParams.sinePhase += 0.5; break; case 'binary-counter': this.sourceParams.counter++; break; case 'cellular-automata': this.evolveCellularAutomata(); break; case 'random-gradient': this.gradientCache = null; break; case 'perlin': this.sourceParams.seed1 += 0.3; this.sourceParams.seed2 += 0.2; this.sourceParams.seed3 += 0.1; break; } } generateSourceEdge() { this.updateSourceParams(); const data = this.imageData.data; const w = this.canvas.width; const h = this.canvas.height; const length = w; const cfg = this.config; for (let i = 0; i < length; i++) { const x = i; const y = 0; // top edge let color; // 2D patterns need both x and y coordinates if (cfg.pattern === 'checkerboard') { const cellX = Math.floor(x / (cfg.cellSize || 10)); const cellY = Math.floor(y / (cfg.cellSize || 10)); color = ((cellX + cellY) % 2 === 0) ? this.fgRGB : this.bgRGB; } else if (cfg.pattern === 'random-blocks') { const cellX = Math.floor(x / (cfg.cellSize || 10)); const cellY = Math.floor(y / (cfg.cellSize || 10)); const seed = cellX * 73856093 ^ cellY * 19349663; const blockRandom = Math.sin(seed + this.sourceParams.seed1) * 43758.5453; color = (blockRandom - Math.floor(blockRandom)) > 0.5 ? this.fgRGB : this.bgRGB; } else { // 1D patterns color = this.getSourceValue(i, length); } const idx = i * 4; data[idx] = color.r; data[idx + 1] = color.g; data[idx + 2] = color.b; data[idx + 3] = Math.round(this.opacity * 255); } } changeDirection() { const dirs = [ { dx: 0, dy: 1, name: 'down' }, { dx: 1, dy: 1, name: 'down-right' }, { dx: 1, dy: 0, name: 'right' }, { dx: 1, dy: -1, name: 'up-right' }, { dx: 0, dy: -1, name: 'up' }, { dx: -1, dy: -1, name: 'up-left' }, { dx: -1, dy: 0, name: 'left' }, { dx: -1, dy: 1, name: 'down-left' } ]; const currentIndex = dirs.findIndex(d => d.dx === this.direction.dx && d.dy === this.direction.dy); if (currentIndex === -1) return; const newIndex = Math.random() > 0.5 ? (currentIndex + 1) % 8 : (currentIndex + 7) % 8; this.direction = dirs[newIndex]; } initPattern() { const w = this.canvas.width; const h = this.canvas.height; const length = w; const cfg = this.config; this.imageData = this.ctx.createImageData(w, h); for (let y = 0; y < h; y++) { for (let x = 0; x < w; x++) { let color; // 2D patterns need both x and y if (cfg.pattern === 'checkerboard') { const cellX = Math.floor(x / (cfg.cellSize || 10)); const cellY = Math.floor(y / (cfg.cellSize || 10)); color = ((cellX + cellY) % 2 === 0) ? this.fgRGB : this.bgRGB; } else if (cfg.pattern === 'random-blocks') { const cellX = Math.floor(x / (cfg.cellSize || 10)); const cellY = Math.floor(y / (cfg.cellSize || 10)); const seed = cellX * 73856093 ^ cellY * 19349663; const blockRandom = Math.sin(seed + this.sourceParams.seed1) * 43758.5453; color = (blockRandom - Math.floor(blockRandom)) > 0.5 ? this.fgRGB : this.bgRGB; } else { // 1D patterns color = this.getSourceValue(x, length); } const idx = (y * w + x) * 4; this.imageData.data[idx] = color.r; this.imageData.data[idx + 1] = color.g; this.imageData.data[idx + 2] = color.b; this.imageData.data[idx + 3] = Math.round(this.opacity * 255); } } this.ctx.putImageData(this.imageData, 0, 0); } smear() { if (!this.imageData) return; const cfg = this.config; const data = this.imageData.data; const w = this.canvas.width; const h = this.canvas.height; const newData = this.ctx.createImageData(w, h); const newPixels = newData.data; const mode = cfg.smearMode || 'replace'; const intensity = cfg.trailIntensity || 0.85; for (let y = 0; y < h; y++) { for (let x = 0; x < w; x++) { let srcX = x - this.direction.dx * cfg.smearAmount; let srcY = y - this.direction.dy * cfg.smearAmount; srcX = (srcX + w) % w; srcY = (srcY + h) % h; const srcIdx = (srcY * w + srcX) * 4; const dstIdx = (y * w + x) * 4; if (mode === 'trail') { newPixels[dstIdx] = data[srcIdx] * intensity + data[dstIdx] * (1 - intensity); newPixels[dstIdx + 1] = data[srcIdx + 1] * intensity + data[dstIdx + 1] * (1 - intensity); newPixels[dstIdx + 2] = data[srcIdx + 2] * intensity + data[dstIdx + 2] * (1 - intensity); newPixels[dstIdx + 3] = Math.round(this.opacity * 255); } else { newPixels[dstIdx] = data[srcIdx]; newPixels[dstIdx + 1] = data[srcIdx + 1]; newPixels[dstIdx + 2] = data[srcIdx + 2]; newPixels[dstIdx + 3] = Math.round(this.opacity * 255); } } } this.imageData = newData; this.ctx.putImageData(this.imageData, 0, 0); } start() { const animate = (timestamp) => { if (!this.lastDirectionChange) this.lastDirectionChange = timestamp; if (!this.lastSourceTime) this.lastSourceTime = timestamp; if (this.enableDirectionChange && timestamp - this.lastDirectionChange >= this.directionChangeInterval) { this.changeDirection(); this.lastDirectionChange = timestamp; this.directionChangeInterval = 1000 + Math.random() * 4000; } if (timestamp - this.lastSourceTime >= (this.config.sourceInterval || 1000)) { this.generateSourceEdge(); this.lastSourceTime = timestamp; } this.smear(); setTimeout(() => requestAnimationFrame(animate), 1000 / this.config.speed); }; requestAnimationFrame(animate); } } partitions.forEach(config => new SmearPartition(config)); </script> </body> </html>
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <title>Smear Art</title> <style>*{margin:0;padding:0;box-sizing:border-box}body,html{width:100%;height:100%;overflow:hidden;background:#000}#container{position:relative;width:600px;height:600px;margin:0 auto;background:#000}.partition{position:absolute;overflow:hidden}.partition canvas{width:100%;height:100%;display:block;image-rendering:pixelated;image-rendering:crisp-edges;position:relative;z-index:10}</style> </head> <body> <div id="container"></div> <script> const container = document.getElementById('container'); const partitions = [{"x":-2,"y":0,"w":308,"h":600,"pattern":"random-blocks","color1":"#ceff00","color2":"#000000","cellSize":10,"speed":5,"smearAmount":2,"direction":{"dx":-1,"dy":0,"name":"left"},"enableDirectionChange":true,"directionChangeInterval":3861.3076612313293,"sourceInterval":1000,"smearMode":"replace","trailIntensity":0.85,"opacity":1},{"x":303,"y":3,"w":311,"h":605,"pattern":"cellular-automata","color1":"#f865b0","color2":"#000000","cellSize":10,"speed":5,"smearAmount":2,"direction":{"dx":1,"dy":0,"name":"right"},"enableDirectionChange":false,"directionChangeInterval":3000,"sourceInterval":1000,"smearMode":"trail","trailIntensity":0.98,"opacity":1},{"x":163,"y":-2,"w":222,"h":611,"pattern":"perlin","color1":"#f4faff","color2":"#000000","cellSize":10,"speed":5,"smearAmount":2,"direction":{"dx":-1,"dy":1,"name":"down-left"},"enableDirectionChange":true,"directionChangeInterval":2805.7492425761707,"sourceInterval":1000,"smearMode":"replace","trailIntensity":0.85,"opacity":0.81}]; class SmearPartition { constructor(config) { this.config = config; this.div = document.createElement('div'); this.div.className = 'partition'; this.div.style.left = config.x + 'px'; this.div.style.top = config.y + 'px'; this.div.style.width = config.w + 'px'; this.div.style.height = config.h + 'px'; this.canvas = document.createElement('canvas'); this.canvas.width = Math.floor(config.w); this.canvas.height = Math.floor(config.h); this.ctx = this.canvas.getContext('2d'); this.ctx.imageSmoothingEnabled = false; this.div.appendChild(this.canvas); container.appendChild(this.div); this.direction = config.direction; this.enableDirectionChange = config.enableDirectionChange || false; this.directionChangeInterval = config.directionChangeInterval || 3000; this.lastDirectionChange = 0; this.lastSourceTime = 0; this.imageData = null; this.opacity = config.opacity !== undefined ? config.opacity : 1.0; this.fgRGB = this.hexToRGB(config.color1); this.bgRGB = this.hexToRGB(config.color2 || '#000000'); this.sourceParams = this.generateSourceParams(); this.gradientCache = null; this.caState = null; this.initPattern(); this.start(); } generateSourceParams() { return { steps: 3 + Math.floor(Math.random() * 6), stripeWidth: 2 + Math.floor(Math.random() * 8), checkSize: 1 + Math.floor(Math.random() * 4), phase: 0, dashLength: 3 + Math.floor(Math.random() * 10), gapLength: 2 + Math.floor(Math.random() * 6), density: 0.05 + Math.random() * 0.25, frequency: 1 + Math.random() * 4, sinePhase: Math.random() * Math.PI * 2, seed1: Math.random() * 100, seed2: Math.random() * 100, seed3: Math.random() * 100, counter: Math.floor(Math.random() * 65536), caRule: Math.random() > 0.5 ? 30 : 110 }; } hexToRGB(hex) { const r = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return r ? { r: parseInt(r[1], 16), g: parseInt(r[2], 16), b: parseInt(r[3], 16) } : { r: 255, g: 255, b: 255 }; } lerpColor(t) { return { r: Math.round(this.bgRGB.r + (this.fgRGB.r - this.bgRGB.r) * t), g: Math.round(this.bgRGB.g + (this.fgRGB.g - this.bgRGB.g) * t), b: Math.round(this.bgRGB.b + (this.fgRGB.b - this.bgRGB.b) * t) }; } getSourceValue(pos, length) { const t = pos / length; const cfg = this.config; switch(cfg.pattern) { case 'noise': return Math.random() > 0.5 ? this.fgRGB : this.bgRGB; case 'linear-gradient': return this.lerpColor(t); case 'stepped-gradient': const steps = this.sourceParams.steps || 5; return this.lerpColor(Math.floor(t * steps) / (steps - 1)); case 'random-gradient': if (!this.gradientCache || this.gradientCache.length !== length) { this.generateRandomGradient(length); } return this.lerpColor(this.gradientCache[pos]); case 'stripes': return (Math.floor(pos / (cfg.cellSize || 10)) % 2 === 0) ? this.fgRGB : this.bgRGB; case 'checkerboard': return ((Math.floor(pos / (cfg.cellSize || 10)) + (this.sourceParams.phase || 0)) % 2 === 0) ? this.fgRGB : this.bgRGB; case 'random-blocks': const blockIndex = Math.floor(pos / (cfg.cellSize || 10)); const blockRandom = Math.sin(blockIndex * 12.9898 + this.sourceParams.seed1) * 43758.5453; return (blockRandom - Math.floor(blockRandom)) > 0.5 ? this.fgRGB : this.bgRGB; case 'dashes': const cycle = (this.sourceParams.dashLength || 6) + (this.sourceParams.gapLength || 3); return (pos % cycle < (this.sourceParams.dashLength || 6)) ? this.fgRGB : this.bgRGB; case 'dots': return Math.random() < (this.sourceParams.density || 0.15) ? this.fgRGB : this.bgRGB; case 'sine': const sv = (Math.sin((t * (this.sourceParams.frequency || 2) * Math.PI * 2) + (this.sourceParams.sinePhase || 0)) + 1) / 2; return this.lerpColor(sv); case 'perlin': const p1 = Math.sin(t * 3.7 + this.sourceParams.seed1) * 0.5; const p2 = Math.sin(t * 7.3 + this.sourceParams.seed2) * 0.25; const p3 = Math.sin(t * 13.1 + this.sourceParams.seed3) * 0.125; return this.lerpColor(Math.max(0, Math.min(1, (p1 + p2 + p3 + 0.875) / 1.75))); case 'binary-counter': return ((this.sourceParams.counter >> (pos % 16)) & 1) ? this.fgRGB : this.bgRGB; case 'cellular-automata': if (!this.caState || this.caState.length !== length) { this.initCellularAutomata(length); } return this.caState[pos] ? this.fgRGB : this.bgRGB; default: return Math.random() > 0.5 ? this.fgRGB : this.bgRGB; } } generateRandomGradient(length) { const np = 3 + Math.floor(Math.random() * 4); const cp = [0]; for (let i = 1; i < np - 1; i++) cp.push(Math.random()); cp.push(1); cp.sort((a, b) => a - b); const cv = cp.map(() => Math.random()); this.gradientCache = []; for (let i = 0; i < length; i++) { const t = i / length; let si = 0; for (let j = 0; j < cp.length - 1; j++) { if (t >= cp[j] && t <= cp[j + 1]) { si = j; break; } } const st = (t - cp[si]) / (cp[si + 1] - cp[si]); this.gradientCache.push(cv[si] + (cv[si + 1] - cv[si]) * st); } } initCellularAutomata(length) { this.caState = []; for (let i = 0; i < length; i++) { this.caState.push(Math.random() > 0.5); } } evolveCellularAutomata() { if (!this.caState) return; const rule = this.sourceParams.caRule || 30; const os = [...this.caState]; const len = this.caState.length; for (let i = 0; i < len; i++) { const p = (os[(i - 1 + len) % len] ? 4 : 0) + (os[i] ? 2 : 0) + (os[(i + 1) % len] ? 1 : 0); this.caState[i] = ((rule >> p) & 1) === 1; } } updateSourceParams() { const cfg = this.config; switch(cfg.pattern) { case 'checkerboard': this.sourceParams.phase = (this.sourceParams.phase + 1) % 2; break; case 'sine': this.sourceParams.sinePhase += 0.5; break; case 'binary-counter': this.sourceParams.counter++; break; case 'cellular-automata': this.evolveCellularAutomata(); break; case 'random-gradient': this.gradientCache = null; break; case 'perlin': this.sourceParams.seed1 += 0.3; this.sourceParams.seed2 += 0.2; this.sourceParams.seed3 += 0.1; break; } } generateSourceEdge() { this.updateSourceParams(); const data = this.imageData.data; const w = this.canvas.width; const h = this.canvas.height; const length = w; const cfg = this.config; for (let i = 0; i < length; i++) { const x = i; const y = 0; // top edge let color; // 2D patterns need both x and y coordinates if (cfg.pattern === 'checkerboard') { const cellX = Math.floor(x / (cfg.cellSize || 10)); const cellY = Math.floor(y / (cfg.cellSize || 10)); color = ((cellX + cellY) % 2 === 0) ? this.fgRGB : this.bgRGB; } else if (cfg.pattern === 'random-blocks') { const cellX = Math.floor(x / (cfg.cellSize || 10)); const cellY = Math.floor(y / (cfg.cellSize || 10)); const seed = cellX * 73856093 ^ cellY * 19349663; const blockRandom = Math.sin(seed + this.sourceParams.seed1) * 43758.5453; color = (blockRandom - Math.floor(blockRandom)) > 0.5 ? this.fgRGB : this.bgRGB; } else { // 1D patterns color = this.getSourceValue(i, length); } const idx = i * 4; data[idx] = color.r; data[idx + 1] = color.g; data[idx + 2] = color.b; data[idx + 3] = Math.round(this.opacity * 255); } } changeDirection() { const dirs = [ { dx: 0, dy: 1, name: 'down' }, { dx: 1, dy: 1, name: 'down-right' }, { dx: 1, dy: 0, name: 'right' }, { dx: 1, dy: -1, name: 'up-right' }, { dx: 0, dy: -1, name: 'up' }, { dx: -1, dy: -1, name: 'up-left' }, { dx: -1, dy: 0, name: 'left' }, { dx: -1, dy: 1, name: 'down-left' } ]; const currentIndex = dirs.findIndex(d => d.dx === this.direction.dx && d.dy === this.direction.dy); if (currentIndex === -1) return; const newIndex = Math.random() > 0.5 ? (currentIndex + 1) % 8 : (currentIndex + 7) % 8; this.direction = dirs[newIndex]; } initPattern() { const w = this.canvas.width; const h = this.canvas.height; const length = w; const cfg = this.config; this.imageData = this.ctx.createImageData(w, h); for (let y = 0; y < h; y++) { for (let x = 0; x < w; x++) { let color; // 2D patterns need both x and y if (cfg.pattern === 'checkerboard') { const cellX = Math.floor(x / (cfg.cellSize || 10)); const cellY = Math.floor(y / (cfg.cellSize || 10)); color = ((cellX + cellY) % 2 === 0) ? this.fgRGB : this.bgRGB; } else if (cfg.pattern === 'random-blocks') { const cellX = Math.floor(x / (cfg.cellSize || 10)); const cellY = Math.floor(y / (cfg.cellSize || 10)); const seed = cellX * 73856093 ^ cellY * 19349663; const blockRandom = Math.sin(seed + this.sourceParams.seed1) * 43758.5453; color = (blockRandom - Math.floor(blockRandom)) > 0.5 ? this.fgRGB : this.bgRGB; } else { // 1D patterns color = this.getSourceValue(x, length); } const idx = (y * w + x) * 4; this.imageData.data[idx] = color.r; this.imageData.data[idx + 1] = color.g; this.imageData.data[idx + 2] = color.b; this.imageData.data[idx + 3] = Math.round(this.opacity * 255); } } this.ctx.putImageData(this.imageData, 0, 0); } smear() { if (!this.imageData) return; const cfg = this.config; const data = this.imageData.data; const w = this.canvas.width; const h = this.canvas.height; const newData = this.ctx.createImageData(w, h); const newPixels = newData.data; const mode = cfg.smearMode || 'replace'; const intensity = cfg.trailIntensity || 0.85; for (let y = 0; y < h; y++) { for (let x = 0; x < w; x++) { let srcX = x - this.direction.dx * cfg.smearAmount; let srcY = y - this.direction.dy * cfg.smearAmount; srcX = (srcX + w) % w; srcY = (srcY + h) % h; const srcIdx = (srcY * w + srcX) * 4; const dstIdx = (y * w + x) * 4; if (mode === 'trail') { newPixels[dstIdx] = data[srcIdx] * intensity + data[dstIdx] * (1 - intensity); newPixels[dstIdx + 1] = data[srcIdx + 1] * intensity + data[dstIdx + 1] * (1 - intensity); newPixels[dstIdx + 2] = data[srcIdx + 2] * intensity + data[dstIdx + 2] * (1 - intensity); newPixels[dstIdx + 3] = Math.round(this.opacity * 255); } else { newPixels[dstIdx] = data[srcIdx]; newPixels[dstIdx + 1] = data[srcIdx + 1]; newPixels[dstIdx + 2] = data[srcIdx + 2]; newPixels[dstIdx + 3] = Math.round(this.opacity * 255); } } } this.imageData = newData; this.ctx.putImageData(this.imageData, 0, 0); } start() { const animate = (timestamp) => { if (!this.lastDirectionChange) this.lastDirectionChange = timestamp; if (!this.lastSourceTime) this.lastSourceTime = timestamp; if (this.enableDirectionChange && timestamp - this.lastDirectionChange >= this.directionChangeInterval) { this.changeDirection(); this.lastDirectionChange = timestamp; this.directionChangeInterval = 1000 + Math.random() * 4000; } if (timestamp - this.lastSourceTime >= (this.config.sourceInterval || 1000)) { this.generateSourceEdge(); this.lastSourceTime = timestamp; } this.smear(); setTimeout(() => requestAnimationFrame(animate), 1000 / this.config.speed); }; requestAnimationFrame(animate); } } partitions.forEach(config => new SmearPartition(config)); </script> </body> </html>
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <title>Smear Art</title> <style>*{margin:0;padding:0;box-sizing:border-box}body,html{width:100%;height:100%;overflow:hidden;background:#000}#container{position:relative;width:600px;height:600px;margin:0 auto;background:#000}.partition{position:absolute;overflow:hidden}.partition canvas{width:100%;height:100%;display:block;image-rendering:pixelated;image-rendering:crisp-edges;position:relative;z-index:10}</style> </head> <body> <div id="container"></div> <script> const container = document.getElementById('container'); const partitions = [{"x":-2,"y":0,"w":308,"h":600,"pattern":"random-blocks","color1":"#ceff00","color2":"#000000","cellSize":10,"speed":5,"smearAmount":2,"direction":{"dx":-1,"dy":0,"name":"left"},"enableDirectionChange":true,"directionChangeInterval":3861.3076612313293,"sourceInterval":1000,"smearMode":"replace","trailIntensity":0.85,"opacity":1},{"x":303,"y":3,"w":311,"h":605,"pattern":"cellular-automata","color1":"#f865b0","color2":"#000000","cellSize":10,"speed":5,"smearAmount":2,"direction":{"dx":1,"dy":0,"name":"right"},"enableDirectionChange":false,"directionChangeInterval":3000,"sourceInterval":1000,"smearMode":"trail","trailIntensity":0.98,"opacity":1},{"x":163,"y":-2,"w":222,"h":611,"pattern":"perlin","color1":"#f4faff","color2":"#000000","cellSize":10,"speed":5,"smearAmount":2,"direction":{"dx":-1,"dy":1,"name":"down-left"},"enableDirectionChange":true,"directionChangeInterval":2805.7492425761707,"sourceInterval":1000,"smearMode":"replace","trailIntensity":0.85,"opacity":0.81}]; class SmearPartition { constructor(config) { this.config = config; this.div = document.createElement('div'); this.div.className = 'partition'; this.div.style.left = config.x + 'px'; this.div.style.top = config.y + 'px'; this.div.style.width = config.w + 'px'; this.div.style.height = config.h + 'px'; this.canvas = document.createElement('canvas'); this.canvas.width = Math.floor(config.w); this.canvas.height = Math.floor(config.h); this.ctx = this.canvas.getContext('2d'); this.ctx.imageSmoothingEnabled = false; this.div.appendChild(this.canvas); container.appendChild(this.div); this.direction = config.direction; this.enableDirectionChange = config.enableDirectionChange || false; this.directionChangeInterval = config.directionChangeInterval || 3000; this.lastDirectionChange = 0; this.lastSourceTime = 0; this.imageData = null; this.opacity = config.opacity !== undefined ? config.opacity : 1.0; this.fgRGB = this.hexToRGB(config.color1); this.bgRGB = this.hexToRGB(config.color2 || '#000000'); this.sourceParams = this.generateSourceParams(); this.gradientCache = null; this.caState = null; this.initPattern(); this.start(); } generateSourceParams() { return { steps: 3 + Math.floor(Math.random() * 6), stripeWidth: 2 + Math.floor(Math.random() * 8), checkSize: 1 + Math.floor(Math.random() * 4), phase: 0, dashLength: 3 + Math.floor(Math.random() * 10), gapLength: 2 + Math.floor(Math.random() * 6), density: 0.05 + Math.random() * 0.25, frequency: 1 + Math.random() * 4, sinePhase: Math.random() * Math.PI * 2, seed1: Math.random() * 100, seed2: Math.random() * 100, seed3: Math.random() * 100, counter: Math.floor(Math.random() * 65536), caRule: Math.random() > 0.5 ? 30 : 110 }; } hexToRGB(hex) { const r = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return r ? { r: parseInt(r[1], 16), g: parseInt(r[2], 16), b: parseInt(r[3], 16) } : { r: 255, g: 255, b: 255 }; } lerpColor(t) { return { r: Math.round(this.bgRGB.r + (this.fgRGB.r - this.bgRGB.r) * t), g: Math.round(this.bgRGB.g + (this.fgRGB.g - this.bgRGB.g) * t), b: Math.round(this.bgRGB.b + (this.fgRGB.b - this.bgRGB.b) * t) }; } getSourceValue(pos, length) { const t = pos / length; const cfg = this.config; switch(cfg.pattern) { case 'noise': return Math.random() > 0.5 ? this.fgRGB : this.bgRGB; case 'linear-gradient': return this.lerpColor(t); case 'stepped-gradient': const steps = this.sourceParams.steps || 5; return this.lerpColor(Math.floor(t * steps) / (steps - 1)); case 'random-gradient': if (!this.gradientCache || this.gradientCache.length !== length) { this.generateRandomGradient(length); } return this.lerpColor(this.gradientCache[pos]); case 'stripes': return (Math.floor(pos / (cfg.cellSize || 10)) % 2 === 0) ? this.fgRGB : this.bgRGB; case 'checkerboard': return ((Math.floor(pos / (cfg.cellSize || 10)) + (this.sourceParams.phase || 0)) % 2 === 0) ? this.fgRGB : this.bgRGB; case 'random-blocks': const blockIndex = Math.floor(pos / (cfg.cellSize || 10)); const blockRandom = Math.sin(blockIndex * 12.9898 + this.sourceParams.seed1) * 43758.5453; return (blockRandom - Math.floor(blockRandom)) > 0.5 ? this.fgRGB : this.bgRGB; case 'dashes': const cycle = (this.sourceParams.dashLength || 6) + (this.sourceParams.gapLength || 3); return (pos % cycle < (this.sourceParams.dashLength || 6)) ? this.fgRGB : this.bgRGB; case 'dots': return Math.random() < (this.sourceParams.density || 0.15) ? this.fgRGB : this.bgRGB; case 'sine': const sv = (Math.sin((t * (this.sourceParams.frequency || 2) * Math.PI * 2) + (this.sourceParams.sinePhase || 0)) + 1) / 2; return this.lerpColor(sv); case 'perlin': const p1 = Math.sin(t * 3.7 + this.sourceParams.seed1) * 0.5; const p2 = Math.sin(t * 7.3 + this.sourceParams.seed2) * 0.25; const p3 = Math.sin(t * 13.1 + this.sourceParams.seed3) * 0.125; return this.lerpColor(Math.max(0, Math.min(1, (p1 + p2 + p3 + 0.875) / 1.75))); case 'binary-counter': return ((this.sourceParams.counter >> (pos % 16)) & 1) ? this.fgRGB : this.bgRGB; case 'cellular-automata': if (!this.caState || this.caState.length !== length) { this.initCellularAutomata(length); } return this.caState[pos] ? this.fgRGB : this.bgRGB; default: return Math.random() > 0.5 ? this.fgRGB : this.bgRGB; } } generateRandomGradient(length) { const np = 3 + Math.floor(Math.random() * 4); const cp = [0]; for (let i = 1; i < np - 1; i++) cp.push(Math.random()); cp.push(1); cp.sort((a, b) => a - b); const cv = cp.map(() => Math.random()); this.gradientCache = []; for (let i = 0; i < length; i++) { const t = i / length; let si = 0; for (let j = 0; j < cp.length - 1; j++) { if (t >= cp[j] && t <= cp[j + 1]) { si = j; break; } } const st = (t - cp[si]) / (cp[si + 1] - cp[si]); this.gradientCache.push(cv[si] + (cv[si + 1] - cv[si]) * st); } } initCellularAutomata(length) { this.caState = []; for (let i = 0; i < length; i++) { this.caState.push(Math.random() > 0.5); } } evolveCellularAutomata() { if (!this.caState) return; const rule = this.sourceParams.caRule || 30; const os = [...this.caState]; const len = this.caState.length; for (let i = 0; i < len; i++) { const p = (os[(i - 1 + len) % len] ? 4 : 0) + (os[i] ? 2 : 0) + (os[(i + 1) % len] ? 1 : 0); this.caState[i] = ((rule >> p) & 1) === 1; } } updateSourceParams() { const cfg = this.config; switch(cfg.pattern) { case 'checkerboard': this.sourceParams.phase = (this.sourceParams.phase + 1) % 2; break; case 'sine': this.sourceParams.sinePhase += 0.5; break; case 'binary-counter': this.sourceParams.counter++; break; case 'cellular-automata': this.evolveCellularAutomata(); break; case 'random-gradient': this.gradientCache = null; break; case 'perlin': this.sourceParams.seed1 += 0.3; this.sourceParams.seed2 += 0.2; this.sourceParams.seed3 += 0.1; break; } } generateSourceEdge() { this.updateSourceParams(); const data = this.imageData.data; const w = this.canvas.width; const h = this.canvas.height; const length = w; const cfg = this.config; for (let i = 0; i < length; i++) { const x = i; const y = 0; // top edge let color; // 2D patterns need both x and y coordinates if (cfg.pattern === 'checkerboard') { const cellX = Math.floor(x / (cfg.cellSize || 10)); const cellY = Math.floor(y / (cfg.cellSize || 10)); color = ((cellX + cellY) % 2 === 0) ? this.fgRGB : this.bgRGB; } else if (cfg.pattern === 'random-blocks') { const cellX = Math.floor(x / (cfg.cellSize || 10)); const cellY = Math.floor(y / (cfg.cellSize || 10)); const seed = cellX * 73856093 ^ cellY * 19349663; const blockRandom = Math.sin(seed + this.sourceParams.seed1) * 43758.5453; color = (blockRandom - Math.floor(blockRandom)) > 0.5 ? this.fgRGB : this.bgRGB; } else { // 1D patterns color = this.getSourceValue(i, length); } const idx = i * 4; data[idx] = color.r; data[idx + 1] = color.g; data[idx + 2] = color.b; data[idx + 3] = Math.round(this.opacity * 255); } } changeDirection() { const dirs = [ { dx: 0, dy: 1, name: 'down' }, { dx: 1, dy: 1, name: 'down-right' }, { dx: 1, dy: 0, name: 'right' }, { dx: 1, dy: -1, name: 'up-right' }, { dx: 0, dy: -1, name: 'up' }, { dx: -1, dy: -1, name: 'up-left' }, { dx: -1, dy: 0, name: 'left' }, { dx: -1, dy: 1, name: 'down-left' } ]; const currentIndex = dirs.findIndex(d => d.dx === this.direction.dx && d.dy === this.direction.dy); if (currentIndex === -1) return; const newIndex = Math.random() > 0.5 ? (currentIndex + 1) % 8 : (currentIndex + 7) % 8; this.direction = dirs[newIndex]; } initPattern() { const w = this.canvas.width; const h = this.canvas.height; const length = w; const cfg = this.config; this.imageData = this.ctx.createImageData(w, h); for (let y = 0; y < h; y++) { for (let x = 0; x < w; x++) { let color; // 2D patterns need both x and y if (cfg.pattern === 'checkerboard') { const cellX = Math.floor(x / (cfg.cellSize || 10)); const cellY = Math.floor(y / (cfg.cellSize || 10)); color = ((cellX + cellY) % 2 === 0) ? this.fgRGB : this.bgRGB; } else if (cfg.pattern === 'random-blocks') { const cellX = Math.floor(x / (cfg.cellSize || 10)); const cellY = Math.floor(y / (cfg.cellSize || 10)); const seed = cellX * 73856093 ^ cellY * 19349663; const blockRandom = Math.sin(seed + this.sourceParams.seed1) * 43758.5453; color = (blockRandom - Math.floor(blockRandom)) > 0.5 ? this.fgRGB : this.bgRGB; } else { // 1D patterns color = this.getSourceValue(x, length); } const idx = (y * w + x) * 4; this.imageData.data[idx] = color.r; this.imageData.data[idx + 1] = color.g; this.imageData.data[idx + 2] = color.b; this.imageData.data[idx + 3] = Math.round(this.opacity * 255); } } this.ctx.putImageData(this.imageData, 0, 0); } smear() { if (!this.imageData) return; const cfg = this.config; const data = this.imageData.data; const w = this.canvas.width; const h = this.canvas.height; const newData = this.ctx.createImageData(w, h); const newPixels = newData.data; const mode = cfg.smearMode || 'replace'; const intensity = cfg.trailIntensity || 0.85; for (let y = 0; y < h; y++) { for (let x = 0; x < w; x++) { let srcX = x - this.direction.dx * cfg.smearAmount; let srcY = y - this.direction.dy * cfg.smearAmount; srcX = (srcX + w) % w; srcY = (srcY + h) % h; const srcIdx = (srcY * w + srcX) * 4; const dstIdx = (y * w + x) * 4; if (mode === 'trail') { newPixels[dstIdx] = data[srcIdx] * intensity + data[dstIdx] * (1 - intensity); newPixels[dstIdx + 1] = data[srcIdx + 1] * intensity + data[dstIdx + 1] * (1 - intensity); newPixels[dstIdx + 2] = data[srcIdx + 2] * intensity + data[dstIdx + 2] * (1 - intensity); newPixels[dstIdx + 3] = Math.round(this.opacity * 255); } else { newPixels[dstIdx] = data[srcIdx]; newPixels[dstIdx + 1] = data[srcIdx + 1]; newPixels[dstIdx + 2] = data[srcIdx + 2]; newPixels[dstIdx + 3] = Math.round(this.opacity * 255); } } } this.imageData = newData; this.ctx.putImageData(this.imageData, 0, 0); } start() { const animate = (timestamp) => { if (!this.lastDirectionChange) this.lastDirectionChange = timestamp; if (!this.lastSourceTime) this.lastSourceTime = timestamp; if (this.enableDirectionChange && timestamp - this.lastDirectionChange >= this.directionChangeInterval) { this.changeDirection(); this.lastDirectionChange = timestamp; this.directionChangeInterval = 1000 + Math.random() * 4000; } if (timestamp - this.lastSourceTime >= (this.config.sourceInterval || 1000)) { this.generateSourceEdge(); this.lastSourceTime = timestamp; } this.smear(); setTimeout(() => requestAnimationFrame(animate), 1000 / this.config.speed); }; requestAnimationFrame(animate); } } partitions.forEach(config => new SmearPartition(config)); </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Jimmy Grid</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { background: #000; overflow: hidden; width: 100vw; height: 100vh; display: flex; align-items: center; justify-content: center; } #grid-container { width: 100vmin; height: 100vmin; position: relative; display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; gap: 0; } .resize-handle-vertical { position: absolute; width: 10px; height: 100%; cursor: ew-resize; z-index: 1000; left: 50%; transform: translateX(-50%); } .resize-handle-horizontal { position: absolute; width: 100%; height: 10px; cursor: ns-resize; z-index: 1000; top: 50%; transform: translateY(-50%); } .cell { width: 100%; height: 100%; position: relative; overflow: hidden; background: #000; } iframe { width: 100%; height: 100%; border: none; display: block; pointer-events: auto; } #loading { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; background: #000; z-index: 1000; color: #888; font-family: monospace; font-size: 14px; } #loading.hidden { display: none; } </style> </head> <body> <div id="loading">Loading images...</div> <div id="grid-container"> <div class="resize-handle-vertical"></div> <div class="resize-handle-horizontal"></div> </div> <script> const TX_HASHES = [ "0x75a22114078aad812352cf3a0261b5a20ac93dfde763a87111717ad3e4803006", "0xbd1e892be3ca111e3b8dd1544190ebbaa743135692d0d8a2e2ba0d7728954754", "0x18cf414c1096b04c0374e5eff588e849673d78e2831b6f006a033f2b8f0e3171", "0x75a22114078aad812352cf3a0261b5a20ac93dfde763a87111717ad3e4803006" ]; let images = []; const cells = []; const cellTimers = []; let isDragging = false; let currentHandle = null; function initGrid() { const container = document.getElementById('grid-container'); const handles = container.querySelectorAll('.resize-handle-vertical, .resize-handle-horizontal'); for (let i = 0; i < 4; i++) { const div = document.createElement('div'); div.className = 'cell'; div.id = `cell-${i}`; container.appendChild(div); cells.push(div); } const vHandle = container.querySelector('.resize-handle-vertical'); const hHandle = container.querySelector('.resize-handle-horizontal'); vHandle.addEventListener('mousedown', (e) => startResize(e, 'vertical')); hHandle.addEventListener('mousedown', (e) => startResize(e, 'horizontal')); document.addEventListener('mousemove', resize); document.addEventListener('mouseup', stopResize); } function startResize(e, direction) { isDragging = true; currentHandle = direction; e.preventDefault(); } function resize(e) { if (!isDragging) return; const container = document.getElementById('grid-container'); const rect = container.getBoundingClientRect(); if (currentHandle === 'vertical') { const x = e.clientX - rect.left; const percent = (x / rect.width) * 100; const clamped = Math.max(20, Math.min(80, percent)); container.style.gridTemplateColumns = `${clamped}% ${100 - clamped}%`; } else if (currentHandle === 'horizontal') { const y = e.clientY - rect.top; const percent = (y / rect.height) * 100; const clamped = Math.max(20, Math.min(80, percent)); container.style.gridTemplateRows = `${clamped}% ${100 - clamped}%`; } } function stopResize() { isDragging = false; currentHandle = null; } function renderCell(index) { if (images.length === 0) { console.error('renderCell called but no images loaded'); return; } const cell = cells[index]; const htmlContent = images[index % images.length]; if (!htmlContent) { console.error('No HTML content for index', index); return; } cell.innerHTML = ''; const iframe = document.createElement('iframe'); iframe.srcdoc = htmlContent; iframe.sandbox = 'allow-scripts allow-same-origin'; cell.appendChild(iframe); } function updateCell(index) { if (images.length === 0) return; const cell = cells[index]; if (!cell) { scheduleNextUpdate(index); return; } const randomIndex = Math.floor(Math.random() * images.length); const htmlContent = images[randomIndex]; cell.innerHTML = ''; const iframe = document.createElement('iframe'); iframe.srcdoc = htmlContent; iframe.sandbox = 'allow-scripts allow-same-origin'; cell.appendChild(iframe); scheduleNextUpdate(index); } function scheduleNextUpdate(index) { const delay = 3000 + Math.random() * 2000; cellTimers[index] = setTimeout(() => updateCell(index), delay); } async function decodeImageFromTx(txHash) { try { const response = await fetch(`https://eth-mainnet.g.alchemy.com/v2/T1Y8E3Z8hUkVqyXz-I67Qh_Un2wHsfIZ`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_getTransactionByHash', params: [txHash] }) }); const data = await response.json(); if (!data.result || !data.result.input) throw new Error('No transaction data'); const hex = data.result.input.slice(2); const bytes = new Uint8Array(hex.match(/.{1,2}/g).map(byte => parseInt(byte, 16))); const decoder = new TextDecoder(); return decoder.decode(bytes); } catch (e) { console.error('Failed to decode:', txHash, e); return null; } } async function init() { try { for (let i = 0; i < TX_HASHES.length; i++) { const html = await decodeImageFromTx(TX_HASHES[i]); if (html) { images.push(html); } else { console.error('Failed to load tx:', TX_HASHES[i]); } } if (images.length === 0) { document.getElementById('loading').textContent = 'Error: No content loaded'; return; } } catch (e) { console.error('Init error:', e); document.getElementById('loading').textContent = 'Error loading content'; return; } document.getElementById('loading').classList.add('hidden'); initGrid(); for (let i = 0; i < 4; i++) { renderCell(i); setTimeout(() => scheduleNextUpdate(i), (i + 1) * 1000); } } init(); </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Fever Dream Grid</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { background: #000; overflow: hidden; } #grid { display: grid; grid-template-columns: 2fr 1fr; grid-template-rows: 1fr 1fr; width: 100vw; height: 100vh; gap: 0; } .large { grid-row: 1 / 3; } .cell { width: 100%; height: 100%; overflow: hidden; background: #000; } .cell video { width: 100%; height: 100%; object-fit: cover; display: block; } #loading { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; background: #000; z-index: 1000; } #loading.hidden { display: none; } .progress-bar { width: 300px; height: 4px; background: #222; border-radius: 2px; overflow: hidden; margin-bottom: 10px; } .progress-fill { height: 100%; background: #CEFF00; width: 0%; transition: width 0.3s; } .progress-text { color: #888; font-family: monospace; font-size: 14px; } </style> </head> <body> <div id="loading"> <div class="progress-bar"> <div class="progress-fill" id="progressFill"></div> </div> <div class="progress-text" id="progressText">Loading videos 0/10...</div> </div> <div id="grid"> <div class="cell large"></div> <div class="cell"></div> <div class="cell"></div> </div> <script> let mintedVideos = []; // Generate a random pattern with increased variety function generateRandomPattern() { const videoCount = mintedVideos.length; const pattern = []; // Pick 3 different random videos (no repeats in pattern) const usedIndices = new Set(); while (pattern.length < 3 && usedIndices.size < Math.min(3, videoCount)) { const randomIndex = Math.floor(Math.random() * videoCount); if (!usedIndices.has(randomIndex)) { pattern.push(randomIndex); usedIndices.add(randomIndex); } } // If we have less than 3 videos total, fill remaining slots while (pattern.length < 3) { pattern.push(Math.floor(Math.random() * videoCount)); } return pattern; } async function decodeVideoFromTx(txHash) { try { const response = await fetch(`https://eth-mainnet.g.alchemy.com/v2/T1Y8E3Z8hUkVqyXz-I67Qh_Un2wHsfIZ`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_getTransactionByHash', params: [txHash] }) }); const data = await response.json(); if (!data.result || !data.result.input) throw new Error('No transaction data'); const hex = data.result.input.slice(2); const bytes = new Uint8Array(hex.match(/.{1,2}/g).map(byte => parseInt(byte, 16))); const decoder = new TextDecoder(); return decoder.decode(bytes); } catch (e) { console.error('Failed to decode:', txHash, e); return null; } } async function fetchAllMinted() { try { console.log('Fetching from API...'); const response = await fetch('https://chainfeed.online/api/projects/fever-dream?page=0&limit=450'); const data = await response.json(); console.log('API response:', data); const minted = data.items.filter(item => item.minted && item.mintTxHash); console.log('Minted items:', minted.length); return minted; } catch (e) { console.error('Fetch error:', e); return []; } } function renderGrid(stagger = false) { const grid = document.getElementById('grid'); const cells = grid.querySelectorAll('.cell'); if (mintedVideos.length === 0) return; const pattern = generateRandomPattern(); cells.forEach((cell, i) => { const delay = stagger ? Math.random() * 2000 : Math.random() * 1000; setTimeout(() => { const videoDataUri = mintedVideos[pattern[i]]; if (!videoDataUri) return; cell.innerHTML = `<video src="${videoDataUri}" loop muted playsinline></video>`; const video = cell.querySelector('video'); video.currentTime = Math.random() * 1; video.play().catch(() => {}); }, delay); }); } function cycleVideos() { if (mintedVideos.length === 0) return; renderGrid(true); } function updateProgress(current, target) { const progressFill = document.getElementById('progressFill'); const progressText = document.getElementById('progressText'); const percentage = (current / target) * 100; progressFill.style.width = `${percentage}%`; progressText.textContent = `Loading videos ${current}/${target}...`; } async function init() { try { console.log('Init started...'); console.log('Loading all minted videos...'); const mintedItems = await fetchAllMinted(); console.log(`Found ${mintedItems.length} minted items`); if (mintedItems.length === 0) { document.getElementById('progressText').textContent = 'No minted videos found'; return; } const target = Math.min(10, mintedItems.length); updateProgress(0, target); // Load first 10 videos (or all if less than 10) for (let i = 0; i < target; i++) { console.log(`Decoding video ${i + 1}/${target}...`); const video = await decodeVideoFromTx(mintedItems[i].mintTxHash); if (video) { mintedVideos.push(video); updateProgress(mintedVideos.length, target); console.log(`Decoded ${mintedVideos.length}/${target}`); } } } catch (e) { console.error('Init error:', e); document.getElementById('progressText').textContent = 'Error loading videos'; } // Hide loading screen document.getElementById('loading').classList.add('hidden'); console.log(`Loaded ${mintedVideos.length} videos`); renderGrid(); setInterval(cycleVideos, 3000); // Continue loading remaining videos in background if (mintedItems.length > 10) { for (let i = 10; i < mintedItems.length; i++) { const video = await decodeVideoFromTx(mintedItems[i].mintTxHash); if (video) { mintedVideos.push(video); console.log(`Background loaded: ${mintedVideos.length}/${mintedItems.length}`); } } } } init(); </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Fever Dream Grid</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { background: #000; overflow: hidden; width: 100vw; height: 100vh; display: flex; align-items: center; justify-content: center; } .grid { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 1fr); width: 100vmin; height: 100vmin; gap: 0; } .cell { width: 100%; height: 100%; overflow: hidden; position: relative; background: #111; } .cell video { width: 100%; height: 100%; object-fit: cover; display: block; } .status { position: fixed; bottom: 8px; right: 8px; color: #CEFF00; padding: 4px 8px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; font-size: 11px; font-weight: 500; z-index: 1000; opacity: 0; transition: opacity 0.3s; pointer-events: none; } .status.visible { opacity: 0.9; } </style> </head> <body> <div class="grid" id="grid"></div> <div class="status" id="status">Loading...</div> <script> const POLL_INTERVAL = 10000; // 10 seconds const API_BASE = 'https://chainfeed.online/api/projects'; const SLUG = 'fever-dream'; // Generate random pattern of 9 video indices function generateRandomPattern(videoCount) { const pattern = []; for (let i = 0; i < 9; i++) { pattern.push(Math.floor(Math.random() * videoCount)); } return pattern; } let currentPattern = []; let mintedVideos = []; // Array of {id, name, dataUri, txHash} let lastMintCount = 0; let statusTimer = null; // Decode video from transaction hash async function decodeVideoFromTx(txHash) { try { const response = await fetch(`https://eth-mainnet.g.alchemy.com/v2/T1Y8E3Z8hUkVqyXz-I67Qh_Un2wHsfIZ`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_getTransactionByHash', params: [txHash] }) }); const data = await response.json(); if (!data.result || !data.result.input) { throw new Error('No transaction data'); } // Decode hex input to data URI const hex = data.result.input.slice(2); const bytes = new Uint8Array(hex.match(/.{1,2}/g).map(byte => parseInt(byte, 16))); const decoder = new TextDecoder(); const dataUri = decoder.decode(bytes); return dataUri; } catch (e) { console.error('Failed to decode video:', txHash, e); return null; } } // Poll metadata to check for new mints async function pollMetadata() { try { const response = await fetch(`${API_BASE}/${SLUG}?metadata=true`); const data = await response.json(); const currentMintCount = data.collection.minted; if (currentMintCount > lastMintCount) { console.log('🆕 New mints detected:', currentMintCount - lastMintCount, 'Current videos:', mintedVideos.length); await fetchNewMints(currentMintCount); console.log('✅ After fetch, total videos:', mintedVideos.length); lastMintCount = currentMintCount; // Show status for 1 minute when new mints found updateStatus(`${currentMintCount}/450`, true); } else { // Just update text without showing updateStatus(`${currentMintCount}/450`, false); } } catch (e) { console.error('Poll error:', e); updateStatus('Error polling...'); } } // Fetch newly minted items - get TX hashes from blockchain async function fetchNewMints(expectedCount) { try { updateStatus(`Fetching ${expectedCount} mint TXs...`); // Fetch items to get mint transaction hashes // Since minted items can be anywhere, fetch multiple pages const limit = 100; let allMintTxs = []; let page = 0; // Fetch pages until we have all minted items while (allMintTxs.length < expectedCount && page < 5) { // Max 5 pages (500 items) const response = await fetch(`${API_BASE}/${SLUG}?page=${page}&limit=${limit}`); const data = await response.json(); const mintedItems = data.items.filter(item => item.minted && item.mintTxHash); allMintTxs.push(...mintedItems.map(item => ({ id: item.id, name: item.name, txHash: item.mintTxHash }))); console.log(`Page ${page}: Found ${mintedItems.length} minted (total: ${allMintTxs.length}/${expectedCount})`); page++; if (!data.hasMore) break; } console.log(`Found ${allMintTxs.length} mint transactions`); // Decode videos directly from blockchain TXs for (const item of allMintTxs) { const alreadyHave = mintedVideos.find(v => v.txHash === item.txHash); if (alreadyHave) continue; updateStatus(`Decoding TX ${item.txHash.slice(0, 10)}...`, true); const dataUri = await decodeVideoFromTx(item.txHash); if (dataUri) { mintedVideos.push({ id: item.id, name: item.name, dataUri: dataUri, txHash: item.txHash }); console.log('✓ Decoded from blockchain:', item.name, '| Total videos now:', mintedVideos.length); // Re-render grid immediately after each new video is decoded console.log('🎬 Rendering grid with', mintedVideos.length, 'videos'); renderGrid(true); } } updateStatus(`${expectedCount}/450`, true); } catch (e) { console.error('Fetch error:', e); updateStatus('Error loading videos'); } } // Render the grid with current pattern function renderGrid(stagger = false) { const grid = document.getElementById('grid'); if (mintedVideos.length === 0) { grid.innerHTML = '<div class="cell"></div>'.repeat(9); return; } // Generate new random pattern if we don't have one or video count changed if (currentPattern.length === 0) { currentPattern = generateRandomPattern(mintedVideos.length); } const pattern = currentPattern; if (!stagger) { // Initial render grid.innerHTML = pattern.map(videoIndex => { const video = mintedVideos[videoIndex]; if (!video) return '<div class="cell"></div>'; return ` <div class="cell"> <video src="${video.dataUri}" loop muted playsinline></video> </div> `; }).join(''); // Start videos with random delays const videoElements = grid.querySelectorAll('video'); videoElements.forEach(video => { const randomDelay = Math.random() * 1000; setTimeout(() => { video.play().catch(() => {}); // Ignore play interruption errors }, randomDelay); }); } else { // Staggered pattern change const cells = grid.querySelectorAll('.cell'); // If no cells exist yet, do initial render if (cells.length === 0) { grid.innerHTML = pattern.map(videoIndex => { const video = mintedVideos[videoIndex]; if (!video) return '<div class="cell"></div>'; return ` <div class="cell"> <video src="${video.dataUri}" loop muted playsinline></video> </div> `; }).join(''); const videoElements = grid.querySelectorAll('video'); videoElements.forEach(video => { const randomDelay = Math.random() * 1000; setTimeout(() => { video.play().catch(() => {}); // Ignore play interruption errors }, randomDelay); }); return; } // Update existing cells with stagger cells.forEach((cell, i) => { const randomDelay = Math.random() * 2000; setTimeout(() => { const videoIndex = pattern[i]; const video = mintedVideos[videoIndex]; if (!video) return; cell.innerHTML = `<video src="${video.dataUri}" loop muted playsinline></video>`; const videoEl = cell.querySelector('video'); videoEl.currentTime = Math.random() * 1; videoEl.play().catch(() => {}); // Ignore play interruption errors }, randomDelay); }); } } // Cycle to next pattern function cyclePattern() { if (mintedVideos.length === 0) return; // Generate new random pattern currentPattern = generateRandomPattern(mintedVideos.length); renderGrid(true); } // Update status display and show for 1 minute function updateStatus(msg, showTemporarily = false) { const statusEl = document.getElementById('status'); statusEl.textContent = msg; if (showTemporarily) { // Clear existing timer if (statusTimer) clearTimeout(statusTimer); // Show status statusEl.classList.add('visible'); // Hide after 60 seconds statusTimer = setTimeout(() => { statusEl.classList.remove('visible'); }, 60000); } } // Start async function init() { await pollMetadata(); // Poll for updates setInterval(pollMetadata, POLL_INTERVAL); // Cycle patterns every 3 seconds setInterval(cyclePattern, 3000); } init(); </script> </body> </html>
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <title>Tree of Life Genesis</title> <style> *{margin:0;padding:0;box-sizing:border-box} body{background:#000;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;font-family:system-ui,sans-serif;color:#fff;overflow:hidden} #load{text-align:center} #title{font-size:clamp(18px,5vw,28px);letter-spacing:.4em;text-transform:uppercase;margin-bottom:40px;font-weight:300;opacity:.9} #bar-wrap{width:min(200px,60vw);height:2px;background:rgba(255,255,255,.1);margin-bottom:16px} #bar{width:0;height:100%;background:#fff;transition:width .2s} #status{font-size:11px;letter-spacing:.15em;text-transform:uppercase;opacity:.4} #player{display:none;position:relative;width:min(90vw,90vh);max-width:640px;aspect-ratio:1/1} #player video{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;opacity:0;transition:opacity .15s} #player video.active{opacity:1} #phase{display:none;margin-top:20px;font-size:clamp(11px,2.5vw,14px);letter-spacing:.3em;text-transform:uppercase;opacity:.5;font-weight:300} #end{display:none;text-align:center} #end-title{font-size:clamp(16px,4vw,24px);letter-spacing:.3em;text-transform:uppercase;margin-bottom:32px;font-weight:300;opacity:.9} .btn{display:inline-block;padding:10px 24px;margin:8px;font-size:11px;letter-spacing:.15em;text-transform:uppercase;text-decoration:none;color:#fff;border:1px solid rgba(255,255,255,.25);background:0 0;cursor:pointer;font-family:inherit;transition:all .2s} .btn:hover{background:rgba(255,255,255,.08);border-color:rgba(255,255,255,.5)} </style> </head> <body> <div id="load"> <div id="title">Tree of Life<br>Genesis</div> <div id="bar-wrap"><div id="bar"></div></div> <div id="status">Loading</div> </div> <div id="player"></div> <div id="phase"></div> <div id="end"> <div id="end-title">Tree of Life<br>Genesis</div> <button class="btn" onclick="replay()">Watch Again</button> <a class="btn" href="https://chainfeed.online/projects/view.html?slug=tree-of-life-genesis">View Collection</a> </div> <script> var TX=[ '0xec59e5ccbc203e624e90d49429cc9e951b0d4516175d73f82c2ca432bc919d25', '0x4ea72a06d060ccb4640427d8f710dddec905e3259fdf4df9cd14f8d2e584afa2', '0xcd6cea15b1f3a144bfaa97b595b23a9702036f8f419e5db177d9ccf2a45bd6c6', '0x27e8cb550fe22fd27c9b2c54a258034b32949ddd3e35b13ecb5a5a9062964e08', '0x4989df3ca4cf0cf488352b441b2f3905656fb72b9ca305b7e69edfc5b503aad1', '0x5f5c9504d3d35704b0641a2d067c2a2476cd04e1c8e471b9f70caf7da5139dc5', '0xfba1bdb8c0203544c289f4f546ff9216098dc3b7aac082f6a9408fabb1469428', '0xfd8c0abace7e607b59b5ed5cf571936c45c59b3dc924be671902c878c9202102', '0x079e3cb470d857a2e339804f85e8e80e0527c016823f7580f69d9d50d81557ec', '0x51978e7236cb63bca655aabb4987adb6bf558df403c15c38f19b777ef6ca1ca7', '0x311fc18e0392aac88fb453b06de04018aa2d12aaf31df03d51a14add5cd613ac' ]; var NAMES=['I. Void','II. Spark','III. Seed','IV. Roots','V. Trunk','VI. Branches','VII. Leaves','VIII. Flowers','IX. Fruit','X. Seeds','XI. Universe']; var load=document.getElementById('load'),bar=document.getElementById('bar'),status=document.getElementById('status'),player=document.getElementById('player'),phase=document.getElementById('phase'),end=document.getElementById('end'); var vids=[],cur=0,loaded=0; function hex2str(h){for(var s='',i=0;i<h.length;i+=2)s+=String.fromCharCode(parseInt(h.substr(i,2),16));return s} function toBlob(d){var p=d.split(','),m=p[0].match(/:(.*?);/)[1],b=atob(p[1]),a=new Uint8Array(b.length);for(var i=0;i<b.length;i++)a[i]=b.charCodeAt(i);return URL.createObjectURL(new Blob([a],{type:m}))} function progress(){bar.style.width=(loaded/TX.length*100)+'%';status.textContent=loaded<TX.length?'Loading '+loaded+'/'+TX.length:'Ready'} async function loadClip(tx,idx){ try{ var r=await fetch('https://eth.blockscout.com/api/v2/transactions/'+tx); if(!r.ok)throw 0; var d=await r.json(); if(!d.raw_input)throw 0; var uri=hex2str(d.raw_input.slice(2)); if(uri.indexOf('data:')!==0)throw 0; var v=document.createElement('video'); v.src=toBlob(uri);v.muted=true;v.playsInline=true;v.preload='auto'; vids[idx]=v;player.appendChild(v); await new Promise(function(ok,no){v.oncanplaythrough=ok;v.onerror=no;v.load()}); }catch(e){} loaded++;progress(); } function play(){ load.style.display='none';player.style.display='block';phase.style.display='block'; function next(){ var v=vids[cur];if(!v){showEnd();return} phase.textContent=NAMES[cur]||''; vids.forEach(function(x){if(x)x.className=''}); v.className='active';v.currentTime=0;v.play(); v.onended=function(){cur++;next()}; } next(); } function showEnd(){player.style.display='none';phase.style.display='none';end.style.display='block'} function replay(){end.style.display='none';cur=0;play()} async function init(){ progress(); await Promise.all(TX.map(function(t,i){return loadClip(t,i)})); var ok=vids.filter(function(v){return v}); if(!ok.length){status.textContent='Failed';return} setTimeout(play,600); } init(); </script> </body> </html>