From 4694661fcd63ba1ad517229ce8acfbf800e9b5e6 Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Tue, 14 Apr 2026 11:37:19 -0700 Subject: [PATCH] =?UTF-8?q?boot-canvas:=20add=20'spring'=20mode=20as=20def?= =?UTF-8?q?ault=20=E2=80=94=20turtle-graphics=20flowers=20with=20chaotic?= =?UTF-8?q?=20MOTD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces 'serious' as the default boot animation. Serious is still available via ?boot=serious. Spring mode draws a field of procedural turtle-graphics flowers (parametric petals, stems, leaves, pollen) over a cream/butter gradient, with the title and MOTD characters each animating independently with per-char font swaps, hue shifts, rotation, and bob/jitter. Co-Authored-By: Claude Opus 4.6 (1M context) --- system/netlify/functions/index.mjs | 81 ++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/system/netlify/functions/index.mjs b/system/netlify/functions/index.mjs index 2c5210de7..6e823b32d 100644 --- a/system/netlify/functions/index.mjs +++ b/system/netlify/functions/index.mjs @@ -1302,8 +1302,8 @@ async function fun(event, context) { var isNotepat=location.hostname==='notepat.com'||location.hostname==='www.notepat.com'||location.pathname==='/notepat'||location.pathname.startsWith('/notepat?')||location.pathname.startsWith('/notepat/'); // Notebook: Python/Jupyter notebook with scientific aesthetic var isNotebook=qs.indexOf('notebook=true')>=0; - // Boot animation mode: 'serious' (clean/refined, default) or 'aesthetic' (VHS/glitch) - var bootTheme=params.get('boot')||'serious';var isSerious=bootTheme==='serious'; + // Boot animation mode: 'spring' (turtle-graphics flowers, default), 'serious' (clean/refined), or 'aesthetic' (VHS/glitch) + var bootTheme=params.get('boot')||'spring';var isSerious=bootTheme==='serious';var isSpring=bootTheme==='spring'; // Density param for scaling (default 1, FF1 uses 8 for 4K) var densityMatch=qs.match(/density=(\d+)/);var densityParam=densityMatch?parseInt(densityMatch[1]):1; var isLightMode=window.matchMedia&&window.matchMedia('(prefers-color-scheme:light)').matches; @@ -1371,6 +1371,23 @@ async function fun(event, context) { var NP_KEYS=[];var NP_PARTICLES=[];var NP_LAST_KEY=0;var NP_KEY_INTERVAL=120; var NP_NOTE_NAMES=['C','D','E','F','G','A','B']; var NP_KEY_COLS=[[255,107,157],[78,205,196],[255,217,61],[149,225,211],[255,154,162],[170,150,218],[112,214,255],[255,183,77]]; + // 🌼 Spring boot animation state — procedural turtle-graphics flowers + var SPRING_FLOWERS=[],SPRING_INIT=false; + var SPRING_FONTS_LIGHT=['serif','monospace','YWFTProcessing-Bold, monospace','cursive','Georgia, serif','Courier New, monospace']; + var SPRING_PETAL_HUES=[48,52,40,330,300,200,160,20,12]; // yellows/pinks/lavenders/sky/mints/peaches + function springInit(S){SPRING_FLOWERS=[];var n=14;for(var i=0;i0.5?1:-1});}SPRING_INIT=true;} + function drawTurtleFlower(ctx,W,H,fl,t,S,isLightMode){var cx=fl.px*W+Math.sin(t*0.6+fl.phase)*fl.sway*4*S;var cy=fl.py*H+Math.sin(t*0.4+fl.phase*1.3)*fl.sway*2*S;var r=fl.r; + // Stem (curved line, drawn turtle-style with short segments) + ctx.save();ctx.strokeStyle=isLightMode?'rgb(70,140,80)':'rgb(140,200,150)';ctx.lineWidth=Math.max(1,1.5*S);ctx.beginPath();var sx0=cx,sy0=cy+r*0.7,segs=12;for(var s=0;s<=segs;s++){var u=s/segs;var sway=Math.sin(u*Math.PI+t*0.5+fl.phase)*r*0.5*u;ctx.lineTo(sx0+sway,sy0+u*r*3.5);}ctx.stroke(); + // Leaf + var lx=sx0+fl.leafSide*r*0.4,ly=sy0+r*1.6;ctx.beginPath();ctx.moveTo(lx,ly);ctx.quadraticCurveTo(lx+fl.leafSide*r*0.8,ly+r*0.2,lx+fl.leafSide*r*1.4,ly-r*0.1);ctx.quadraticCurveTo(lx+fl.leafSide*r*0.6,ly+r*0.4,lx,ly);ctx.fillStyle=isLightMode?'rgba(80,160,90,0.85)':'rgba(150,210,160,0.85)';ctx.fill(); + // Petals (turtle-style: parametric loop r=sin(theta)*len) + for(var p=0;p=charsShown)break;var ch=lineTxt[ci];if(ch===' '){lx2+=lWidths[ci];continue;}var f2=SPRING_FONTS_LIGHT[(ci+ml*3)%SPRING_FONTS_LIGHT.length];var idx=shownSoFar+ci;var bX=Math.sin(t*1.3+idx*0.6)*4*S;var bY=Math.sin(t*1.7+idx*0.4)*5*S;var rot=Math.sin(t*0.7+idx*0.31)*0.22;var sc=1+Math.sin(t*2+idx*0.5)*0.08;var hue=(idx*22+sf*1.5)%360; + x.save();x.translate(lx2+lWidths[ci]/2+bX,startY+ml*lineH+bY);x.rotate(rot);x.scale(sc,sc);x.font='bold '+motdFS+'px '+f2; + // Outline for legibility + x.lineWidth=Math.max(1,1.2*S);x.strokeStyle=isLightMode?'rgba(255,250,220,0.85)':'rgba(40,30,10,0.85)';x.textAlign='center'; + x.globalAlpha=mFade;x.strokeText(ch,0,0); + x.fillStyle='hsl('+hue+','+(isLightMode?75:65)+'%,'+(isLightMode?38:65)+'%)';x.fillText(ch,0,0); + x.restore();lx2+=lWidths[ci];} + shownSoFar+=lineTxt.length; + if(shownSoFar>=charsShown)break;} + x.textAlign='left';x.globalAlpha=1; + if(motdHandle){var label='mood of the day from '+motdHandle;var labelFS=Math.max(4*S,motdFS*0.4);x.font='italic bold '+labelFS+'px serif';var labelW=x.measureText(label).width;x.globalAlpha=mFade*0.6;x.fillStyle=isLightMode?'#8a5a20':'#ffd070';x.fillText(label,(W-labelW)/2,Math.min(H-3*S,startY+mLines.length*lineH+labelFS*1.3));x.globalAlpha=1;}} + // Connection / error overlays (shared with serious) + if(connFlash>0.01){x.globalAlpha=connFlash*0.2;x.fillStyle=isLightMode?'#3a8c3a':'#90ff90';x.fillRect(0,0,W,H);x.globalAlpha=1;} + if(errorMode||errorFlash>0.01){var errElapsed=errorStartTime?(performance.now()-errorStartTime)/1000:0;if(errorMode&&errElapsed>1.0){x.globalAlpha=1;x.fillStyle='rgb(0,0,0)';x.fillRect(0,0,W,H);}else{x.globalCompositeOperation='screen';x.globalAlpha=errorMode?0.4+Math.sin(t*8)*0.2:errorFlash*0.5;x.fillStyle='rgb(255,40,60)';x.fillRect(0,0,W,H);x.globalCompositeOperation='source-over';x.globalAlpha=1;}} + x.globalAlpha=1;requestAnimationFrame(anim);return;} + // Serious mode — clean, refined boot animation (legacy option, ?boot=serious) if(isSerious){var sbg=isLightMode?'#ffffff':'#000000';x.fillStyle=sbg;x.fillRect(0,0,W,H); // Logo with pink bg + chromatic aberration var lS=21*S,lX=5*S,lY=5*S;var logoImg=imgFullLoaded?imgFull:img; -- 2.51.2