memoscan
← all memos

Memo 0x35da58ce…c4e20c on Ethereum

"}; let _seed; let vehicles = []; const vehicleTypes = ["train", "bus", "car"]; const morphDuration = 120; let numTrains; const trainLength = 100; const trainHeight = 20; let bgs; // Background settings object let rAlpha; // Random alpha factor for backgrounds let entropyType; let bgTrait; // Sky trait let st; // Spacetime settings object let pBgo = 0, pMgo = 0, pFgo = 0; // Parallax offsets const tsBg = 100, tsMg = 100, tsFg = 100; // Tile sizes const pBgsp = 0.2, pMgsp = 0.5, pFgsp = 1.0; // Parallax speeds let basesp; // Base speed for vehicles const COLOR_CHANGE_COOLDOWN = 180; const COLOR_LERP_DURATION = 60; let lastColorChangeFrame = -COLOR_CHANGE_COOLDOWN; let colorTransitionTarget = null; let colorTransitionStartColors = {}; let colorTransitionProgress = 1; const { PI, sin, cos, floor, round, abs, sqrt, max, min } = Math; const TWO_PI = 2 * PI; function map(value, start1, stop1, start2, stop2) { return start2 + (stop2 - start2) * ((value - start1) / (stop1 - start1)); } function lerp(start, stop, amt) { return start + (stop - start) * amt; } function easeInOut(t) { return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t; } function rand() { _seed ^= _seed << 13; _seed ^= _seed >>> 17; _seed ^= _seed << 5; return (_seed >>> 0) / 4294967296; } function random(a, b) { if (a === undefined) return rand(); if (b === undefined) { b = a; a = 0; } return rand() * (b - a) + a; } const hl = { randomElement: arr => arr[floor(rand() * arr.length)] }; const PERLIN_YWRAPB = 4; const PERLIN_YWRAP = 1 << PERLIN_YWRAPB; const PERLIN_SIZE = 4095; const PERLIN_OCTAVES = 4; const PERLIN_AMP_FALLOFF = 0.5; let perlin_noise_array; function seedNoise() { perlin_noise_array = new Array(PERLIN_SIZE + 1); for (let i_pn = 0; i_pn < PERLIN_SIZE + 1; i_pn++) { perlin_noise_array[i_pn] = floor(random(256)); } } const scaled_cosine = (i_sc) => 0.5 * (1.0 - cos(i_sc * PI)); function noise(x_noise, y_noise = 0) { if (perlin_noise_array === undefined) { seedNoise(); } if (x_noise < 0) x_noise = -x_noise; if (y_noise < 0) y_noise = -y_noise; let xi_noise = floor(x_noise); let yi_noise = floor(y_noise); let xf_noise = x_noise - xi_noise; let yf_noise = y_noise - yi_noise; let rxf_noise, ryf_noise; let r_noise = 0; let ampl_noise = 0.5; let n1_noise, n2_noise; for (let o_noise = 0; o_noise < PERLIN_OCTAVES; o_noise++) { let of_noise = xi_noise + (yi_noise << PERLIN_YWRAPB); rxf_noise = scaled_cosine(xf_noise); ryf_noise = scaled_cosine(yf_noise); n1_noise = perlin_noise_array[of_noise & PERLIN_SIZE] / 255.0; n1_noise += rxf_noise * ((perlin_noise_array[(of_noise + 1) & PERLIN_SIZE] / 255.0) - n1_noise); n2_noise = perlin_noise_array[(of_noise + PERLIN_YWRAP) & PERLIN_SIZE] / 255.0; n2_noise += rxf_noise * ((perlin_noise_array[(of_noise + PERLIN_YWRAP + 1) & PERLIN_SIZE] / 255.0) - n2_noise); n1_noise += ryf_noise * (n2_noise - n1_noise); r_noise += n1_noise * ampl_noise; ampl_noise *= PERLIN_AMP_FALLOFF; xi_noise <<= 1; xf_noise *= 2; yi_noise <<= 1; yf_noise *= 2; if (xf_noise >= 1.0) { xi_noise++; xf_noise--; } if (yf_noise >= 1.0) { yi_noise++; yf_noise--; } } return max(0, min(1, r_noise)); } function parseColor(c) { if (typeof c === 'string' && c.startsWith('#')) { let bigint = parseInt(c.slice(1), 16); return { r: (bigint >> 16) & 255, g: (bigint >> 8) & 255, b: bigint & 255, a: 1 }; } else if (Array.isArray(c)) { let [h, s, b_val, a_val] = c; h %= 360; s /= 100; b_val /= 100; a_val = (a_val === undefined) ? 1 : a_val; let i = floor(h / 60), f = h / 60 - i, p = b_val * (1 - s), q = b_val * (1 - f * s), t = b_val * (1 - (1 - f) * s); let r_rgb, g_rgb, b_rgb_val; switch (i % 6) { case 0: r_rgb = b_val; g_rgb = t; b_rgb_val = p; break; case 1: r_rgb = q; g_rgb = b_val; b_rgb_val = p; break; case 2: r_rgb = p; g_rgb = b_val; b_rgb_val = t; break; case 3: r_rgb = p; g_rgb = q; b_rgb_val = b_val; break; case 4: r_rgb = t; g_rgb = p; b_rgb_val = b_val; break; case 5: r_rgb = b_val; g_rgb = p; b_rgb_val = q; break; default: r_rgb = 0; g_rgb = 0; b_rgb_val = 0; } return { r: r_rgb * 255, g: g_rgb * 255, b: b_rgb_val * 255, a: a_val }; } else if (typeof c === 'object' && c !== null && 'r' in c && 'g' in c && 'b' in c) { return { ...c, a: c.a === undefined ? 1 : c.a }; } return { r: 0, g: 0, b: 0, a: 1 }; } function toCanvasRGBA(rgba) { return `rgba(${round(rgba.r)},${round(rgba.g)},${round(rgba.b)},${rgba.a})`; } function lerpColor(color1, color2, amt) { let c1 = parseColor(color1); let c2 = parseColor(color2); return { r: lerp(c1.r, c2.r, amt), g: lerp(c1.g, c2.g, amt), b: lerp(c1.b, c2.b, amt), a: lerp(c1.a, c2.a, amt) }; } function color(...args) { if (args.length === 1) return parseColor(args[0]); if (args.length >= 3) return parseColor([args[0], args[1], args[2], args[3]]); return parseColor(null); } class MorphingVehicle { constructor(x, y, currentBasesp) { this.x = x; this.y = y; this.baseLength = trainLength; this.baseHeight = trainHeight; this.sp = random(currentBasesp, currentBasesp * 2); this.color = color(hl.randomElement(bgs.trc)); this.baseY = y; this.fadeInStart = frameCount; this.fadeInDuration = random(30, 90); this.fadeOutStart = frameCount + random(300, 900); this.fadeOutDuration = random(60, 120); this.isMorphing = false; this.holdDuration = floor(random(60, 240)); this.holdCounter = 0; this.currentType = hl.randomElement(vehicleTypes); this.nextType = this.getNextType(); this.morphProgress = 0; this.vertices = this.getShapeVertices(this.currentType); this.targetVertices = this.getShapeVertices(this.nextType); this.fixVertexCount(); } update() { this.x += this.sp; if (entropyType === "Wabisabi") { this.y = this.baseY + map(noise(this.x * 0.005, frameCount * 0.003), 0, 1, -10, 10); this.x += map(noise(this.x * 0.007, frameCount * 0.003), 0, 1, -2, 2); } else if (entropyType === "Freestyle") { this.y = this.baseY + map(noise(this.x * 0.005, frameCount * 0.005), 0, 1, -50, 50); this.x += map(noise(this.x * 0.003, frameCount * 0.005), 0, 1, -3, 3); } if (entropyType === "Balance") { this.y = this.baseY + sin(frameCount * 0.05 + this.x * 0.01) * 20; } if (this.x > width + this.baseLength || frameCount >= this.fadeOutStart + this.fadeOutDuration) { this.reset(); return; } if (!this.isMorphing) { this.holdCounter++; if (this.holdCounter >= this.holdDuration) { this.isMorphing = true; this.holdCounter = 0; } } else { this.morphProgress += 1 / morphDuration; if (this.morphProgress >= 1) { this.currentType = this.nextType; this.vertices = this.targetVertices; this.nextType = this.getNextType(); this.targetVertices = this.getShapeVertices(this.nextType); this.fixVertexCount(); this.morphProgress = 0; this.isMorphing = false; this.holdDuration = floor(random(60, 240)); } } } display() { let alpha; if (frameCount < this.fadeInStart + this.fadeInDuration) alpha = map(frameCount, this.fadeInStart, this.fadeInStart + this.fadeInDuration, 0, 1); else if (frameCount < this.fadeOutStart) alpha = 1; else if (frameCount < this.fadeOutStart + this.fadeOutDuration) alpha = map(frameCount, this.fadeOutStart, this.fadeOutStart + this.fadeOutDuration, 1, 0); else alpha = 0; if (alpha <= 0) return; let morphedVertices = this.vertices.map((v, i) => ({ x: lerp(v.x, this.targetVertices[i].x, easeInOut(this.morphProgress)), y: lerp(v.y, this.targetVertices[i].y, easeInOut(this.morphProgress)) })); let finalColor = { ...this.color, a: this.color.a * alpha }; ctx.strokeStyle = toCanvasRGBA(finalColor); ctx.lineWidth = 3; ctx.fillStyle = 'transparent'; ctx.beginPath(); if (morphedVertices.length > 0) { ctx.moveTo(this.x + morphedVertices[0].x, this.y + morphedVertices[0].y); for (let i = 1; i < morphedVertices.length; i++) { ctx.lineTo(this.x + morphedVertices[i].x, this.y + morphedVertices[i].y); } } ctx.closePath(); ctx.stroke(); } reset() { this.x = random(-this.baseLength, width); this.y = random(height * 0.05, height * 0.95); this.baseY = this.y; this.sp = random(basesp, basesp * 2); this.color = color(hl.randomElement(bgs.trc)); this.fadeInStart = frameCount; this.fadeInDuration = random(30, 90); this.fadeOutStart = frameCount + random(300, 900); this.fadeOutDuration = random(60, 120); this.currentType = hl.randomElement(vehicleTypes); this.nextType = this.getNextType(); this.morphProgress = 0; this.isMorphing = false; this.holdCounter = 0; this.holdDuration = floor(random(60, 240)); this.vertices = this.getShapeVertices(this.currentType); this.targetVertices = this.getShapeVertices(this.nextType); this.fixVertexCount(); } getNextType() { return vehicleTypes[(vehicleTypes.indexOf(this.currentType) + 1) % vehicleTypes.length]; } getShapeVertices(type) { let L = this.baseLength, H = this.baseHeight; if (type === "train") return [{x:-L,y:-H*.5},{x:-L*.3,y:-H*.5},{x:-L*.15,y:-H*.5},{x:L*.15,y:-H*.5},{x:L*.2,y:-H*.5},{x:L*.25,y:-H*.2},{x:L*.35,y:0},{x:L*.38,y:H*.2},{x:L*.3,y:H*.5},{x:-L*.15,y:H*.5},{x:-L*.3,y:H*.5},{x:-L,y:H*.5}]; if (type === "bus") return [{x:-L*.5,y:-H*.5},{x:-L*.3,y:-H*.5},{x:-L*.15,y:-H*.5},{x:0,y:-H*.5},{x:L*.15,y:-H*.5},{x:L*.25,y:-H*.5},{x:L*.3,y:-H*.3},{x:L*.32,y:H*.3},{x:L*.3,y:H*.5},{x:-L*.15,y:H*.5},{x:-L*.3,y:H*.5},{x:-L*.5,y:H*.5}]; if (type === "car") return [{x:-L*.3,y:-H*.35},{x:-L*.1,y:-H*.5},{x:0,y:-H*.5},{x:L*.1,y:-H*.2},{x:L*.2,y:-H*.1},{x:L*.27,y:H*.1},{x:L*.25,y:H*.3},{x:L*.1,y:H*.3},{x:-L*.1,y:H*.3},{x:-L*.3,y:H*.3},{x:-L*.35,y:H*.1},{x:-L*.3,y:-H*.1}]; return []; } fixVertexCount() { let maxLen = max(this.vertices.length, this.targetVertices.length); while (this.vertices.length < maxLen) this.vertices.push({ ...this.vertices[this.vertices.length - 1] }); while (this.targetVertices.length < maxLen) this.targetVertices.push({ ...this.targetVertices[this.targetVertices.length - 1] }); } } let effectStates = {}; function initializeArtwork() { // Read data injected by the smart contract const traits = window.GOM_TRAITS; if (!traits || !traits.hash || !traits.rand_indices) { document.body.innerHTML = `<pre style="color:red;">Error: Trait data not found.</pre>`; return; } const rand_indices = traits.rand_indices.split(',').map(n => parseInt(n)); _seed = parseInt(traits.hash.slice(2, 18), 16); perlin_noise_array = undefined; // --- START: Trait Generation using On-Chain Indices --- const numTrainsOptions = [1,1,3,3,3,3,3,11,11,11,11,11,11,11,11,11,21,21,21,33]; numTrains = numTrainsOptions[rand_indices[0]]; const rAlphaOptions = [37, 38, 39]; rAlpha = rAlphaOptions[rand_indices[1]] / 1000.0; const bgsOptions = [ { sn: "Kabuki", trc: ["#fa4e28","#A10D2E","#E63946","#F4A261","#f0d507"], bgt_raw: [240,77,28], bgb_raw: [240,70,39], trailColor: "#B51A15"}, { sn: "Diamond Sky", trc: ["#a7fae7","#fca9e3","#b97af5","#f2ffed","#ff6b84"], bgt_raw: [199,100.0,98.04], bgb_raw: [197,87,98], trailColor: "#fcfcfc"}, { sn: "Red Silk", trc: ["#ff6b84","#ff1c1c","#ffc505","#e4b7e8","#f2e8b6"], bgt_raw: [358,94.44,35], bgb_raw: [355,70.0,25], trailColor: "#fcd4d4"}, { sn: "Green Tea", trc: ["#a34ea3","#e4b7e8","#aae6e6","#9e9ef7","#f5c4c4"], bgt_raw: [111,50,60], bgb_raw: [111,58,73], trailColor: "#EA1DC3"}, { sn: "Sand Wave", trc: ["#dbf1ff","#F2EBEF","#00acd0","#6270a3","#026bc7"], bgt_raw: [50,50,65], bgb_raw: [50,50,77], trailColor: "#afe3ed"}, { sn: "Whispers of History", trc: ["#f51b05","#a60228","#343deb","#f5f0f0","#202691"], bgt_raw: [220,15,5], bgb_raw: [355,96,18], trailColor: "#850303"}, { sn: "Plum Blossom", trc: ["#b50719","#d1ba02","#E5D0A8","#607D3B","#B18969"], bgt_raw: [290,65,39], bgb_raw: [284,63,55], trailColor: "#f7d7f7"}, { sn: "Typhoon", trc: ["#66a3d4","#333f4d","#5ebda4","#A3A3A3","#414B56"], bgt_raw: [119,15,92], bgb_raw: [192,35,95], trailColor: "#65c1f7"}, { sn: "Maple Leaves", trc: ["#D81159","#8B0000","#F4A261","#264653","#A53860"], bgt_raw: [38,35,92.16], bgb_raw: [32,27,92], trailColor: "#fa5050"} ]; bgs = bgsOptions[rand_indices[2]]; bgs.bgt = [...bgs.bgt_raw, rAlpha]; bgs.bgb = [...bgs.bgb_raw, rAlpha]; const stOptions = [ { spt: "Markets", _a:bgs.bgb, _b:bgs.trailColor, _c:bgs.bgb, wa:0, uta:!1, wb:3, utb:!0, wc:0, utc:!1 }, { spt: "Streets Imaginaire",_a:bgs.bgb, _b:bgs.trailColor, _c:bgs.trailColor, wa:35, uta:!1, wb:3, utb:!0, wc:15, utc:!0 }, { spt: "XXX", _a:bgs.bgb, _b:bgs.bgb, _c:bgs.trailColor, wa:10, uta:!1, wb:5, utb:!1, wc:1, utc:!0 }, { spt: "Drift", _a:bgs.bgb, _b:bgs.bgb, _c:bgs.trailColor, wa:0, uta:!1, wb:25, utb:!1, wc:3, utc:!0 }, { spt: "Portals", _a:bgs.trailColor, _b:bgs.trailColor, _c:bgs.bgb, wa:3, uta:!0, wb:2, utb:!0, wc:10, utc:!1 }, { spt: "Gang", _a:bgs.trailColor, _b:bgs.trailColor, _c:bgs.trailColor, wa:3, uta:!0, wb:1, utb:!0, wc:1, utc:!0 }, { spt: "Dust", _a:bgs.trailColor, _b:bgs.trailColor, _c:bgs.trailColor, wa:1, uta:!0, wb:1, utb:!0, wc:1.5,utc:!0 }, { spt: "Rings", _a:bgs.trailColor, _b:bgs.trailColor, _c:bgs.trailColor, wa:1.5,uta:!0, wb:1, utb:!0, wc:0.5,utc:!0 }, { spt: "Entangled", _a:bgs.trailColor, _b:bgs.bgb, _c:bgs.bgb, wa:3, uta:!0, wb:0, utb:!1, wc:0, utc:!1 } ]; st = stOptions[rand_indices[3]]; st.acla = Array.isArray(st._a) ? color(...st._a) : color(st._a); st.aclb = Array.isArray(st._b) ? color(...st._b) : color(st._b); st.aclc = Array.isArray(st._c) ? color(...st._c) : color(st._c); st.weighta = st.wa; st.usesTrailColorA = st.uta; st.weightb = st.wb; st.usesTrailColorB = st.utb; st.weightc = st.wc; st.usesTrailColorC = st.utc; entropyType = ({0.039:"Wabisabi", 0.038:"Balance", 0.037:"Freestyle"})[rAlpha.toFixed(3)]; const bgTraitOptions = ["Comet", "Sun", "Stars", "Moonbow", "Nebula"]; bgTrait = bgTraitOptions[rand_indices[4]]; basesp = sqrt(width * height) * 0.002; vehicles = []; for (let i = 0; i < numTrains; i++) { vehicles.push(new MorphingVehicle(random(-trainLength, width), random(height * 0.05, height * 0.95), basesp)); } effectStates = {}; if (bgTrait === "Sun") effectStates.sun = initSun(); if (bgTrait === "Comet") effectStates.comet = initComet(); if (bgTrait === "Stars") { effectStates.stars = []; for (let i = 0; i < 500; i++) { effectStates.stars.push({ x: random(width), y: random(height), size: random(1, 3) }); } effectStates.starRotationCenter = { x: random(width/3-width*.25, width/3+width*.25), y: random(height/3-height*.25, height/3+height*.25) }; effectStates.starRotation = random(TWO_PI); effectStates.starRotationsp = random(0.0005, 0.002) * (random() < 0.5 ? 1 : -1); } if (bgTrait === "Moonbow") effectStates.moonbow = initMoonbow(); if (bgTrait === "Nebula") effectStates.nebula = initNebula(); drawBgTile.ballX = undefined; if (perlin_noise_array === undefined) { seedNoise(); } } function renderFrame() { let bgTopColor = color(...bgs.bgt); ctx.fillStyle = toCanvasRGBA(bgTopColor); ctx.fillRect(0, 0, width, height); if (colorTransitionProgress < 1 && colorTransitionTarget) { colorTransitionProgress += 1 / COLOR_LERP_DURATION; colorTransitionProgress = min(1, colorTransitionProgress); let lerpAmt = easeInOut(colorTransitionProgress); if(st.usesTrailColorA) st.acla = lerpColor(colorTransitionStartColors.a, colorTransitionTarget, lerpAmt); if(st.usesTrailColorB) st.aclb = lerpColor(colorTransitionStartColors.b, colorTransitionTarget, lerpAmt); if(st.usesTrailColorC) st.aclc = lerpColor(colorTransitionStartColors.c, colorTransitionTarget, lerpAmt); } if (random() < 0.05 && frameCount >= lastColorChangeFrame + COLOR_CHANGE_COOLDOWN && colorTransitionProgress >=1) { let visVehicles = vehicles.filter(v => v.x >= 0 && v.x <= width); if (visVehicles.length > 0) { let visColors = visVehicles.map(v => v.color); if (visColors.length > 0) { colorTransitionTarget = hl.randomElement(visColors); colorTransitionStartColors = { a: {...st.acla}, b: {...st.aclb}, c: {...st.aclc} }; colorTransitionProgress = 0; lastColorChangeFrame = frameCount; } } } drawpLayers(); drawAdditionalBackground(); for (let vehicle of vehicles) { vehicle.update(); vehicle.display(); } } function drawpLayers() { ctx.save(); drawBackgroundLayer(); drawMidgroundLayer(); drawForegroundLayer(); ctx.restore(); } function drawBackgroundLayer() { pBgo += pBgsp; let oX = pBgo % tsBg; for (let x = -tsBg; x < width + tsBg; x += tsBg) for (let y = 0; y < height; y += tsBg) { ctx.save(); ctx.translate(x - oX, y); drawBgTile(); ctx.restore(); } } function drawMidgroundLayer() { pMgo += pMgsp; let oX = pMgo % tsMg; for (let x = -tsMg; x < width + tsMg; x += tsMg) for (let y = 0; y < height; y += tsMg) { ctx.save(); ctx.translate(x - oX, y); drawMgTile(); ctx.restore(); } } function drawForegroundLayer() { pFgo += pFgsp; let oX = pFgo % tsFg; for (let x = -tsFg; x < width + tsFg; x += tsFg) for (let y = 0; y < height; y += tsFg) { ctx.save(); ctx.translate(x - oX, y); drawFgTile(); ctx.restore(); } } function drawBgTile() { let cola = st.acla; if (st.weighta <= 0) return; ctx.strokeStyle = toCanvasRGBA(cola); ctx.lineWidth = st.weighta; ctx.fillStyle = 'transparent'; ctx.beginPath(); const spt = st.spt; if (spt === "Markets" && typeof noise === 'function') { ctx.moveTo(0, map(noise(0, frameCount * 0.01), 0, 1, 0, tsBg)); for (let x = 5; x <= tsBg; x += 5) ctx.lineTo(x, map(noise(x * 0.05, frameCount * 0.01), 0, 1, 0, tsBg)); } else if (spt === "Streets Imaginaire") { ctx.lineCap = 'square'; ctx.moveTo(0,0);ctx.lineTo(tsBg,0); ctx.moveTo(0,0);ctx.lineTo(0,tsBg); ctx.moveTo(0,tsBg);ctx.lineTo(tsBg,tsBg); ctx.moveTo(tsBg,0);ctx.lineTo(tsBg,tsBg); } else if (spt === "XXX") { ctx.moveTo(0,0);ctx.lineTo(tsBg,0); ctx.moveTo(0,0);ctx.lineTo(0,tsBg); ctx.moveTo(0,tsBg);ctx.lineTo(tsBg,tsBg); ctx.moveTo(tsBg,0);ctx.lineTo(tsBg,tsBg); ctx.moveTo(0,0);ctx.lineTo(tsBg,tsBg); ctx.moveTo(tsBg,0);ctx.lineTo(0,tsBg); } else if (spt === "Drift") { ctx.moveTo(0,0);ctx.lineTo(tsBg,0); } else if (spt === "Portals") { let y = map(sin(frameCount*0.02),-1,1,0,tsBg); ctx.moveTo(0,y);ctx.lineTo(tsBg,y); let x = map(sin(frameCount*0.02),-1,1,0,tsBg); ctx.moveTo(x,0);ctx.lineTo(x,tsBg); } else if (spt === "Gang" && typeof noise === 'function') { let oX = map(noise(frameCount*0.0025),0,1,-90,90), oY = map(noise(frameCount*0.0025+100),0,1,-90,90); ctx.save(); ctx.translate(tsBg/2+oX, tsBg/2+oY); ctx.strokeRect(-tsBg*.1,0,tsBg*.07,tsBg*.07); ctx.strokeRect(tsBg*.1,0,tsBg*.07,tsBg*.07); ctx.restore(); ctx.closePath(); return; } else if (spt === "Dust") { if (drawBgTile.ballX === undefined) { drawBgTile.ballX = tsBg/2; drawBgTile.ballY = tsBg/2; drawBgTile.ballVX = 2; drawBgTile.ballVY = 3; } drawBgTile.ballX += drawBgTile.ballVX; drawBgTile.ballY += drawBgTile.ballVY; if (drawBgTile.ballX<0||drawBgTile.ballX>tsBg) { drawBgTile.ballX=max(0,min(tsBg,drawBgTile.ballX)); drawBgTile.ballVX*=-1; } if (drawBgTile.ballY<0||drawBgTile.ballY>tsBg) { drawBgTile.ballY=max(0,min(tsBg,drawBgTile.ballY)); drawBgTile.ballVY*=-1; } ctx.lineWidth = st.weighta; ctx.beginPath(); ctx.ellipse(drawBgTile.ballX,drawBgTile.ballY,tsBg*.01,tsBg*.01,0,0,TWO_PI); ctx.stroke(); ctx.closePath(); return; } else if (spt === "Rings") { let ang=frameCount*.015, pX=map(cos(ang),-1,1,0,tsBg), pY=map(sin(ang*1.2),-1,1,0,tsBg); ctx.beginPath(); ctx.ellipse(pX,pY,1,1,0,0,TWO_PI); ctx.stroke(); ctx.closePath(); return; } else if (spt === "Entangled" && typeof noise === 'function') { let sd=floor(noise(frameCount*.001)*1e4), raw=sin(sd*.00123), ang=map(raw,-1,1,-PI/4,PI/4); if(sd%2===0)ang=PI-ang; let dX=cos(ang),dY=sin(ang),rX=tsBg/2+dX*frameCount,rY=tsBg/2+dY*frameCount; const mB=(p,s)=>abs(((p%(2*s))+(2*s))%(2*s)-s); ctx.beginPath();ctx.ellipse(mB(rX,tsBg),mB(rY,tsBg),1.5,1.5,0,0,TWO_PI);ctx.stroke();ctx.closePath();return; } ctx.stroke(); } function drawMgTile() { let colb = st.aclb; if (st.weightb <= 0) return; ctx.strokeStyle = toCanvasRGBA(colb); ctx.lineWidth = st.weightb; ctx.fillStyle = 'transparent'; ctx.beginPath(); const spt = st.spt; if (spt === "Markets" && typeof noise === 'function') { ctx.moveTo(0, map(noise(0, frameCount * 0.01), 0, 1, 0, tsMg)); for (let x = 5; x <= tsMg; x += 5) ctx.lineTo(x, map(noise(x * 0.05, frameCount * 0.01), 0, 1, 0, tsMg)); } else if (spt === "Streets Imaginaire" && typeof noise === 'function') { ctx.save(); ctx.translate(tsMg/2,tsMg/2); ctx.rotate(noise(frameCount*.01)*TWO_PI); ctx.lineWidth=3; ctx.moveTo(-tsMg*.1,0); ctx.lineTo(tsMg*.1,0); ctx.stroke(); ctx.restore(); ctx.closePath(); return; } else if (spt === "XXX") { ctx.moveTo(0,0);ctx.lineTo(tsMg,0); ctx.moveTo(0,0);ctx.lineTo(0,tsMg); ctx.moveTo(0,tsMg);ctx.lineTo(tsMg,tsMg); ctx.moveTo(tsMg,0);ctx.lineTo(tsMg,tsMg); ctx.moveTo(0,0);ctx.lineTo(tsMg,tsMg); ctx.moveTo(tsMg,0);ctx.lineTo(0,tsMg); } else if (spt === "Drift") { ctx.moveTo(0,0);ctx.lineTo(tsMg,tsMg); ctx.moveTo(tsMg,0);ctx.lineTo(0,tsMg); } else if (spt === "Portals") { let y=map(sin(frameCount*.02),-1,1,0,tsMg); ctx.moveTo(0,y);ctx.lineTo(tsMg,y); let x=map(sin(frameCount*.02),-1,1,0,tsMg); ctx.moveTo(x,0);ctx.lineTo(x,tsMg); } else if (spt === "Gang") { let s2=map(sin(frameCount*.05),-1,1,-99,-90),seta=tsMg/2; ctx.moveTo(0,seta);ctx.lineTo(tsMg+s2,seta); ctx.moveTo(0,seta);ctx.lineTo(0,tsMg+s2+seta); ctx.moveTo(0,tsMg+s2+seta);ctx.lineTo(tsMg+s2,tsMg+s2+seta); ctx.moveTo(tsMg+s2,seta);ctx.lineTo(tsMg+s2,tsMg+s2+seta); let s2b=map(sin(frameCount*.05),-1,1,-99,-90),gap2=tsMg/5; ctx.moveTo(gap2,seta);ctx.lineTo(gap2+tsMg+s2b,seta); ctx.moveTo(gap2,seta);ctx.lineTo(gap2,tsMg+s2b+seta); ctx.moveTo(gap2,tsMg+s2b+seta);ctx.lineTo(gap2+tsMg+s2b,tsMg+s2b+seta); ctx.moveTo(gap2+tsMg+s2b,seta);ctx.lineTo(gap2+tsMg+s2b,tsMg+s2b+seta); } else if (spt === "Dust") { ctx.beginPath(); ctx.ellipse(0,tsMg,1,1,0,0,TWO_PI); ctx.fillStyle=toCanvasRGBA(colb); ctx.fill(); ctx.closePath(); return; } else if (spt === "Rings") { let ang=frameCount*.015, pX=map(cos(ang),-1,1,0,tsMg), pY=map(sin(ang*1.2),-1,1,0,tsMg); ctx.beginPath(); ctx.ellipse(pX,pY,1,1,0,0,TWO_PI); ctx.stroke(); ctx.closePath(); return; } else if (spt === "Entangled") { ctx.moveTo(0,0);ctx.lineTo(tsMg,tsMg); } ctx.stroke(); } function drawFgTile() { let colc = st.aclc; if (st.weightc <= 0) return; ctx.strokeStyle = toCanvasRGBA(colc); ctx.lineWidth = st.weightc; ctx.fillStyle = 'transparent'; ctx.lineCap = 'square'; ctx.beginPath(); const spt = st.spt; if (spt === "Streets Imaginaire") { ctx.moveTo(0,0);ctx.lineTo(tsFg,0); ctx.moveTo(0,0);ctx.lineTo(0,tsFg); ctx.moveTo(0,tsFg);ctx.lineTo(tsFg,tsFg); ctx.moveTo(tsFg,0);ctx.lineTo(tsFg,tsFg); } else if (spt === "XXX") { ctx.moveTo(0,0);ctx.lineTo(tsFg,tsFg); ctx.moveTo(tsFg,0);ctx.lineTo(0,tsFg); } else if (spt === "Drift") { ctx.beginPath(); ctx.moveTo(0,0);ctx.lineTo(0,tsFg); ctx.stroke(); ctx.beginPath(); ctx.moveTo(tsFg,0);ctx.lineTo(tsFg,tsFg); ctx.stroke(); ctx.fillStyle=toCanvasRGBA(colc); let cX=tsFg/10, pS=max(1,st.weightc); for(let i=5;i<tsFg;i+=10) ctx.fillRect(cX-pS/2,i-pS/2,pS,pS); return; } else if (spt === "Portals" || spt === "Gang") { ctx.moveTo(0,0);ctx.lineTo(0,tsFg); ctx.moveTo(tsFg,0);ctx.lineTo(tsFg,tsFg); } else if (spt === "Dust") { ctx.beginPath(); ctx.ellipse(0,tsFg/2,1.5,1.5,0,0,TWO_PI); ctx.fillStyle=toCanvasRGBA(colc); ctx.fill(); ctx.closePath(); return; } else if (spt === "Rings") { let ang=frameCount*.015,pX=map(cos(ang),-1,1,0,tsFg),pY=map(sin(ang*1.2),-1,1,0,tsFg); ctx.beginPath();ctx.ellipse(pX,pY,1,1,0,0,TWO_PI);ctx.stroke();ctx.closePath();return; } else if (spt === "Entangled") { ctx.moveTo(0,0);ctx.lineTo(tsFg,tsFg); } if (spt !== "Markets" && spt !== "Drift" && spt !== "Dust" && spt !== "Rings") { ctx.stroke(); } } function initSun() { return { active:!0, startTime:frameCount, cycleTime:1200, pauseTime:300, startY:random(height/1.5,height/1.05), endY:random(height/1.5,height/1.05) }; } function initComet() { return { active:!0, startTime:frameCount, cycleTime:1000, pauseTime:120, fromLeft:random()<.5, startY:random(height*.2,height*.8), endY:random(height*.3,height*.05), horizontalDir:random()<.5?1:-1 }; } function initMoonbow() { return { active:!0, startTime:frameCount, cycleTime:random(1200,2000), pauseTime:random(150,200), startX:-80, startY:random(height/8,height/2.2), endX:width+80, endY:random(height/8,height/2.2), size:random(25,50) }; } function initNebula() { let iW=random(100,150); return { active:!0, startTime:frameCount, cycleTime:random(1800,2300), pauseTime:random(120,300), startX:random(width), startY:height+80, endX:random(width), endY:-80, nebulaWidth:iW, nebulaHeight:iW/3 }; } function manageEffectLifecycle(effectName, initFn) { if (!effectStates[effectName]) effectStates[effectName] = initFn(); let state = effectStates[effectName], isActive = !1, t = null; if (state.active) { t = (frameCount - state.startTime) / state.cycleTime; if (t >= 1) { state.active = !1; state.pauseStart = frameCount; t = null; } else isActive = !0; } else if (frameCount - state.pauseStart > state.pauseTime) { effectStates[effectName] = initFn(); } return { isActive: isActive, t: t, state: state }; } function drawAdditionalBackground() { ctx.save(); ctx.strokeStyle = 'transparent'; const currentBgTrait = bgTrait; if (currentBgTrait === "Sun") { let eS = manageEffectLifecycle("sun", initSun); if (eS.isActive) { let s=eS.state, t=eS.t, sX=lerp(width+150,-150,t), sY=lerp(s.startY,s.endY,t)-sin(PI*t)*250; let iC=color(bgs.trailColor), oC=color(bgs.trailColor); iC.a=1; oC.a=1; for(let r=40;r>0;r-=4){ let intr=map(r,0,40,1,0), curC=lerpColor(iC,oC,intr); ctx.fillStyle=toCanvasRGBA(curC); ctx.beginPath(); let rad=r*1.75; ctx.ellipse(sX,sY,rad,rad,0,0,TWO_PI); ctx.fill(); } } } else if (currentBgTrait === "Comet") { let eS = manageEffectLifecycle("comet", initComet); if (eS.isActive) { let c=eS.state, t=eS.t, cX=c.fromLeft?lerp(-50,width+50,t):lerp(width+50,-50,t), cY=lerp(c.startY,c.endY,t)-sin(t*PI)*c.endY; let bCometC=color(bgs.trailColor); bCometC.a=1; ctx.fillStyle=toCanvasRGBA(bCometC); ctx.beginPath(); ctx.ellipse(cX,cY,15,15,0,0,TWO_PI); ctx.fill(); let tDX=c.fromLeft?-1:1, tDY=0, tL=15, bTEC=color(...bgs.bgb); for(let i=0;i<tL;i++){ let intr=map(i,0,tL,0,1), tailC=lerpColor(bCometC,bTEC,intr); tailC.a=map(i,0,tL,1,0); let tX=cX+tDX*i,tY=cY+tDY*i,tRad=(30-i*.15)/2; if(tRad<=0)continue; ctx.fillStyle=toCanvasRGBA(tailC);ctx.beginPath();ctx.ellipse(tX,tY,tRad,tRad,0,0,TWO_PI);ctx.fill(); } } } else if (currentBgTrait === "Stars") { effectStates.starRotation += effectStates.starRotationsp; let cx_s=effectStates.starRotationCenter.x, cy_s=effectStates.starRotationCenter.y, theta=effectStates.starRotation; let starC=color(bgs.trailColor); ctx.fillStyle=toCanvasRGBA(starC); for(let s_star of effectStates.stars){ let dX=s_star.x-cx_s,dY=s_star.y-cy_s,rX=dX*cos(theta)-dY*sin(theta)+cx_s,rY=dX*sin(theta)+dY*cos(theta)+cy_s; ctx.beginPath();let rad=s_star.size/2;ctx.ellipse(rX,rY,rad,rad,0,0,TWO_PI);ctx.fill(); } } else if (currentBgTrait === "Moonbow") { let eS = manageEffectLifecycle("moonbow", initMoonbow); if (eS.isActive) { let bh=eS.state, t=eS.t, bX=lerp(width+80,-80,t), bY=lerp(bh.startY,bh.endY,t), pY=bY+sin(PI*t)*100; ctx.save();ctx.translate(bX,pY); let rad=bh.size,c1=color("#f5de10"),c2=color(bgs.trailColor); for(let ym=-rad;ym<=rad;ym++){let intr=map(ym,-rad,rad,0,1),gC=lerpColor(c1,c2,intr);gC.a=map(abs(ym),0,rad,.05,.01);ctx.fillStyle=toCanvasRGBA(gC);let sH=2*sqrt(max(0,rad*rad-ym*ym));if(sH>0)ctx.fillRect(-sH/2,ym,sH,1);} ctx.restore(); } } else if (currentBgTrait === "Nebula") { let eS = manageEffectLifecycle("nebula", initNebula); if (eS.isActive && typeof noise === 'function') { let bh=eS.state, t=eS.t, bX=lerp(bh.startX,bh.endX,t), bY=lerp(bh.startY,bh.endY,t), pX=bX+sin(t*TWO_PI*3)*50; ctx.save();ctx.translate(pX,bY); let nebC=color(...bgs.bgb); ctx.fillStyle=toCanvasRGBA(nebC); let topEdgePts=[]; for(let xn=-bh.nebulaWidth/2;xn<=bh.nebulaWidth/2;xn++){let sH=bh.nebulaHeight*sqrt(max(0,1-(2*xn/bh.nebulaWidth)*(2*xn/bh.nebulaWidth)));if(sH>0){topEdgePts.push({x:xn,y:-sH/2});ctx.fillStyle=toCanvasRGBA(nebC);ctx.fillRect(xn,-sH/2,1,sH);}} if(topEdgePts.length>1){let distArcPts=[];for(let p of topEdgePts){let nV=noise((p.x+1e3)*.01,frameCount*.002);distArcPts.push({x:p.x,y:p.y+map(nV,0,1,-35,35)});} let arcC=color(bgs.trailColor);arcC.a=.8;ctx.strokeStyle=toCanvasRGBA(arcC);ctx.lineCap='round';ctx.lineJoin='round';let minW=1,maxW=3;for(let i=1;i<distArcPts.length;i++){let p1=distArcPts[i-1],p2=distArcPts[i],tArc=(i-.5)/(distArcPts.length-1);ctx.lineWidth=lerp(maxW,minW,abs(tArc-.5)*2);ctx.beginPath();ctx.moveTo(p1.x,p1.y);ctx.lineTo(p2.x,p2.y);ctx.stroke();}} ctx.restore(); } } ctx.restore(); } if (typeof window !== 'undefined') { window.GOM_ART = { initialize: initializeArtwork, render: renderFrame }; } else if (typeof self !== 'undefined') { self.GOM_ART = { initialize: initializeArtwork, render: renderFrame }; } // --- Renderer Setup and Execution --- window.ctx = canvas.getContext('2d'); window.width = canvas.width; window.height = canvas.height; window.frameCount = 0; try { if (window.GOM_ART && typeof window.GOM_ART.initialize === 'function') { window.GOM_ART.initialize(); function animate() { window.frameCount++; window.GOM_ART.render(); requestAnimationFrame(animate); } animate(); } else { throw new Error("GOM_ART object not found after script execution."); } } catch (e) { console.error("Error during animation setup:", e); document.body.innerHTML = `<pre style="color:red;font-family:sans-serif;">Error: ${e.message}</pre>`; } </script> </body> </html>