asciify-engine 4.2.0 → 4.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/studio.js CHANGED
@@ -1 +1 @@
1
- var t=["ascii","blocks","braille","dots","lines","cross","diagonal","diamond","mixed","pixel","mosaic","lego","voxel","disco","dither","hex","led","cmyk"],e=["none","bayer2","bayer4","bayer8","bayer16","floyd-steinberg","atkinson","stucki","sierra","sierra-lite","burkes","jarvis","halftone","lines","noise","vertical-lines","diagonal-lines","radial"],i=["source-over","multiply","screen","overlay","soft-light","hard-light","color-dodge","color-burn","hue","saturation","color","luminosity"],s={original:[],gray4:["#000000","#555555","#aaaaaa","#ffffff"],gray8:["#000000","#242424","#494949","#6d6d6d","#929292","#b6b6b6","#dbdbdb","#ffffff"],rgb8:["#000000","#ff0000","#00ff00","#0000ff","#ffff00","#ff00ff","#00ffff","#ffffff"],cyberpunk:["#08081a","#3c1670","#ed299a","#29dfef","#fff7bf"],pastel:["#393448","#a5b4d4","#9ed9c6","#e7aac1","#f5e8be"],mono:["#080808","#f2f2e8"],asciify:["#080808","#625b38","#a19558","#e8b900"],gameboy:["#0f380f","#306230","#8bac0f","#9bbc0f"],cga:["#000000","#55ffff","#ff55ff","#ffffff"],pico8:["#000000","#1d2b53","#7e2553","#008751","#ab5236","#5f574f","#c2c3c7","#fff1e8","#ff004d","#ffa300","#ffec27","#00e436","#29adff","#83769c","#ff77a8","#ffccaa"],c64:["#000000","#ffffff","#880000","#aaffee","#cc44cc","#00cc55","#0000aa","#eeee77","#dd8855","#664400","#ff7777","#333333","#777777","#aaff66","#0088ff","#bbbbbb"],amber:["#100b00","#5f3b00","#c98014","#ffd17c"],ice:["#081b29","#25586e","#69a6b3","#d9ece7"],risograph:["#f4ebd0","#f2594b","#2853a5","#29232e"],sepia:["#201b17","#6b5039","#b49868","#f6e6c5"],nord:["#2e3440","#4c566a","#88c0d0","#eceff4"],dusk:["#211b35","#654271","#c3788c","#f5c9a3"],forest:["#101f1b","#355747","#87a16f","#e4e3ba"],ocean:["#071b33","#17577f","#3fa7a4","#d9efde"],newsprint:["#191919","#666666","#aaaaaa","#eee9df"],custom:["#080808","#e8b900"]},a={version:1,aspectRatio:"original",style:"ascii",cellSize:6,charset:" .:-=+*#%@",colorMode:"accent",ink:"#e8b900",crop:{x:.5,y:.5,zoom:1,rotation:0},backdrop:{mode:"solid",color:"#080808",color2:"#393323",blur:12,opacity:1},color:{brightness:0,contrast:1,saturation:1,grayscale:0,tint:"#e8b900",amount:0,blend:"source-over"},dither:{algorithm:"none",palette:"mono",colors:["#080808","#e8b900"],amount:1,scale:2,threshold:.5,motion:"none",speed:1,direction:45,colorSpace:"rgb"},mask:{enabled:!1,invert:!1,shapes:[]},lights:[],effects:{bloom:0,characterBloom:0,grain:0,dust:0,scanlines:0,crt:0,prism:0,rgbSplit:0,sharpen:0,vignette:0,glitch:0,pixelate:0,blur:0,blurType:"gaussian",angle:0,focus:.5,halftone:0},motion:{type:"none",speed:1,amount:1},hover:{effect:"trail",strength:.55,radius:.2,edgeSafe:!1}},r=t=>t&&"object"==typeof t&&!Array.isArray(t)?t:{},o=(t,e,i,s)=>"number"==typeof t&&Number.isFinite(t)?Math.min(s,Math.max(i,t)):e,n=(t,e)=>"string"==typeof t&&/^#[\da-f]{6}$/i.test(t)?t:e,h=(t,e,i)=>e.includes(t)?t:i;function l(l={}){const c=r(l),f=a;if(void 0!==c.version&&1!==c.version)throw new Error("Unsupported studio settings version. Update asciify-engine.");const m=r(c.crop),d=r(c.backdrop),u=r(c.color),g=r(c.dither),p=r(c.mask),M=r(c.effects),x=r(c.motion),v=r(c.hover),b={...f.effects};for(const t of["bloom","characterBloom","grain","dust","scanlines","crt","prism","rgbSplit","sharpen","vignette","glitch","pixelate","halftone"])b[t]=o(M[t],0,0,1);b.blur=o(M.blur,0,0,24),b.blurType=h(M.blurType,["gaussian","directional","radial","progressive"],f.effects.blurType),b.angle=o(M.angle,0,-180,180),b.focus=o(M.focus,.5,0,1);let w=2048;return{version:1,aspectRatio:h(c.aspectRatio,["original","16:9","9:16","1:1","4:3","3:4","21:9"],"original"),style:h(c.style,t,f.style),cellSize:o(c.cellSize,f.cellSize,["pixel","mosaic","lego","voxel","disco","dots","hex","led","cmyk"].includes(String(c.style))?1:3,60),charset:"string"==typeof c.charset&&c.charset.length?Array.from(c.charset).slice(0,128).join(""):f.charset,colorMode:h(c.colorMode,["accent","source","gray"],f.colorMode),ink:n(c.ink,f.ink),crop:{x:o(m.x,.5,0,1),y:o(m.y,.5,0,1),zoom:o(m.zoom,1,1,5),rotation:90*Math.round(o(m.rotation,0,-360,360)/90)},backdrop:{mode:h(d.mode,["solid","transparent","source","blurred","gradient"],f.backdrop.mode),color:n(d.color,f.backdrop.color),color2:n(d.color2,f.backdrop.color2),blur:o(d.blur,12,0,32),opacity:o(d.opacity,1,0,1)},color:{brightness:o(u.brightness,0,-1,1),contrast:o(u.contrast,1,0,3),saturation:o(u.saturation,1,0,3),grayscale:o(u.grayscale,0,0,1),tint:n(u.tint,f.color.tint),amount:o(u.amount,0,0,1),blend:h(u.blend,i,f.color.blend)},dither:{algorithm:h(g.algorithm,e,"none"),palette:"string"==typeof g.palette&&Object.prototype.hasOwnProperty.call(s,g.palette)?g.palette:"mono",colors:Array.isArray(g.colors)&&g.colors.length>=2?g.colors.slice(0,32).map(t=>n(t,"#000000")):[...f.dither.colors],amount:o(g.amount,1,0,2),scale:Math.round(o(g.scale,2,1,12)),threshold:o(g.threshold,.5,0,1),motion:h(g.motion,["none","drift","shimmer"],"none"),speed:o(g.speed,1,.1,3),direction:o(g.direction,45,-180,180),colorSpace:h(g.colorSpace,["rgb","luminance"],"rgb")},mask:{enabled:!0===p.enabled,invert:!0===p.invert,shapes:Array.isArray(p.shapes)?p.shapes.slice(0,64).map(t=>{const e=r(t),i=h(e.kind,["rectangle","ellipse","brush"],"rectangle"),s="brush"===i&&Array.isArray(e.points)?e.points.slice(0,w).map(t=>Array.isArray(t)?[Math.round(1e4*o(t[0],0,0,1))/1e4,Math.round(1e4*o(t[1],0,0,1))/1e4]:[0,0]):[];return w-=s.length,{kind:i,x:o(e.x,0,0,1),y:o(e.y,0,0,1),width:o(e.width,.5,0,1),height:o(e.height,.5,0,1),size:o(e.size,.05,.005,.5),points:s}}):[]},lights:Array.isArray(c.lights)?c.lights.slice(0,4).map(t=>{const e=r(t);return{x:o(e.x,.5,0,1),y:o(e.y,.5,0,1),radius:o(e.radius,.3,.02,1),intensity:o(e.intensity,.5,0,1),color:n(e.color,"#ffffff")}}):[],effects:b,motion:{type:h(x.type,["none","caustics","current","reform"],"none"),speed:o(x.speed,1,.1,3),amount:o(x.amount,1,0,2)},hover:{effect:h(v.effect,["none","trail","water","contour","dissolve","silk","vortex"],"trail"),strength:o(v.strength,.55,0,1),radius:o(v.radius,.2,.1,1),edgeSafe:!0===v.edgeSafe}}}function c(t){return JSON.stringify(l(t))}function f(t){if(t.length>1e5)throw new Error("Settings exceed 100 KB.");return l(JSON.parse(t))}function m(t,e,i,s,a){const r=Math.abs(a.rotation/90)%2==1,o=r?e:t,n=r?t:e,h=Math.max(i/o,s/n)*a.zoom;return{width:o*h,height:n*h,x:(i-o*h)*a.x,y:(s-n*h)*a.y,scale:h,rotated:r}}function d(t,e,i){if(Math.abs(i.crop.rotation/90)%2==1&&([t,e]=[e,t]),"original"===i.aspectRatio)return[t,e];const[s,a]=i.aspectRatio.split(":").map(Number);return t/e>s/a?[Math.round(e*s/a),e]:[t,Math.round(t*a/s)]}function u(t,e){const i={...t,...e};for(const s of["crop","backdrop","color","dither","mask","effects","motion","hover"])e[s]&&Object.assign(i,{[s]:{...t[s],...e[s]}});return l(i)}function g(t,e,i,s=12e3,a=1){const r=Math.max(2,Number.isFinite(t)?t:960),o=Math.max(2,Number.isFinite(e)?e:540),n=Math.max(1,Math.floor(Number.isFinite(s)?s:12e3)),h=Math.max(.01,Number.isFinite(a)?a:1),l=["dither","pixel","mosaic","lego","voxel","disco","dots","hex","led","cmyk"].includes(i.style)?1:1.65;let c=Math.max(1,Math.ceil(Math.sqrt(r*o/(n*l))/h));for(;Math.ceil(r/(c*h))*Math.ceil(o/(c*h*l))>n;)c++;const f="dither"===i.style?i.dither.scale:i.cellSize,m=Math.max(c,f)*h;return{cell:m,rowHeight:m*l,columns:Math.ceil(r/m),rows:Math.ceil(o/(m*l)),minimum:c,limited:f<c}}var p=[["ascii","Signature ASCII","Brand ink and clean characters.",{}],["print","Newsprint","Fine diffusion on warm paper.",{style:"dither",dither:{algorithm:"floyd-steinberg",palette:"newsprint"}}],["handheld","Pocket camera","Four-tone ordered dither.",{style:"dither",dither:{algorithm:"bayer4",palette:"gameboy",scale:3}}],["riso","Two inks","Limited-color print texture.",{style:"dither",dither:{algorithm:"atkinson",palette:"risograph",scale:2}}],["night","Night signal","Gray characters and optical fringe.",{ink:"#888888",effects:{prism:.7,scanlines:.2,vignette:.3}}],["terminal","Phosphor","Green glyphs with a soft glow.",{ink:"#83efad",effects:{bloom:.4,scanlines:.35,crt:.2}}],["mosaic","Mosaic study","Separated color tiles.",{style:"mosaic",cellSize:14,colorMode:"source"}],["bricks","Brickwork","Colorful studded cells.",{style:"lego",cellSize:18,colorMode:"source"}],["voxel","Little volumes","Three-face isometric tiles.",{style:"voxel",cellSize:22,colorMode:"source"}],["disco","Mirror room","Faceted tiles and warm light.",{style:"disco",cellSize:18,colorMode:"source",lights:[{x:.35,y:.25,radius:.25,intensity:.45,color:"#fff2d4"}]}],["wallpaper","Wallpaper","Living ASCII over a softened source.",{backdrop:{mode:"blurred",blur:18,opacity:.55},ink:"#f2e9c9",motion:{type:"caustics",speed:.5}}],["blueprint","Blueprint","Linework in a cool monochrome.",{style:"lines",ink:"#9bccdf",backdrop:{mode:"solid",color:"#0a1e30"}}]].map(([t,e,i,s])=>({id:t,name:e,description:i,settings:l(s)}));function M(t){const e=p.find(e=>e.id===t);if(!e)throw new Error("Unknown studio look.");return l(e.settings)}function x(t,e=Math.random){const i=p.filter(e=>e.settings.style!==t.style||e.settings.ink!==t.ink),s=M(i[Math.min(i.length-1,Math.max(0,Math.floor(e()*i.length)))].id);return s.aspectRatio=t.aspectRatio,s.crop={...t.crop},s.mask=l(t).mask,s}var v=[{value:"none",mode:"none",label:"Off",description:"Keep the image still. Hover stays available."},{value:"caustics",mode:"caustics",label:"Caustics",description:"Soft ribbons of light travel across a stationary character grid."},{value:"current",mode:"current",label:"Slow Current",description:"A gentle flow through the image, with steady edges."},{value:"reform",mode:"reform",label:"Reveal & Reform",description:"Disperse, gather, then hold the complete image."}],b=t=>Math.max(0,Math.min(1,t)),w=(t,e,i)=>{const s=b((i-t)/(e-t));return s*s*(3-2*s)},y=class{constructor(){this.last=null,this.mode="none",this.phase=0}update(t,e,i=1){return Number.isFinite(t)?((e!==this.mode||null!==this.last&&t<this.last)&&(this.mode=e,this.phase=0,this.last=t),null!==this.last&&(this.phase+=Math.max(0,t-this.last)*Math.max(.1,Math.min(3,Number.isFinite(i)?i:1))),this.last=t,this.phase):this.phase}};function S(t,e,i,s,a){if(a[0]=a[1]=a[2]=0,a[3]=1,"none"===t)return a;const r=s*Math.PI/6,o=w(0,.12,Math.min(e,1-e,i,1-i));if("caustics"===t){const t=Math.sin(9*e+5*i+r)+Math.sin(8*i-3*e-r),s=Math.pow(Math.max(0,1-.72*Math.abs(t)),3);a[2]=(.22*s-.045)*o}else if("current"===t)a[0]=Math.sin(9*i+r)*Math.cos(7*e-r)*10*o,a[1]=Math.cos(8*e+r)*Math.sin(6*i-r)*7*o;else if("reform"===t){const t=(s%12+12)%12,n=((t,e)=>{const i=(.06711056*t+.00583715*e)%1*52.9829189;return i-Math.floor(i)})(Math.floor(160*e),Math.floor(100*i)),h=.65*e+.2*i+.15*n+.08*Math.sin(8*e-5*i),l=w(1.8+1.2*h,3.8+1.2*h,t)*(1-w(5+1.7*h,7.2+1.7*h,t));a[0]=Math.sin(9*i+r)*l*(1-l)*24*o,a[1]=-l*(1-l)*(8+12*n)*o,a[2]=.45*-l,a[3]=1-.9*l}return a}function k(t,e){const i=Math.max(0,Math.min(2,Number.isFinite(e)?e:1));return t[0]*=i,t[1]*=i,t[2]*=i,t[3]=b(1+(t[3]-1)*i),t}var E=(t,e)=>{const i=43758.5453*Math.sin(127.1*t+311.7*e);return i-Math.floor(i)};function A(t,e,i){let s=0;for(let a=1;a<i;a*=2){const i=(Math.floor(t/a)%2+2)%2;s=4*s+[[0,2],[3,1]][(Math.floor(e/a)%2+2)%2][i]}return(s+.5)/(i*i)-.5}var F={"floyd-steinberg":[[1,0,7/16],[-1,1,3/16],[0,1,5/16],[1,1,1/16]],atkinson:[[1,0,.125],[2,0,.125],[-1,1,.125],[0,1,.125],[1,1,.125],[0,2,.125]],stucki:[[1,0,8/42],[2,0,4/42],[-2,1,2/42],[-1,1,4/42],[0,1,8/42],[1,1,4/42],[2,1,2/42],[-2,2,1/42],[-1,2,2/42],[0,2,4/42],[1,2,2/42],[2,2,1/42]],sierra:[[1,0,5/32],[2,0,3/32],[-2,1,2/32],[-1,1,4/32],[0,1,5/32],[1,1,4/32],[2,1,2/32],[-1,2,2/32],[0,2,3/32],[1,2,2/32]],"sierra-lite":[[1,0,.5],[-1,1,.25],[0,1,.25]],burkes:[[1,0,.25],[2,0,4/32],[-2,1,2/32],[-1,1,4/32],[0,1,.25],[1,1,4/32],[2,1,2/32]],jarvis:[[1,0,7/48],[2,0,5/48],[-2,1,3/48],[-1,1,5/48],[0,1,7/48],[1,1,5/48],[2,1,3/48],[-2,2,1/48],[-1,2,3/48],[0,2,5/48],[1,2,3/48],[2,2,1/48]]},T=t=>[parseInt(t.slice(1,3),16),parseInt(t.slice(3,5),16),parseInt(t.slice(5,7),16)];function I(t,e,i){const s=Math.min(t*Math.max(1,i),(1-t)*Math.max(1,i),e*Math.max(1,1/i),(1-e)*Math.max(1,1/i)),a=Math.max(0,Math.min(1,(s-.035)/.105));return a*a*a*(a*(6*a-15)+10)}var P=class{constructor(t,e){this.edgeSafe=!1,this.previous=null,this.peak=0,this.mode="dissolve",this.width=t,this.height=e;const i=t*e;this.pixels=new Uint8Array(4*i),this.ink=new Float32Array(i),this.targetX=new Float32Array(i),this.targetY=new Float32Array(i),this.offsetX=new Float32Array(i),this.offsetY=new Float32Array(i),this.edge=new Float32Array(i);for(let i=0;i<e;i++)for(let s=0;s<t;s++)this.edge[i*t+s]=I(s/(t-1),i/(e-1),t/e);this.clear()}get active(){return this.peak>2e-4}setMode(t){t!==this.mode&&(this.mode=t,this.clear())}clear(){for(const t of[this.ink,this.targetX,this.targetY,this.offsetX,this.offsetY,this.pixels])t.fill(0);if(this.previous=null,this.peak=0,"dissolve"!==this.mode)for(let t=0;t<this.pixels.length;t+=4)this.pixels[t]=this.pixels[t+2]=128}leave(){this.previous=null}move(t,e,i){if(!Number.isFinite(t+e+i))return;t=Math.max(0,Math.min(1,t)),e=Math.max(0,Math.min(1,e));const s=this.previous;this.previous={x:t,y:e};const a=this.width,r=this.height,o=Math.max(1,a/r),n=Math.max(1,r/a),h=s?(t-s.x)*o:0,l=s?(e-s.y)*n:0,c=Math.hypot(h,l);if(!s&&"dissolve"!==this.mode)return;if(s&&c<1e-4)return;const f=.035+.11*Math.max(.1,Math.min(1,i)),m=Math.min(128,Math.max(1,Math.ceil(c/(.45*f)))),d=c?h/c:1,u=c?l/c:0,g=Math.min(1,c/(f*m)*2.2);for(let i=0;i<m;i++){const h=(i+.5)/m,l=s?s.x+(t-s.x)*h:t,c=s?s.y+(e-s.y)*h:e,p=Math.max(0,Math.floor((l-2.5*f/o)*(a-1))),M=Math.min(a-1,Math.ceil((l+2.5*f/o)*(a-1))),x=Math.max(0,Math.floor((c-2.5*f/n)*(r-1))),v=Math.min(r-1,Math.ceil((c+2.5*f/n)*(r-1)));for(let t=x;t<=v;t++)for(let e=p;e<=M;e++){const i=(e/(a-1)-l)*o/f,s=(t/(r-1)-c)*n/f,h=Math.exp(1.5*-(i*i+s*s)),m=t*a+e;if("dissolve"===this.mode)this.ink[m]=Math.max(this.ink[m],h);else{const t=(-i*u+s*d)*h*2.8,e="silk"===this.mode?d*t:-s*h*2.5,a="silk"===this.mode?u*t:i*h*2.5;this.targetX[m]=Math.max(-1,Math.min(1,this.targetX[m]+e*g)),this.targetY[m]=Math.max(-1,Math.min(1,this.targetY[m]+a*g))}}}this.peak=1}step(t){if(!this.active||!Number.isFinite(t))return;const e=Math.max(0,Math.min(.05,t)),i=Math.exp(-("dissolve"===this.mode?2.15:3.2)*e),s=1-Math.exp(-22*e);let a=0;for(let t=0;t<this.ink.length;t++)if("dissolve"===this.mode){this.ink[t]*=i,a=Math.max(a,this.ink[t]);const e=Math.round(65535*this.ink[t]);this.pixels[4*t]=e>>>8,this.pixels[4*t+1]=255&e}else{this.targetX[t]*=i,this.targetY[t]*=i,this.offsetX[t]+=(this.targetX[t]-this.offsetX[t])*s,this.offsetY[t]+=(this.targetY[t]-this.offsetY[t])*s,a=Math.max(a,Math.abs(this.offsetX[t]),Math.abs(this.offsetY[t]),Math.abs(this.targetX[t]),Math.abs(this.targetY[t]));const e=Math.round(this.offsetX[t]*(this.edgeSafe?this.edge[t]:1)*32767+32768),r=Math.round(this.offsetY[t]*(this.edgeSafe?this.edge[t]:1)*32767+32768);this.pixels[4*t]=e>>>8,this.pixels[4*t+1]=255&e,this.pixels[4*t+2]=r>>>8,this.pixels[4*t+3]=255&r}this.peak=a,this.active||this.clear()}sample(t,e,i){const s=Math.max(0,Math.min(this.width-1.001,t*(this.width-1))),a=Math.max(0,Math.min(this.height-1.001,e*(this.height-1))),r=Math.floor(s),o=Math.floor(a),n=s-r,h=a-o,l=o*this.width+r,c=l+this.width,f=t=>(t[l]*(1-n)+t[l+1]*n)*(1-h)+(t[c]*(1-n)+t[c+1]*n)*h;i[0]=f(this.offsetX)*(this.edgeSafe?I(t,e,this.width/this.height):1)*.08,i[1]=f(this.offsetY)*(this.edgeSafe?I(t,e,this.width/this.height):1)*.08,i[2]="dissolve"===this.mode?6*-f(this.ink):0}},R=class{constructor(t,e){this.rings=[],this.previous=null,this.elapsed=1,this.echoPeak=0,this.width=t,this.height=e,this.pixels=new Uint8Array(t*e*4),this.echo=new Float32Array(t*e)}get active(){return this.rings.length>0||this.echoPeak>2e-4}clear(){this.rings=[],this.previous=null,this.elapsed=1,this.pixels.fill(0),this.echo.fill(0),this.echoPeak=0}leave(){this.previous=null}move(t,e,i){const s=Math.max(1,this.width/this.height),a=Math.max(1,this.height/this.width);this.previous&&(Math.hypot((t-this.previous.x)*s,(e-this.previous.y)*a)<.065||this.elapsed<.3)||4!==this.rings.length&&(this.previous={x:t,y:e},this.elapsed=0,this.rings.push({x:t,y:e,age:0,radius:i}))}ringSample(t,e){const i=Math.max(1,this.width/this.height),s=Math.max(1,this.height/this.width);let a=0;for(const r of this.rings){const o=.015+r.age*(.15+.22*r.radius),n=.009+.014*r.radius,h=(Math.hypot((t-r.x)*i,(e-r.y)*s)-o)/n,l=Math.sin(Math.min(1,r.age/.07)*Math.PI/2)*Math.max(0,1-r.age/1.35)**2;a=Math.max(a,Math.exp(-h*h)*l)}return a}sample(t,e){const i=Math.max(0,Math.min(this.width-1,Math.round(t*(this.width-1)))),s=Math.max(0,Math.min(this.height-1,Math.round(e*(this.height-1))));return Math.max(this.ringSample(t,e),this.echo[s*this.width+i])}step(t){if(!Number.isFinite(t))return;const e=Math.max(0,Math.min(.05,t));this.elapsed+=e;for(const t of this.rings)t.age+=e;if(this.rings=this.rings.filter(t=>t.age<1.35),!this.active)return void this.pixels.fill(0);const i=Math.exp(-5.5*e);let s=0;for(let t=0;t<this.height;t++)for(let e=0;e<this.width;e++){const a=t*this.width+e;this.echo[a]=Math.max(this.ringSample(e/(this.width-1),t/(this.height-1)),this.echo[a]*i),s=Math.max(s,this.echo[a]);const r=Math.round(65535*this.echo[a]),o=4*a;this.pixels[o]=r>>>8,this.pixels[o+1]=255&r}this.echoPeak=s,this.active||(this.echo.fill(0),this.pixels.fill(0))}},C=class{constructor(t,e){this.previous=null,this.peak=0,this.width=t,this.height=e;const i=t*e;this.pixels=new Uint8Array(4*i),this.signal=new Float32Array(i),this.offsetX=new Float32Array(i),this.offsetY=new Float32Array(i),this.driftX=new Float32Array(i),this.driftY=new Float32Array(i),this.u=new Float32Array(i),this.v=new Float32Array(i),this.ink=new Float32Array(i),this.a=new Float32Array(i),this.b=new Float32Array(i),this.c=new Float32Array(i),this.pressure=new Float32Array(i),this.pressureNext=new Float32Array(i),this.divergence=new Float32Array(i),this.curl=new Float32Array(i)}get active(){return this.peak>2e-4}clear(){for(const t of[this.u,this.v,this.ink,this.a,this.b,this.c,this.pressure,this.pressureNext,this.divergence,this.curl,this.signal,this.pixels,this.offsetX,this.offsetY,this.driftX,this.driftY])t.fill(0);this.previous=null,this.peak=0}leave(){this.previous=null}move(t,e,i,s=performance.now()){if(!Number.isFinite(t+e+i+s))return;t=Math.max(0,Math.min(1,t)),e=Math.max(0,Math.min(1,e)),i=Math.max(.1,Math.min(1,i));const a=this.previous;if(this.previous={x:t,y:e,time:s},!a)return;const r=(t-a.x)*(this.width-1),o=(e-a.y)*(this.height-1),n=Math.hypot(r,o);if(n<.001)return;const h=Math.min(this.width,this.height),l=s>a.time?Math.max(1/240,(s-a.time)/1e3):1/60,c=Math.min(12,n/h/l),f=1-Math.exp(-c/2.2),m=h*(.022+.075*i)*(.65+.95*f),d=Math.min(128,Math.max(1,Math.ceil(n/(.4*m)))),u=n/m*(.32+.22*f)/d,g=r/n,p=o/n,M=-p,x=g,v=n*(2+18*f)/d;for(let i=0;i<d;i++){const s=(i+.5)/d,r=(a.x+(t-a.x)*s)*(this.width-1),o=(a.y+(e-a.y)*s)*(this.height-1),n=Math.max(1,Math.floor(r-3*m)),h=Math.min(this.width-2,Math.ceil(r+3*m)),l=Math.max(1,Math.floor(o-3*m)),c=Math.min(this.height-2,Math.ceil(o+3*m));for(let t=l;t<=c;t++)for(let e=n;e<=h;e++){const i=((e-r)*g+(t-o)*p)/m,s=((e-r)*M+(t-o)*x)/m,a=Math.exp(-i*i*.65-s*s*1.7),n=t*this.width+e;this.ink[n]=1-(1-this.ink[n])*Math.exp(-a*u);const h=1-s*s*.55,l=i*s*.7;this.u[n]=Math.max(-80,Math.min(80,this.u[n]+(g*h+M*l)*a*v)),this.v[n]=Math.max(-80,Math.min(80,this.v[n]+(p*h+x*l)*a*v))}}this.peak=1}read(t,e,i){const s=0|(e=Math.max(.5,Math.min(this.width-1.5,e))),a=0|(i=Math.max(.5,Math.min(this.height-1.5,i))),r=e-s,o=i-a,n=a*this.width+s;return(t[n]*(1-r)+t[n+1]*r)*(1-o)+(t[n+this.width]*(1-r)+t[n+this.width+1]*r)*o}sample(t,e){return this.read(this.signal,t*(this.width-1),e*(this.height-1))}displacement(t,e,i){i[0]=this.read(this.offsetX,t*(this.width-1),e*(this.height-1)),i[1]=this.read(this.offsetY,t*(this.width-1),e*(this.height-1))}step(t){if(!this.active||!Number.isFinite(t))return;let e=Math.max(0,Math.min(.05,t));const i=this.width,s=this.height;for(;e>1e-7;){const t=Math.min(1/60,e);e-=t;const a=Math.exp(-1.9*t),r=Math.exp(-3.2*t);for(let e=1;e<s-1;e++)for(let o=1;o<i-1;o++){const n=e*i+o,h=Math.max(.5,Math.min(i-1.5,o-this.u[n]*t)),l=Math.max(.5,Math.min(s-1.5,e-this.v[n]*t)),c=0|h,f=0|l,m=h-c,d=l-f,u=f*i+c,g=(1-m)*(1-d),p=m*(1-d),M=(1-m)*d,x=m*d;this.a[n]=(this.u[u]*g+this.u[u+1]*p+this.u[u+i]*M+this.u[u+i+1]*x)*a,this.b[n]=(this.v[u]*g+this.v[u+1]*p+this.v[u+i]*M+this.v[u+i+1]*x)*a,this.c[n]=(this.ink[u]*g+this.ink[u+1]*p+this.ink[u+i]*M+this.ink[u+i+1]*x)*r}[this.u,this.a]=[this.a,this.u],[this.v,this.b]=[this.b,this.v],[this.ink,this.c]=[this.c,this.ink];for(let t=1;t<s-1;t++)for(let e=1;e<i-1;e++){const s=t*i+e;this.curl[s]=.5*(this.v[s+1]-this.v[s-1]-this.u[s+i]+this.u[s-i])}for(let e=1;e<s-1;e++)for(let s=1;s<i-1;s++){const a=e*i+s,r=Math.abs(this.curl[a+1])-Math.abs(this.curl[a-1]),o=Math.abs(this.curl[a+i])-Math.abs(this.curl[a-i]),n=Math.hypot(r,o)+1e-4,h=this.curl[a]*t*10,l=Math.hypot(this.u[a],this.v[a]);this.u[a]+=o/n*h,this.v[a]-=r/n*h;const c=Math.min(1,l/(Math.hypot(this.u[a],this.v[a])+1e-6));this.u[a]*=c,this.v[a]*=c,this.divergence[a]=-.5*(this.u[a+1]-this.u[a-1]+this.v[a+i]-this.v[a-i])}this.pressure.fill(0);for(let t=0;t<4;t++){for(let t=1;t<s-1;t++)for(let e=1;e<i-1;e++){const s=t*i+e;this.pressureNext[s]=.25*(this.divergence[s]+this.pressure[s-1]+this.pressure[s+1]+this.pressure[s-i]+this.pressure[s+i])}[this.pressure,this.pressureNext]=[this.pressureNext,this.pressure]}let o=0;for(let e=1;e<s-1;e++)for(let s=1;s<i-1;s++){const a=e*i+s;this.u[a]=Math.max(-80,Math.min(80,this.u[a]-.5*(this.pressure[a+1]-this.pressure[a-1]))),this.v[a]=Math.max(-80,Math.min(80,this.v[a]-.5*(this.pressure[a+i]-this.pressure[a-i]))),this.driftX[a]+=(3.6*this.u[a]-58*this.offsetX[a]-12*this.driftX[a])*t,this.driftY[a]+=(3.6*this.v[a]-58*this.offsetY[a]-12*this.driftY[a])*t,this.offsetX[a]=Math.max(-2,Math.min(2,this.offsetX[a]+this.driftX[a]*t)),this.offsetY[a]=Math.max(-2,Math.min(2,this.offsetY[a]+this.driftY[a]*t)),this.signal[a]=1-Math.exp(.06*-Math.hypot(this.u[a],this.v[a])-.16*this.ink[a]),o=Math.max(o,this.signal[a])}this.peak=o}if(this.active)for(let t=0;t<this.ink.length;t++){const e=Math.round(65535*Math.min(1,this.signal[t]));this.pixels[4*t]=e>>>8,this.pixels[4*t+1]=255&e,this.pixels[4*t+2]=Math.round(128+63.5*this.offsetX[t]),this.pixels[4*t+3]=Math.round(128+63.5*this.offsetY[t])}else this.clear()}};function L(t,e){const i=Math.max(0,Math.min(1,e/.38));return t+(1-2*t)*(i*i*(3-2*i))}var N=class{constructor(t=1){this.mode="water",this.amount=.55,this.radius=.2,this.lensX=.5,this.lensY=.5,this.lensStrength=0,this.previous=null,this.energy=0,this.pending=!1,this.remainder=0,t=Math.max(.25,Math.min(4,t)),this.columns=Math.round(t>=1?128:128*t),this.rows=Math.round(t>=1?128/t:128),this.xScale=Math.max(1,t),this.yScale=Math.max(1,1/t),this.heights=new Float32Array(this.columns*this.rows),this.velocity=new Float32Array(this.heights.length),this.next=new Float32Array(this.heights.length),this.normals=new Float32Array(2*this.heights.length),this.edgeMask=new Float32Array(this.heights.length);for(let e=0;e<this.rows;e++)for(let i=0;i<this.columns;i++)this.edgeMask[e*this.columns+i]=I(i/(this.columns-1),e/(this.rows-1),t);this.refraction={width:this.columns,height:this.rows,pixels:new Uint8Array(4*this.heights.length),strength:30,mode:"water",focus:[.5,.5,0,.2]},this.waterPixels=this.refraction.pixels,this.trail=new C(this.columns,this.rows),this.contour=new R(this.columns,this.rows),this.afterimage=new P(this.columns,this.rows),this.clear()}get invertsDensity(){return"trail"===this.mode}get densityTrail(){return["trail","contour","dissolve"].includes(this.mode)}get hasRefraction(){return this.active||this.lensStrength>15e-5}get active(){return["dissolve","silk","vortex"].includes(this.mode)?this.afterimage.active:"contour"===this.mode?this.contour.active:"trail"===this.mode?this.trail.active:this.pending||this.energy>15e-5}setMode(t){t!==this.mode&&(this.clear(),this.mode=t,this.refraction.mode=t,"dissolve"!==t&&"silk"!==t&&"vortex"!==t||this.afterimage.setMode(t),this.refraction.pixels="trail"===t?this.trail.pixels:"contour"===t?this.contour.pixels:["dissolve","silk","vortex"].includes(t)?this.afterimage.pixels:this.waterPixels,this.refraction.strength="water"===t?30*this.amount/.55:"silk"===t?28*this.amount:"vortex"===t?38*this.amount:0)}configure(t,e=.55,i=.2,s=!1){this.setMode(t),this.refraction.edgeSafe=s,this.afterimage.edgeSafe=s,this.amount=Number.isFinite(e)?Math.max(0,Math.min(1,e)):.55,this.radius=Number.isFinite(i)?Math.max(.1,Math.min(1,i)):.2,this.refraction.strength="water"===t?30*this.amount/.55:"silk"===t?28*this.amount:"vortex"===t?38*this.amount:0,this.refraction.focus[2]=this.densityTrail?this.amount:this.lensStrength*this.amount,this.refraction.focus[3]=this.radius}move(t,e,i){if("none"===this.mode||0===this.amount)return;if(!Number.isFinite(t+e))return;if(t=Math.max(0,Math.min(1,t)),e=Math.max(0,Math.min(1,e)),["dissolve","silk","vortex"].includes(this.mode))return void this.afterimage.move(t,e,this.radius);if("contour"===this.mode)return void this.contour.move(t,e,this.radius);if("trail"===this.mode)return void this.trail.move(t,e,this.radius,i);if("water"!==this.mode)return!this.previous&&this.lensStrength<.001&&(this.lensX=t,this.lensY=e),this.previous={x:t,y:e},void(this.pending=!0);if(!this.previous)return void(this.previous={x:t,y:e});const s=this.previous;this.previous={x:t,y:e};const a=(t-s.x)*this.xScale,r=(e-s.y)*this.yScale,o=Math.hypot(a,r);if(o<1e-5)return;const n=.035+.1*this.radius,h=3*n,l=Math.min(192,Math.max(1,Math.ceil(o/(.5*n)))),c=95*Math.min(o,1)/l;for(let i=0;i<l;i++){const a=(i+.5)/l,r=s.x+(t-s.x)*a,o=s.y+(e-s.y)*a,f=Math.max(1,Math.floor((r-h/this.xScale)*(this.columns-1))),m=Math.min(this.columns-2,Math.ceil((r+h/this.xScale)*(this.columns-1))),d=Math.max(1,Math.floor((o-h/this.yScale)*(this.rows-1))),u=Math.min(this.rows-2,Math.ceil((o+h/this.yScale)*(this.rows-1)));for(let t=d;t<=u;t++)for(let e=f;e<=m;e++){const i=(e/(this.columns-1)-r)*this.xScale,s=(t/(this.rows-1)-o)*this.yScale,a=Math.exp(-(i*i+s*s)/(n*n))*c,h=t*this.columns+e;this.velocity[h]=Math.max(-12,this.velocity[h]-a)}}this.pending=!0}leave(){this.afterimage.leave(),this.contour.leave(),this.trail.leave(),this.previous=null,"water"!==this.mode&&this.lensStrength&&(this.pending=!0)}clear(){this.trail.clear(),this.contour.clear(),this.afterimage.clear(),this.heights.fill(0),this.velocity.fill(0),this.next.fill(0),this.normals.fill(0),this.refraction.focus[2]=0,this.previous=null,this.lensStrength=0,this.energy=0,this.pending=!1,this.remainder=0;for(let t=0;t<this.waterPixels.length;t+=4)this.waterPixels[t]=this.waterPixels[t+2]=128,this.waterPixels[t+1]=this.waterPixels[t+3]=0}step(t){if(!this.active||!Number.isFinite(t))return;if(["dissolve","silk","vortex"].includes(this.mode))return void this.afterimage.step(t);if("contour"===this.mode)return void this.contour.step(t);if("trail"===this.mode)return void this.trail.step(t);if("water"!==this.mode)return void this.stepLight(Math.max(0,Math.min(.05,t)));this.remainder+=Math.max(0,Math.min(.05,t));const e=1/120,i=Math.exp(-3.8*e),s=.3*Math.min(this.columns,this.rows);for(;this.remainder+1e-8>=e;){this.remainder-=e;let t=0;for(let a=1;a<this.rows-1;a++)for(let r=1;r<this.columns-1;r++){const o=a*this.columns+r,n=this.heights[o],h=this.heights[o-1]+this.heights[o+1]+this.heights[o-this.columns]+this.heights[o+this.columns]-4*n,l=Math.min(r,a,this.columns-1-r,this.rows-1-a),c=l<5?.88+.024*l:1,f=(this.velocity[o]+(h*s*s-8*n)*e)*i*c;this.velocity[o]=f,this.next[o]=(n+f*e)*c,t=Math.max(t,Math.abs(this.next[o]),.1*Math.abs(f))}this.heights.set(this.next),this.energy=t,this.pending=!1}if(!this.active)return void this.clear();const a=.18*Math.min(this.columns,this.rows);for(let t=0;t<this.rows;t++)for(let e=0;e<this.columns;e++){const i=t*this.columns+e,s=(this.heights[t*this.columns+Math.max(0,e-1)]-this.heights[t*this.columns+Math.min(this.columns-1,e+1)])*a,r=(this.heights[Math.max(0,t-1)*this.columns+e]-this.heights[Math.min(this.rows-1,t+1)*this.columns+e])*a,o=Math.tanh(s)*(this.refraction.edgeSafe?this.edgeMask[i]:1),n=Math.tanh(r)*(this.refraction.edgeSafe?this.edgeMask[i]:1);this.normals[2*i]=o,this.normals[2*i+1]=n;const h=Math.round(32767*o+32768),l=Math.round(32767*n+32768);this.refraction.pixels[4*i]=h>>>8,this.refraction.pixels[4*i+1]=255&h,this.refraction.pixels[4*i+2]=l>>>8,this.refraction.pixels[4*i+3]=255&l}}stepLight(t){const e=this.previous,i=1-Math.exp(-30*t),s=1-Math.exp(-12*t);e&&(this.lensX+=(e.x-this.lensX)*i,this.lensY+=(e.y-this.lensY)*i),this.lensStrength+=((e?1:0)-this.lensStrength)*s,this.energy=Math.max(e?Math.abs(e.x-this.lensX)+Math.abs(e.y-this.lensY):0,Math.abs((e?1:0)-this.lensStrength)),this.pending=!1,e||this.active?this.refraction.focus=[this.lensX,this.lensY,this.lensStrength*this.amount,this.radius]:this.clear()}sample(t,e,i){if(["dissolve","silk","vortex"].includes(this.mode))return this.afterimage.sample(t,e,i),i[0]*=this.amount,i[1]*=this.amount,void(i[2]*=this.amount);if("contour"===this.mode)return i[0]=i[1]=0,void(i[2]=this.contour.sample(t,e)*this.amount*3);if("trail"===this.mode)return i[0]=i[1]=0,void(i[2]=this.trail.sample(t,e)*this.amount*3);const s=Math.max(0,Math.min(this.columns-1.001,t*(this.columns-1))),a=Math.max(0,Math.min(this.rows-1.001,e*(this.rows-1))),r=Math.floor(s),o=Math.floor(a),n=s-r,h=a-o,l=2*(o*this.columns+r),c=l+2*this.columns;for(let t=0;t<2;t++)i[t]=.08*((this.normals[l+t]*(1-n)+this.normals[l+2+t]*n)*(1-h)+(this.normals[c+t]*(1-n)+this.normals[c+2+t]*n)*h);i[0]*=this.amount/.55,i[1]*=this.amount/.55,i[2]="light"===this.mode||"scan"===this.mode?function(t,e,i,s){const[a,r,o,n]=i.focus,h=(t-a)*Math.max(1,s)/n,l=(e-r)*Math.max(1,1/s)/n;return"light"===i.mode?Math.exp(-3*(h*h+l*l))*o:"scan"===i.mode?(1.3*Math.exp(-h*h*110)-.3*Math.exp(-h*h*12))*Math.exp(-l*l*1.8)*o:0}(t,e,this.refraction,this.xScale/this.yScale):0}};function D(t){return"videoWidth"in t?[t.videoWidth,t.videoHeight]:"naturalWidth"in t?[t.naturalWidth,t.naturalHeight]:[t.width,t.height]}var U=(t=!1)=>{const e=document.createElement("canvas"),i=e.getContext("2d",{willReadFrequently:t});if(!i)throw new Error("Canvas 2D is unavailable.");return{canvas:e,ctx:i}},X=(t,e,i)=>{t.width===e&&t.height===i||(t.width=e,t.height=i)},B={blocks:" ░▒▓█",braille:" ⠁⠃⠇⡇⣇⣧⣷⣿",lines:" -═╪╬",cross:" ·+xX#",diagonal:" /╱╳",diamond:" ·◇◈◆",mixed:" .:+/◇#@"};function O(t,e={},i={}){const a=t.getContext("2d");if(!a)throw new Error("Canvas is already owned by another renderer.");const r=U(),o=U(!0),n=U(),h=U(),c=U(),f=U(),d=U(),u=U(),p=U(),M=U();let x,v=0,b=l(e),w=0,y="",I="",P=new Uint8ClampedArray(0),R=new Uint8ClampedArray(0),C=0,O=0,Y=0,z=null,_="",W=new N,q=0;const G=[0,0,0],j=[0,0,0,1],V=Math.max(64,Math.min(4096,Number.isFinite(i.maxDimension)?i.maxDimension:960)),H=Math.max(256,Math.min(1048576,Number.isFinite(i.maxCells)?i.maxCells:12e3));let $=H,Q=Math.max(.1,Math.min(8,i.pixelRatio??1));return{canvas:t,render:function(e,i=0,l=960,U=540,H=i*b.motion.speed){if(Number.isFinite(i)||(i=0),!Number.isFinite(l)||!Number.isFinite(U)||l<1||U<1)throw new Error("Render dimensions must be positive finite numbers.");const[J,K]=D(e);if(!J||!K)return;const Z=Math.min(1,V/Math.max(l,U)),tt=Math.max(2,Math.round(l*Z)),et=Math.max(2,Math.round(U*Z));X(t,tt,et),X(r.canvas,tt,et),X(n.canvas,tt,et),X(h.canvas,tt,et),X(c.canvas,tt,et),q!==tt/et&&(q=tt/et,W=new N(q)),W.configure(b.hover.effect,b.hover.strength,b.hover.radius,b.hover.edgeSafe),W.step(Math.max(0,Math.min(.05,i-Y))),Y=i;const it="currentTime"in e?e.getVideoPlaybackQuality?.().totalVideoFrames??e.currentTime:0,st=[w,tt,et,it].join(":"),at="dither"===b.style,rt=g(tt,et,b,$,Q),ot=rt.cell,nt=rt.rowHeight,ht=rt.columns,lt=rt.rows;if(y!==st||z!==e||C!==ht||O!==lt){z=e,y=st,I="",C=ht,O=lt,r.ctx.clearRect(0,0,tt,et),r.ctx.save();const t=m(J,K,tt,et,b.crop);r.ctx.translate(t.x+t.width/2,t.y+t.height/2),r.ctx.rotate(b.crop.rotation*Math.PI/180),r.ctx.drawImage(e,-J*t.scale/2,-K*t.scale/2,J*t.scale,K*t.scale),r.ctx.restore(),X(o.canvas,C,O),o.ctx.clearRect(0,0,C,O),o.ctx.drawImage(r.canvas,0,0,C,O),P=o.ctx.getImageData(0,0,C,O).data;const i=b.color;for(let t=0;t<P.length;t+=4){const e=.299*P[t]+.587*P[t+1]+.114*P[t+2];for(let s=0;s<3;s++){const a=e+(P[t+s]-e)*i.saturation;P[t+s]=((a*(1-i.grayscale)+e*i.grayscale)/255-.5)*i.contrast*255+127.5+255*i.brightness}}}n.ctx.clearRect(0,0,tt,et),n.ctx.globalAlpha=1,n.ctx.globalCompositeOperation="source-over";const ct=H;if(at){const t=!W.active&&"none"===b.motion.type&&"none"===b.dither.motion;if(!t||I!==st){R.length!==P.length&&(R=new Uint8ClampedArray(P.length));const e=R;e.set(P);const a=b.motion.type;if(W.active||"none"!==a)for(let t=0;t<O;t++)for(let i=0;i<C;i++){const s=i/C,r=t/O;let o=i,n=t,h=0,l=1,c=1;W.active&&(W.sample(s,r,G),o-=G[0]*C,n-=G[1]*O,h=G[2]/3),S(a,s,r,ct,j),k(j,b.motion.amount??1),o-=j[0]/960*C,n-=j[1]/960*O,l=1+j[2],c=j[3],o=Math.max(0,Math.min(C-1,o)),n=Math.max(0,Math.min(O-1,n));const f=Math.floor(o),m=Math.floor(n),d=o-f,u=n-m,g=4*(t*C+i),p=4*(m*C+f),M=4*(m*C+Math.min(C-1,f+1)),x=4*(Math.min(O-1,m+1)*C+f),v=4*(Math.min(O-1,m+1)*C+Math.min(C-1,f+1));for(let t=0;t<3;t++){const i=((P[p+t]*(1-d)+P[M+t]*d)*(1-u)+(P[x+t]*(1-d)+P[v+t]*d)*u)/255;let s=(["trail","contour"].includes(b.hover.effect)?L(i,h):"dissolve"===b.hover.effect?i*Math.max(0,1+h):i)*l;e[g+t]=255*s}e[g+3]=P[p+3]*c}!function(t,e,i,a,r=0){const{algorithm:o,amount:n,threshold:h}=a,l=("custom"===a.palette?a.colors:s[a.palette]??s.mono).map(T),c=F[o],f=c?new Float32Array(3*e*3):null,m="drift"===a.motion?r*a.speed*3:0,d=(a.direction??45)*Math.PI/180,u=Math.round(Math.cos(d)*m),g=Math.round(Math.sin(d)*m),p=(t,e)=>(t%e+e)%e,M=l.map(t=>.299*t[0]+.587*t[1]+.114*t[2]),x="shimmer"===a.motion?.15*Math.sin(r*a.speed*2):0;for(let s=0;s<i;s++)for(let r=0;r<e;r++){const d=4*(s*e+r),v=3*(s%3*e+r);if(0===t[d+3]){f&&(f[v]=f[v+1]=f[v+2]=0);continue}let b=0;o.startsWith("bayer")?b=A(r-u&15,s-g&15,Number(o.slice(5))):"noise"===o?b=E(r-u,s-g)-.5:"lines"===o?b=p(s-g,4)/3-.5:"vertical-lines"===o?b=p(r-u,4)/3-.5:"diagonal-lines"===o?b=p(r-u+s-g,4)/3-.5:"radial"===o?b=p(Math.hypot(r-e/2,s-i/2)-m,6)/6-.5:"halftone"===o&&(b=Math.hypot(p(r-u,6)-2.5,p(s-g,6)-2.5)/3.54-.5);const w=128*(b*n+x+(.5-h)),y=t[d]+w+(f?.[v]??0),S=t[d+1]+w+(f?.[v+1]??0),k=t[d+2]+w+(f?.[v+2]??0);let F=0,T=0,I=0;if(l.length){let t=1/0;const e=.299*y+.587*S+.114*k;for(let i=0;i<l.length;i++){const s=l[i],r="luminance"===a.colorSpace?(e-M[i])**2:(y-s[0])**2*.299+(S-s[1])**2*.587+(k-s[2])**2*.114;r<t&&(t=r,[F,T,I]=s)}}else F=51*Math.round(y/51),T=51*Math.round(S/51),I=51*Math.round(k/51);if(t[d]=F,t[d+1]=T,t[d+2]=I,f&&c){f[v]=f[v+1]=f[v+2]=0;for(const[t,a,o]of c){const h=r+t;if(h<0||h>=e||s+a>=i)continue;const l=3*((s+a)%3*e+h);f[l]+=(y-F)*o*n,f[l+1]+=(S-T)*o*n,f[l+2]+=(k-I)*o*n}}}}(e,C,O,b.dither,i),o.ctx.putImageData(new ImageData(e,C,O),0,0),I=t?st:""}n.ctx.imageSmoothingEnabled=!1,n.ctx.drawImage(o.canvas,0,0,tt,et)}else{const t=Array.from(B[b.style]??b.charset),e=n.ctx,i="ascii"===b.style||!!B[b.style],s=Math.max(3,Math.round(.92*nt)),a=Math.ceil(ot+4),r=Math.ceil(nt+4),o=[t.join(""),s,a,r,b.ink,b.colorMode].join(":");if(i&&o!==_&&(_=o,X(d.canvas,a*t.length,r),d.ctx.clearRect(0,0,d.canvas.width,r),d.ctx.font=`${s}px monospace`,d.ctx.textBaseline="middle",d.ctx.textAlign="center",d.ctx.fillStyle="accent"===b.colorMode?b.ink:"#ffffff",t.forEach((t,e)=>d.ctx.fillText(t,e*a+a/2,r/2))),"lego"===b.style&&v!==ot){v=ot,X(M.canvas,Math.ceil(ot),Math.ceil(nt));const t=M.ctx;t.clearRect(0,0,M.canvas.width,M.canvas.height),t.fillStyle="rgba(255,255,255,.2)",t.strokeStyle="rgba(0,0,0,.25)",t.lineWidth=Math.max(.5,.6*Q),t.beginPath(),t.arc(ot/2,nt/2,.28*ot,0,2*Math.PI),t.fill(),t.stroke()}e.font=`${s}px monospace`,e.textBaseline="middle",e.textAlign="center";for(let s=0;s<O;s++)for(let o=0;o<C;o++){let n=o,h=s,l=0;W.active&&(W.sample(o/C,s/O,G),n=Math.max(0,Math.min(C-1,o-G[0]*C)),h=Math.max(0,Math.min(O-1,s-G[1]*O)),l=G[2]/3);const c=4*(Math.floor(h)*C+Math.floor(n));if(!P[c+3])continue;const f=P[c],m=P[c+1],u=P[c+2],g=(.299*f+.587*m+.114*u)/255;let p=g;["trail","contour"].includes(b.hover.effect)&&l?p=L(p,l):"dissolve"===b.hover.effect&&(p*=Math.max(0,1+l));const x=b.motion.type;let v=o*ot,w=s*nt,y=1;S(x,o/C,s/O,ct,j),k(j,b.motion.amount??1),v+=j[0]*tt/960,w+=j[1]*et/960,p=Math.max(0,Math.min(1,p+j[2])),y=j[3],i||"accent"!==b.colorMode||"cmyk"===b.style||(y*=p),e.globalAlpha=y;const A=255*(p-g);let F="source"===b.colorMode?`rgb(${f+A},${m+A},${u+A})`:"gray"===b.colorMode?`rgb(${Math.round(255*p)},${Math.round(255*p)},${Math.round(255*p)})`:b.ink;if(e.fillStyle=F,i){const i=Math.max(0,Math.min(t.length-1,Math.round(p*(t.length-1))));"accent"===b.colorMode?e.drawImage(d.canvas,i*a,0,a,r,"none"===x||"caustics"===x?Math.round(v+(ot-a)/2):v+(ot-a)/2,"none"===x||"caustics"===x?Math.round(w+(nt-r)/2):w+(nt-r)/2,a,r):e.fillText(t[i],v+ot/2,w+nt/2)}else if("dots"===b.style)e.beginPath(),e.arc(v+ot/2,w+nt/2,.48*ot*Math.sqrt(p),0,2*Math.PI),e.fill();else if("led"===b.style)e.beginPath(),e.arc(v+ot/2,w+nt/2,.39*ot,0,2*Math.PI),e.fill(),e.globalAlpha=.35*y,e.fillStyle="#ffffff",e.beginPath(),e.arc(v+.43*ot,w+.4*nt,.11*ot,0,2*Math.PI),e.fill();else if("hex"===b.style){const t=v+ot/2,i=w+nt/2;e.beginPath();for(let s=0;s<6;s++){const a=Math.PI/3*s,r=t+Math.cos(a)*ot*.49,o=i+Math.sin(a)*nt*.49;0===s?e.moveTo(r,o):e.lineTo(r,o)}e.closePath(),e.fill()}else if("cmyk"===b.style){e.fillStyle="#ffffff",e.fillRect(v,w,ot,nt);const t=1-Math.max(f,m,u)/255,i=Math.max(.001,1-t),s=[["#00ffff",(1-f/255-t)/i,.36,.36],["#ff00ff",(1-m/255-t)/i,.64,.36],["#ffff00",(1-u/255-t)/i,.5,.66],["#000000",t,.5,.5]];e.globalCompositeOperation="multiply";for(const[t,i,a,r]of s)e.fillStyle=t,e.beginPath(),e.arc(v+ot*a,w+nt*r,.48*ot*Math.sqrt(Math.max(0,i)),0,2*Math.PI),e.fill();e.globalCompositeOperation="source-over"}else if("voxel"===b.style){const t=v+ot/2,i=w+nt/2;e.beginPath(),e.moveTo(t,w),e.lineTo(v+ot,w+.25*nt),e.lineTo(t,i),e.lineTo(v,w+.25*nt),e.closePath(),e.fill(),e.globalAlpha=.72*y,e.beginPath(),e.moveTo(v,w+.25*nt),e.lineTo(t,i),e.lineTo(t,w+nt),e.lineTo(v,w+.75*nt),e.closePath(),e.fill(),e.globalAlpha=.42*y,e.beginPath(),e.moveTo(t,i),e.lineTo(v+ot,w+.25*nt),e.lineTo(v+ot,w+.75*nt),e.lineTo(t,w+nt),e.closePath(),e.fill()}else{const t="pixel"===b.style?0:Math.max(.5,.06*ot);e.fillRect(v,w,ot-t,nt-t),"lego"===b.style&&e.drawImage(M.canvas,v,w),"disco"===b.style&&(e.fillStyle=`rgba(255,255,255,${.1+.25*E(o,s)})`,e.fillRect(v,w,ot-t,.35*nt),e.fillStyle="rgba(0,0,0,.25)",e.fillRect(v,w+.65*nt,ot-t,.35*nt-t))}}}n.ctx.globalAlpha=1,(b.color.amount||b.lights.length)&&(X(u.canvas,tt,et),u.ctx.clearRect(0,0,tt,et),u.ctx.drawImage(n.canvas,0,0)),b.color.amount&&(n.ctx.save(),n.ctx.globalCompositeOperation=b.color.blend,n.ctx.globalAlpha=b.color.amount,n.ctx.fillStyle=b.color.tint,n.ctx.fillRect(0,0,tt,et),n.ctx.restore());for(const t of b.lights){const e=t.radius*Math.max(tt,et),i=n.ctx.createRadialGradient(t.x*tt,t.y*et,0,t.x*tt,t.y*et,e);i.addColorStop(0,t.color),i.addColorStop(1,"transparent"),n.ctx.save(),n.ctx.globalCompositeOperation="screen",n.ctx.globalAlpha=t.intensity,n.ctx.fillStyle=i,n.ctx.fillRect(0,0,tt,et),n.ctx.restore()}if((b.color.amount||b.lights.length)&&(n.ctx.globalCompositeOperation="destination-in",n.ctx.drawImage(u.canvas,0,0),n.ctx.globalCompositeOperation="source-over"),b.mask.enabled&&b.mask.shapes.length){const t=c.ctx;t.clearRect(0,0,tt,et),t.fillStyle="#fff",t.strokeStyle="#fff",t.lineCap="round",t.lineJoin="round";for(const e of b.mask.shapes){if(t.beginPath(),"ellipse"===e.kind)t.ellipse((e.x+e.width/2)*tt,(e.y+e.height/2)*et,e.width*tt/2,e.height*et/2,0,0,2*Math.PI);else{if("rectangle"!==e.kind){if(t.lineWidth=(e.size??.05)*tt,1===e.points?.length){t.arc(e.points[0][0]*tt,e.points[0][1]*et,t.lineWidth/2,0,2*Math.PI),t.fill();continue}e.points?.forEach(([e,i],s)=>s?t.lineTo(e*tt,i*et):t.moveTo(e*tt,i*et)),t.stroke();continue}t.rect(e.x*tt,e.y*et,e.width*tt,e.height*et)}t.fill()}n.ctx.globalCompositeOperation=b.mask.invert?"destination-out":"destination-in",n.ctx.drawImage(c.canvas,0,0),n.ctx.globalCompositeOperation="source-over"}const ft=h.ctx;ft.clearRect(0,0,tt,et),ft.globalAlpha=b.backdrop.opacity;const mt=b.backdrop;if("solid"===mt.mode&&(ft.fillStyle=mt.color,ft.fillRect(0,0,tt,et)),"gradient"===mt.mode){const t=ft.createLinearGradient(0,0,tt,et);t.addColorStop(0,mt.color),t.addColorStop(1,mt.color2),ft.fillStyle=t,ft.fillRect(0,0,tt,et)}if("source"===mt.mode&&ft.drawImage(r.canvas,0,0),"blurred"===mt.mode){const t=Math.max(1,mt.blur);X(f.canvas,Math.max(2,Math.round(tt/t)),Math.max(2,Math.round(et/t))),f.ctx.drawImage(r.canvas,0,0,f.canvas.width,f.canvas.height),ft.imageSmoothingEnabled=!0,ft.drawImage(f.canvas,0,0,tt,et)}ft.globalAlpha=1,b.effects.characterBloom>0&&(X(p.canvas,Math.max(2,Math.round(tt/3)),Math.max(2,Math.round(et/3))),p.ctx.clearRect(0,0,p.canvas.width,p.canvas.height),p.ctx.drawImage(n.canvas,0,0,p.canvas.width,p.canvas.height),ft.save(),ft.globalAlpha=.8*b.effects.characterBloom,ft.globalCompositeOperation="screen",ft.filter=`blur(${1+4*b.effects.characterBloom}px)`,ft.drawImage(p.canvas,0,0,tt,et),ft.restore()),ft.drawImage(n.canvas,0,0);const dt=Object.entries(b.effects).some(([t,e])=>!["angle","focus","blurType","characterBloom"].includes(t)&&"number"==typeof e&&e>0);a.clearRect(0,0,tt,et),dt?(void 0===x&&(x=function(){const t=document.createElement("canvas"),e=t.getContext("webgl",{alpha:!0,premultipliedAlpha:!1,antialias:!1,preserveDrawingBuffer:!0});if(!e)return null;const i=[],s=(t,s)=>{const a=e.createShader(t);if(i.push(a),e.shaderSource(a,s),e.compileShader(a),!e.getShaderParameter(a,e.COMPILE_STATUS))throw new Error(e.getShaderInfoLog(a)||"Finish shader failed");return a},a=e.createProgram();if(e.attachShader(a,s(e.VERTEX_SHADER,"attribute vec2 p;varying vec2 uv;void main(){uv=(p+1.)*.5;gl_Position=vec4(p,0.,1.);}")),e.attachShader(a,s(e.FRAGMENT_SHADER,"precision mediump float;varying vec2 uv;uniform sampler2D src;uniform vec2 size;uniform float t,bloom,grain,dust,scanlines,crt,prism,vignette,glitch,pixelate,blur,blurMode,angle,focus,halftone,rgbSplit,sharpen;\n float hash(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}\n vec4 sampleAt(vec2 p){if(min(min(p.x,p.y),min(1.-p.x,1.-p.y))<0.)return vec4(0.);return texture2D(src,vec2(p.x,1.-p.y));}\n void main(){vec2 q=uv,c=q-.5;q=c*(1.+dot(c,c)*crt*.25)+.5;\n if(glitch>0.)q.x+=(hash(vec2(floor(q.y*35.),floor(t*5.)))-.5)*.07*glitch*step(.82,hash(vec2(floor(q.y*35.),floor(t*5.)+3.)));\n if(pixelate>0.){vec2 grid=size/(1.+pixelate*20.);q=(floor(q*grid)+.5)/grid;}\n vec4 base=sampleAt(q);float edge=smoothstep(.12,.65,length(c));\n vec2 shift=vec2((prism*3.*edge+rgbSplit*8.)/size.x,prism*edge/size.y);\n if(prism>0.||rgbSplit>0.)base.rgb=vec3(sampleAt(q+shift).r,base.g,sampleAt(q-shift).b);\n vec2 dir=vec2(cos(angle),sin(angle))/size;float amount=blur;\n if(blurMode>1.5&&blurMode<2.5){dir=normalize(c+vec2(.0001))/size;amount*=length(c)*2.;}\n if(blurMode>2.5)amount*=smoothstep(.05,.5,abs(q.y-focus));\n vec4 blurred=base;\n if(amount>0.){blurred=base*.2;for(int i=1;i<=4;i++){float f=float(i)*amount*.35;vec2 off=dir*f;if(blurMode<.5)off=vec2(cos(float(i)*1.57),sin(float(i)*1.57))*amount/size;blurred+=sampleAt(q+off)*.1+sampleAt(q-off)*.1;}}\n vec3 light=vec3(0.);if(bloom>0.)for(int i=0;i<4;i++){float a=float(i)*1.5708;vec3 s=sampleAt(q+vec2(cos(a),sin(a))*6./size).rgb;light+=max(vec3(0.),s-.35)*.25;}\n vec3 col=blurred.rgb+light*bloom;\n if(sharpen>0.){vec2 px=1./size;vec3 near=sampleAt(q+vec2(px.x,0.)).rgb+sampleAt(q-vec2(px.x,0.)).rgb+sampleAt(q+vec2(0.,px.y)).rgb+sampleAt(q-vec2(0.,px.y)).rgb;col+=(base.rgb*4.-near)*sharpen*.5;}\ncol*=1.-scanlines*.4*(.5+.5*cos(q.y*size.y*3.14159));\n col*=1.-vignette*smoothstep(.15,.72,length(c));\n col+=(hash(floor(q*size)+floor(t*12.))-.5)*grain*.2;\n if(dust>0.)col+=step(1.-dust*.002,hash(floor(q*size*.25)+floor(t*2.)))*.4;\n if(halftone>0.)col*=mix(1.,smoothstep(.1,.65,length(fract(q*size/6.)-.5)),halftone);\n gl_FragColor=vec4(clamp(col,0.,1.),blurred.a);}")),e.linkProgram(a),!e.getProgramParameter(a,e.LINK_STATUS))throw new Error("Finish link failed");e.useProgram(a);const r=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,r),e.bufferData(e.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,1,1]),e.STATIC_DRAW);const o=e.getAttribLocation(a,"p");e.enableVertexAttribArray(o),e.vertexAttribPointer(o,2,e.FLOAT,!1,0,0);const n=e.createTexture();e.bindTexture(e.TEXTURE_2D,n),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE);const h=["t","bloom","grain","dust","scanlines","crt","prism","rgbSplit","sharpen","vignette","glitch","pixelate","blur","blurMode","angle","focus","halftone"],l=Object.fromEntries(h.map(t=>[t,e.getUniformLocation(a,t)])),c=e.getUniformLocation(a,"size");return{canvas:t,render(i,s,r,o=1){t.width===i.width&&t.height===i.height||(t.width=i.width,t.height=i.height,e.viewport(0,0,t.width,t.height)),e.useProgram(a),e.bindTexture(e.TEXTURE_2D,n),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,e.RGBA,e.UNSIGNED_BYTE,i),e.uniform2f(c,t.width/o,t.height/o);for(const t of h)e.uniform1f(l[t],"t"===t?r:"blurMode"===t?["gaussian","directional","radial","progressive"].indexOf(s.blurType):"angle"===t?s.angle*Math.PI/180:s[t]??0);return e.drawArrays(e.TRIANGLE_STRIP,0,4),t},destroy(){e.deleteTexture(n),e.deleteBuffer(r),e.deleteProgram(a),i.forEach(t=>e.deleteShader(t)),t.width=t.height=1,e.getExtension("WEBGL_lose_context")?.loseContext()}}}()),x?a.drawImage(x.render(h.canvas,b.effects,i,Q),0,0):a.drawImage(h.canvas,0,0),t.dataset.studioFinish=x?"gpu":"unavailable"):(a.drawImage(h.canvas,0,0),t.dataset.studioFinish="none")},setPixelRatio(t){Q=Math.max(.1,Math.min(8,Number.isFinite(t)?t:1))},get pixelRatio(){return Q},setBudget(t){$=Math.max(1500,Math.min(H,Math.round(t)))},get maxCells(){return $},configure(t){b=l(t),w++},pointer:(t,e,i)=>{W.configure(b.hover.effect,b.hover.strength,b.hover.radius,b.hover.edgeSafe),W.move(t,e,i)},leave(){W.leave()},get active(){return W.active},get settings(){return l(b)},get capabilities(){return{gpuFinish:null!==x}},invalidate(){y=""},destroy(){x?.destroy();for(const t of[r,o,n,h,c,f,d,u,p,M])t.canvas.width=t.canvas.height=1;z=null,P=new Uint8ClampedArray(0)}}}function Y(t,e,i){return new Promise((s,a)=>{const r=()=>{l(),s()},o=()=>{l(),a(new Error("Unable to load this media. Check its format and cross-origin permissions."))},n=()=>{l(),a(new DOMException("Cancelled","AbortError"))},h=setTimeout(o,2e4),l=()=>{clearTimeout(h),t.removeEventListener(e,r),t.removeEventListener("error",o),i?.removeEventListener("abort",n)};t.addEventListener(e,r,{once:!0}),t.addEventListener("error",o,{once:!0}),i?.addEventListener("abort",n,{once:!0}),i?.aborted&&n()})}async function z(t,e){if("string"==typeof t&&t.startsWith("blob:")){const i=await fetch(t,{signal:e}),s=await i.blob();return z(new File([s],"Imported media",{type:s.type}),e)}const i="string"!=typeof t,s=i?t.name:t;if(i?"image/gif"===t.type:/\.gif(?:[?#]|$)/i.test(s)){const s=await import("gifuct-js"),{parseGIF:a,decompressFrames:r}="function"==typeof s.parseGIF?s:s.default,o=i?await t.arrayBuffer():await fetch(t,{signal:e}).then(t=>{if(!t.ok)throw new Error("GIF download failed.");return t.arrayBuffer()});if(e?.aborted)throw new DOMException("Cancelled","AbortError");const n=a(o),h=n.lsd.width,l=n.lsd.height;if(h*l>4e6)throw new Error("GIF is too large. Use a smaller GIF or an MP4.");const c=n.frames.filter(t=>"image"in t).length;if(!c||c*h*l*4>128e6)throw new Error("GIF exceeds the decoded memory budget. Use MP4 for long animations.");const f=r(n,!0);if(f.length*h*l*4>128e6)throw new Error("GIF exceeds the 128 MB decoded budget. Use MP4 for long animations.");const m=document.createElement("canvas");m.width=h,m.height=l;const d=m.getContext("2d"),u=document.createElement("canvas"),g=u.getContext("2d"),p=f.reduce((t,e)=>t+Math.max(20,e.delay),0)/1e3;let M=-1,x=null;const v=t=>{const e=(t%p+p)%p*1e3;let i=0,s=0;for(;s<f.length-1&&(i+=Math.max(20,f[s].delay),!(e<i));s++);s<M&&(d.clearRect(0,0,h,l),M=-1,x=null);for(let t=M+1;t<=s;t++){const e=f[t-1];2===e?.disposalType&&d.clearRect(e.dims.left,e.dims.top,e.dims.width,e.dims.height),3===e?.disposalType&&x&&d.putImageData(x,0,0);const i=f[t];x=3===i.disposalType?d.getImageData(0,0,h,l):null,u.width=i.dims.width,u.height=i.dims.height,g.putImageData(new ImageData(new Uint8ClampedArray(i.patch),u.width,u.height),0,0),d.drawImage(u,i.dims.left,i.dims.top)}return M=s,m};return v(0),{source:m,width:h,height:l,duration:p,animated:!0,seek:async()=>{},frame:v,destroy(){m.width=u.width=1,f.length=0,x=null}}}const a=i?t.type.startsWith("video/"):/\.(mp4|webm|mov)(?:[?#]|$)/i.test(s),r=i?URL.createObjectURL(t):t,o=a?document.createElement("video"):new Image;o.crossOrigin="anonymous",o instanceof HTMLVideoElement&&(o.muted=!0,o.playsInline=!0,o.preload="auto");const n=()=>{o instanceof HTMLVideoElement?(o.pause(),o.removeAttribute("src"),o.load()):o.src="",i&&URL.revokeObjectURL(r)};try{const t=Y(o,a?"loadeddata":"load",e);o.src=r,await t;const i=o instanceof HTMLVideoElement?o.videoWidth:o.naturalWidth,s=o instanceof HTMLVideoElement?o.videoHeight:o.naturalHeight;return{source:o,width:i,height:s,duration:o instanceof HTMLVideoElement?o.duration:0,animated:a,async seek(t,e){if(!(o instanceof HTMLVideoElement))return;if(!Number.isFinite(o.duration)||o.duration<=0)throw new Error("Video duration is unavailable.");const i=Math.max(0,Math.min(o.duration-.001,t%o.duration));if(Math.abs(o.currentTime-i)<.001&&o.readyState>=2)return;const s=Y(o,"seeked",e);o.currentTime=i,await s},frame:()=>o,destroy:n}}catch(t){throw n(),t}}var _=t=>{if(t?.aborted)throw new DOMException("Export cancelled","AbortError")};async function W(t,e,i){const{format:s,signal:a}=i,r=Math.round(i.width),o=Math.round(i.height);if(!Number.isFinite(r)||!Number.isFinite(o)||r<2||o<2||r>4096||o>4096||r*o>16777216)throw new Error("Choose export dimensions between 2 and 4096 pixels.");if(!["png","jpeg","mp4","webm","gif"].includes(s))throw new Error("Unknown export format.");if(void 0!==i.fps&&!Number.isFinite(i.fps))throw new Error("Frames per second must be finite.");const n=Math.max(1,Math.min(60,Math.round(i.fps??24))),h=i.duration??5;if(!Number.isFinite(h)||h<=0||h>60)throw new Error("Choose a duration from 0 to 60 seconds.");const c="png"===s||"jpeg"===s,f=i.time??0;if(!Number.isFinite(f)||f<0)throw new Error("Image time must be a finite non-negative number.");if(!c&&r*o>8294400)throw new Error("Video exports support up to 3840 × 2160 pixels.");if("gif"===s&&(r*o>921600||h*n>600))throw new Error("GIF export supports up to 1280 × 720 and 600 frames. Use MP4 for larger exports.");if(void 0!==i.referenceWidth&&(!Number.isFinite(i.referenceWidth)||i.referenceWidth<1))throw new Error("Reference width must be a positive number.");if("gif"===s&&n>30)throw new Error("GIF exports support up to 30 fps. Choose MP4 or WebM for higher frame rates.");const m=document.createElement("canvas"),d=O(m,l(e),{maxDimension:4096,maxCells:i.maxCells??12e3,pixelRatio:r/(i.referenceWidth??Math.min(r,960))});let u;try{if(_(a),i.onProgress?.(0),c){if(d.render(await t.frame(f,a),f,r,o),"unavailable"===m.dataset.studioFinish)throw new Error("Optical effects need WebGL. Disable these effects or export in a supported browser.");_(a);const e=await new Promise((t,e)=>m.toBlob(i=>i?t(i):e(new Error("Image export failed.")),"png"===s?"image/png":"image/jpeg",i.quality??.92));return i.onProgress?.(1),e}const e=Math.ceil(h*n);if("gif"===s){const s=await import("gifenc"),{GIFEncoder:l,quantize:c,applyPalette:f}="function"==typeof s.GIFEncoder?s:s.default,u=l(),g=m.getContext("2d");for(let s=0;s<e;s++){_(a);const l=s/n;if(d.invalidate(),d.render(await t.frame(l,a),l,r,o),"unavailable"===m.dataset.studioFinish)throw new Error("Optical effects need WebGL. Disable these effects or export in a supported browser.");const p=g.getImageData(0,0,r,o).data,M=c(p,256),x=f(p,M);u.writeFrame(x,r,o,{palette:M,delay:10*(Math.round(100*Math.min((s+1)/n,h))-Math.round(100*s/n)),repeat:0}),i.onProgress?.((s+1)/e*.98),await new Promise(t=>setTimeout(t,0))}return u.finish(),i.onProgress?.(1),new Blob([new Uint8Array(u.bytes())],{type:"image/gif"})}const{Output:l,Mp4OutputFormat:g,WebMOutputFormat:p,BufferTarget:M,CanvasSource:x,canEncodeVideo:v}=await import("mediabunny"),b="mp4"===s?"avc":"vp9";if(!await v(b,{width:r,height:o}))throw new Error(`${s.toUpperCase()} encoding is unavailable in this browser. Try another format or a current browser with WebCodecs.`);const w=new l({format:"mp4"===s?new g({fastStart:"in-memory"}):new p,target:new M});m.width=r,m.height=o,u=()=>w.cancel();const y=new x(m,{codec:b,bitrate:Math.max(5e5,Math.round(r*o*n*.15))});w.addVideoTrack(y,{frameRate:n}),m.width=r,m.height=o,await w.start();for(let s=0;s<e;s++){_(a);const l=s/n;if(d.invalidate(),d.render(await t.frame(l,a),l,r,o),"unavailable"===m.dataset.studioFinish)throw new Error("Optical effects need WebGL. Disable these effects or export in a supported browser.");await y.add(l,Math.min(1/n,h-l)),i.onProgress?.((s+1)/e*.98),s%4==0&&await new Promise(t=>setTimeout(t,0))}return y.close(),_(a),await w.finalize(),u=void 0,i.onProgress?.(1),new Blob([w.target.buffer],{type:"mp4"===s?"video/mp4":"video/webm"})}catch(t){throw await(u?.().catch(()=>{})),t}finally{d.destroy(),m.width=m.height=1}}async function q(t,e,i={}){const s=await z(e,i.signal);try{return G(t,s,i)}catch(t){throw s.destroy(),t}}function G(t,e,i={}){let s=l(i.settings),a=i.width??e.width,r=i.height??e.height;const o=O(t,s,i),n=new y;let h=0,c=!1,f=!0,m=!1,g=!0,p=0,M=0,x=0,v=-1,b=!1!==i.adaptive,w=0,S=0,k=0;const E=matchMedia("(prefers-reduced-motion: reduce)"),A=e.source instanceof HTMLVideoElement?e.source:null,F=()=>{c||h||!g||document.hidden||(h=requestAnimationFrame(P))},T=()=>!m&&!E.matches&&g&&!document.hidden,I=()=>{A&&(A.loop=!0,T()?A.play().catch(()=>{m=!0,f=!0,F()}):A.pause()),M=0,f=!0,F()};function P(t){if(h=0,c||!g||document.hidden)return;const l=T();l&&(p+=M?Math.min(.1,(t-M)/1e3):0),M=t;const u=l&&(e.animated||"none"!==s.motion.type||"none"!==s.dither.motion||s.effects.grain>0||s.effects.dust>0||s.effects.glitch>0),y=A?.getVideoPlaybackQuality?.().totalVideoFrames??-1,E="none"!==s.motion.type||"none"!==s.dither.motion||s.effects.grain>0||s.effects.dust>0||s.effects.glitch>0,I=!A||y<0||y!==v;if(f||o.active||u&&(I||E)&&t-x>=1e3/Math.max(1,Math.min(60,i.fps??60))-.5){const h=performance.now();try{e.animated&&!A&&o.invalidate();const[t,l]=d(a,r,s);o.render(e.frame(A?.currentTime??p),p,t,l,n.update(p,s.motion.type,s.motion.speed));const c=performance.now()-h;i.onFrame?.(A?.currentTime??p,c),S=S?.9*S+.1*c:c,++w>20&&b&&(u||o.active)&&(k=S>13?k+1:0,k>=12&&o.maxCells>3e3&&(o.setBudget(Math.max(3e3,.8*o.maxCells)),i.onQualityChange?.(o.maxCells),k=0,S=0))}catch(t){m=!0,i.onError?.(t instanceof Error?t:new Error(String(t)))}f=!1,x=t,v=y}l&&(u||o.active)&&F()}const R=e=>{if(!T())return;const i=t.getBoundingClientRect();o.pointer((e.clientX-i.left)/i.width,(e.clientY-i.top)/i.height,e.timeStamp),F()},C=()=>{o.leave(),F()},L=new IntersectionObserver(([t])=>{g=t.isIntersecting,I()});L.observe(t),t.addEventListener("pointermove",R),t.addEventListener("pointerleave",C),t.addEventListener("pointercancel",C),document.addEventListener("visibilitychange",I),E.addEventListener("change",I);const N=()=>{c||(c=!0,cancelAnimationFrame(h),L.disconnect(),t.removeEventListener("pointermove",R),t.removeEventListener("pointerleave",C),t.removeEventListener("pointercancel",C),document.removeEventListener("visibilitychange",I),E.removeEventListener("change",I),i.signal?.removeEventListener("abort",N),o.destroy(),e.destroy())};if(i.signal?.addEventListener("abort",N,{once:!0}),i.signal?.aborted)throw N(),new DOMException("Cancelled","AbortError");return I(),{canvas:t,media:e,update(t){s=u(s,t),o.configure(s),f=!0,F()},resize(t,e,i){void 0!==i&&o.setPixelRatio(i),a=Math.max(2,t),r=Math.max(2,e),f=!0,F()},pause(t=!0){m=t,I()},async seek(t){p=Math.max(0,t),await e.seek(p),o.invalidate(),f=!0,F()},redraw(){o.invalidate(),f=!0,F()},get time(){return A?.currentTime??p},get paused(){return m||E.matches},setAdaptive(t){b=t,w=k=S=0},setBudget(t){o.setBudget(t),i.onQualityChange?.(o.maxCells),f=!0,F()},get pixelRatio(){return o.pixelRatio},get maxCells(){return o.maxCells},get capabilities(){return o.capabilities},destroy:N}}var j={Standard:()=>import("figlet/fonts/Standard"),Slant:()=>import("figlet/fonts/Slant"),Small:()=>import("figlet/fonts/Small"),Big:()=>import("figlet/fonts/Big"),Banner:()=>import("figlet/fonts/Banner"),Block:()=>import("figlet/fonts/Block"),Bubble:()=>import("figlet/fonts/Bubble"),Digital:()=>import("figlet/fonts/Digital"),Doom:()=>import("figlet/fonts/Doom"),Lean:()=>import("figlet/fonts/Lean"),Mini:()=>import("figlet/fonts/Mini"),Script:()=>import("figlet/fonts/Script"),Shadow:()=>import("figlet/fonts/Shadow"),Speed:()=>import("figlet/fonts/Speed"),Starwars:()=>import("figlet/fonts/Star Wars"),Stop:()=>import("figlet/fonts/Stop"),Straight:()=>import("figlet/fonts/Straight"),Thin:()=>import("figlet/fonts/Thin"),ThreeD:()=>import("figlet/fonts/3-D"),Univers:()=>import("figlet/fonts/Univers"),Wavy:()=>import("figlet/fonts/Wavy")},V=Object.keys(j);async function H(t,e="Standard",i="#ffffff"){if(!Object.prototype.hasOwnProperty.call(j,e))throw new Error("Unknown text font.");const{default:s}=await import("figlet/browser"),a=await j[e]();s.parseFont(e,a.default),s.defaults({fetchFontIfMissing:!1});const r=s.textSync(t.slice(0,120),{font:e}),o=r.split("\n"),n=Math.max(1,...o.map(t=>t.length)),h=Math.max(1,o.length),l=Math.min(1,2048/(9*n)),c=document.createElement("canvas");c.width=Math.ceil(9*n*l+32),c.height=Math.ceil(15*h*l+32);const f=c.getContext("2d");return f.fillStyle="#080808",f.fillRect(0,0,c.width,c.height),f.fillStyle=i,f.font=15*l+"px monospace",f.textBaseline="top",o.forEach((t,e)=>f.fillText(t,16,16+15*e*l)),{canvas:c,ascii:r}}var $={standard:{label:"Classic ASCII",chars:" .:-=+*#%@"},asciify:{label:"Asciify",chars:" .:-=+ASCIIFY#@"},minimal:{label:"Minimal",chars:" .:+"},detailed:{label:"Detailed",chars:" .,:;i1tfLCG08@"},letters:{label:"Letters",chars:" .ilLCFTYASMW"},technical:{label:"Technical",chars:" .:;>_/[{}]=+*#@$"},blocks:{label:"Tonal blocks",chars:" ░▒▓█"},braille:{label:"Braille",chars:" ⠁⠃⠇⡇⣇⣧⣷⣿"}},Q=$;export{i as BLEND_MODES,$ as CHARACTER_SETS,a as DEFAULT_STUDIO_SETTINGS,e as DITHER_ALGORITHMS,Q as STUDIO_CHARACTER_SETS,p as STUDIO_LOOKS,v as STUDIO_MOTIONS,s as STUDIO_PALETTES,t as STUDIO_STYLES,V as STUDIO_TEXT_FONTS,O as createStudioRenderer,H as createStudioText,W as exportStudio,z as loadStudioMedia,q as mountStudio,G as mountStudioMedia,l as normalizeStudioSettings,f as parseStudioSettings,x as restyleStudio,c as serializeStudioSettings,m as studioCrop,d as studioDimensions,g as studioGrid,M as studioLook,D as studioSourceSize,u as updateStudioSettings};//# sourceMappingURL=studio.js.map
1
+ var t=["ascii","blocks","braille","dots","lines","cross","diagonal","diamond","mixed","pixel","mosaic","lego","voxel","disco","dither","hex","led","cmyk"],e=["none","bayer2","bayer4","bayer8","bayer16","floyd-steinberg","atkinson","stucki","sierra","sierra-lite","burkes","jarvis","halftone","lines","noise","vertical-lines","diagonal-lines","radial"],i=["source-over","multiply","screen","overlay","soft-light","hard-light","color-dodge","color-burn","hue","saturation","color","luminosity"],a={original:[],gray4:["#000000","#555555","#aaaaaa","#ffffff"],gray8:["#000000","#242424","#494949","#6d6d6d","#929292","#b6b6b6","#dbdbdb","#ffffff"],rgb8:["#000000","#ff0000","#00ff00","#0000ff","#ffff00","#ff00ff","#00ffff","#ffffff"],cyberpunk:["#08081a","#3c1670","#ed299a","#29dfef","#fff7bf"],pastel:["#393448","#a5b4d4","#9ed9c6","#e7aac1","#f5e8be"],mono:["#080808","#f2f2e8"],asciify:["#080808","#625b38","#a19558","#e8b900"],gameboy:["#0f380f","#306230","#8bac0f","#9bbc0f"],cga:["#000000","#55ffff","#ff55ff","#ffffff"],pico8:["#000000","#1d2b53","#7e2553","#008751","#ab5236","#5f574f","#c2c3c7","#fff1e8","#ff004d","#ffa300","#ffec27","#00e436","#29adff","#83769c","#ff77a8","#ffccaa"],c64:["#000000","#ffffff","#880000","#aaffee","#cc44cc","#00cc55","#0000aa","#eeee77","#dd8855","#664400","#ff7777","#333333","#777777","#aaff66","#0088ff","#bbbbbb"],amber:["#100b00","#5f3b00","#c98014","#ffd17c"],ice:["#081b29","#25586e","#69a6b3","#d9ece7"],risograph:["#f4ebd0","#f2594b","#2853a5","#29232e"],sepia:["#201b17","#6b5039","#b49868","#f6e6c5"],nord:["#2e3440","#4c566a","#88c0d0","#eceff4"],dusk:["#211b35","#654271","#c3788c","#f5c9a3"],forest:["#101f1b","#355747","#87a16f","#e4e3ba"],ocean:["#071b33","#17577f","#3fa7a4","#d9efde"],newsprint:["#191919","#666666","#aaaaaa","#eee9df"],custom:["#080808","#e8b900"]},s={version:1,aspectRatio:"original",style:"ascii",cellSize:6,charset:" .:-=+*#%@",colorMode:"accent",ink:"#e8b900",crop:{x:.5,y:.5,zoom:1,rotation:0},backdrop:{mode:"solid",color:"#080808",color2:"#393323",blur:12,opacity:1},color:{brightness:0,contrast:1,saturation:1,grayscale:0,tint:"#e8b900",amount:0,blend:"source-over"},dither:{algorithm:"none",palette:"mono",colors:["#080808","#e8b900"],amount:1,scale:2,threshold:.5,motion:"none",speed:1,direction:45,colorSpace:"rgb"},mask:{enabled:!1,invert:!1,shapes:[]},lights:[],effects:{bloom:0,characterBloom:0,grain:0,dust:0,scanlines:0,crt:0,prism:0,rgbSplit:0,sharpen:0,vignette:0,glitch:0,pixelate:0,blur:0,blurType:"gaussian",angle:0,focus:.5,halftone:0},motion:{type:"none",speed:1,amount:1},hover:{effect:"trail",strength:.55,radius:.2,edgeSafe:!1}},r=t=>t&&"object"==typeof t&&!Array.isArray(t)?t:{},o=(t,e,i,a)=>"number"==typeof t&&Number.isFinite(t)?Math.min(a,Math.max(i,t)):e,n=(t,e)=>"string"==typeof t&&/^#[\da-f]{6}$/i.test(t)?t:e,h=(t,e,i)=>e.includes(t)?t:i;function l(l={}){const c=r(l),f=s;if(void 0!==c.version&&1!==c.version)throw new Error("Unsupported studio settings version. Update asciify-engine.");const m=r(c.crop),d=r(c.backdrop),u=r(c.color),g=r(c.dither),p=r(c.mask),M=r(c.effects),x=r(c.motion),v=r(c.hover),b={...f.effects};for(const t of["bloom","characterBloom","grain","dust","scanlines","crt","prism","rgbSplit","sharpen","vignette","glitch","pixelate","halftone"])b[t]=o(M[t],0,0,1);b.blur=o(M.blur,0,0,24),b.blurType=h(M.blurType,["gaussian","directional","radial","progressive"],f.effects.blurType),b.angle=o(M.angle,0,-180,180),b.focus=o(M.focus,.5,0,1);let w=2048;return{version:1,aspectRatio:h(c.aspectRatio,["original","16:9","9:16","1:1","4:3","3:4","21:9"],"original"),style:h(c.style,t,f.style),cellSize:o(c.cellSize,f.cellSize,["pixel","mosaic","lego","voxel","disco","dots","hex","led","cmyk"].includes(String(c.style))?1:3,60),charset:"string"==typeof c.charset&&c.charset.length?Array.from(c.charset).slice(0,128).join(""):f.charset,colorMode:h(c.colorMode,["accent","source","gray"],f.colorMode),ink:n(c.ink,f.ink),crop:{x:o(m.x,.5,0,1),y:o(m.y,.5,0,1),zoom:o(m.zoom,1,1,5),rotation:90*Math.round(o(m.rotation,0,-360,360)/90)},backdrop:{mode:h(d.mode,["solid","transparent","source","blurred","gradient"],f.backdrop.mode),color:n(d.color,f.backdrop.color),color2:n(d.color2,f.backdrop.color2),blur:o(d.blur,12,0,32),opacity:o(d.opacity,1,0,1)},color:{brightness:o(u.brightness,0,-1,1),contrast:o(u.contrast,1,0,3),saturation:o(u.saturation,1,0,3),grayscale:o(u.grayscale,0,0,1),tint:n(u.tint,f.color.tint),amount:o(u.amount,0,0,1),blend:h(u.blend,i,f.color.blend)},dither:{algorithm:h(g.algorithm,e,"none"),palette:"string"==typeof g.palette&&Object.prototype.hasOwnProperty.call(a,g.palette)?g.palette:"mono",colors:Array.isArray(g.colors)&&g.colors.length>=2?g.colors.slice(0,32).map(t=>n(t,"#000000")):[...f.dither.colors],amount:o(g.amount,1,0,2),scale:Math.round(o(g.scale,2,1,12)),threshold:o(g.threshold,.5,0,1),motion:h(g.motion,["none","drift","shimmer"],"none"),speed:o(g.speed,1,.1,3),direction:o(g.direction,45,-180,180),colorSpace:h(g.colorSpace,["rgb","luminance"],"rgb")},mask:{enabled:!0===p.enabled,invert:!0===p.invert,shapes:Array.isArray(p.shapes)?p.shapes.slice(0,64).map(t=>{const e=r(t),i=h(e.kind,["rectangle","ellipse","brush"],"rectangle"),a="brush"===i&&Array.isArray(e.points)?e.points.slice(0,w).map(t=>Array.isArray(t)?[Math.round(1e4*o(t[0],0,0,1))/1e4,Math.round(1e4*o(t[1],0,0,1))/1e4]:[0,0]):[];return w-=a.length,{kind:i,x:o(e.x,0,0,1),y:o(e.y,0,0,1),width:o(e.width,.5,0,1),height:o(e.height,.5,0,1),size:o(e.size,.05,.005,.5),points:a}}):[]},lights:Array.isArray(c.lights)?c.lights.slice(0,4).map(t=>{const e=r(t);return{x:o(e.x,.5,0,1),y:o(e.y,.5,0,1),radius:o(e.radius,.3,.02,1),intensity:o(e.intensity,.5,0,1),color:n(e.color,"#ffffff")}}):[],effects:b,motion:{type:h(x.type,["none","caustics","current","reform","sheen","tidal","grain"],"none"),speed:o(x.speed,1,.1,3),amount:o(x.amount,1,0,2)},hover:{effect:h(v.effect,["none","trail","water","contour","dissolve","silk","vortex","magnetic","scatter"],"trail"),strength:o(v.strength,.55,0,1),radius:o(v.radius,.2,.1,1),edgeSafe:!0===v.edgeSafe}}}function c(t){return JSON.stringify(l(t))}function f(t){if(t.length>1e5)throw new Error("Settings exceed 100 KB.");return l(JSON.parse(t))}function m(t,e,i,a,s){const r=Math.abs(s.rotation/90)%2==1,o=r?e:t,n=r?t:e,h=Math.max(i/o,a/n)*s.zoom;return{width:o*h,height:n*h,x:(i-o*h)*s.x,y:(a-n*h)*s.y,scale:h,rotated:r}}function d(t,e,i){if(Math.abs(i.crop.rotation/90)%2==1&&([t,e]=[e,t]),"original"===i.aspectRatio)return[t,e];const[a,s]=i.aspectRatio.split(":").map(Number);return t/e>a/s?[Math.round(e*a/s),e]:[t,Math.round(t*s/a)]}function u(t,e){const i={...t,...e};for(const a of["crop","backdrop","color","dither","mask","effects","motion","hover"])e[a]&&Object.assign(i,{[a]:{...t[a],...e[a]}});return l(i)}function g(t,e,i,a=12e3,s=1){const r=Math.max(2,Number.isFinite(t)?t:960),o=Math.max(2,Number.isFinite(e)?e:540),n=Math.max(1,Math.floor(Number.isFinite(a)?a:12e3)),h=Math.max(.01,Number.isFinite(s)?s:1),l=["dither","pixel","mosaic","lego","voxel","disco","dots","hex","led","cmyk"].includes(i.style)?1:1.65;let c=Math.max(1,Math.ceil(Math.sqrt(r*o/(n*l))/h));for(;Math.ceil(r/(c*h))*Math.ceil(o/(c*h*l))>n;)c++;const f="dither"===i.style?i.dither.scale:i.cellSize,m=Math.max(c,f)*h;return{cell:m,rowHeight:m*l,columns:Math.ceil(r/m),rows:Math.ceil(o/(m*l)),minimum:c,limited:f<c}}var p=[["ascii","Signature ASCII","Brand ink and clean characters.",{}],["print","Newsprint","Fine diffusion on warm paper.",{style:"dither",dither:{algorithm:"floyd-steinberg",palette:"newsprint"}}],["handheld","Pocket camera","Four-tone ordered dither.",{style:"dither",dither:{algorithm:"bayer4",palette:"gameboy",scale:3}}],["riso","Two inks","Limited-color print texture.",{style:"dither",dither:{algorithm:"atkinson",palette:"risograph",scale:2}}],["night","Night signal","Gray characters and optical fringe.",{ink:"#888888",effects:{prism:.7,scanlines:.2,vignette:.3}}],["terminal","Phosphor","Green glyphs with a soft glow.",{ink:"#83efad",effects:{bloom:.4,scanlines:.35,crt:.2}}],["mosaic","Mosaic study","Separated color tiles.",{style:"mosaic",cellSize:14,colorMode:"source"}],["bricks","Brickwork","Colorful studded cells.",{style:"lego",cellSize:18,colorMode:"source"}],["voxel","Little volumes","Three-face isometric tiles.",{style:"voxel",cellSize:22,colorMode:"source"}],["disco","Mirror room","Faceted tiles and warm light.",{style:"disco",cellSize:18,colorMode:"source",lights:[{x:.35,y:.25,radius:.25,intensity:.45,color:"#fff2d4"}]}],["wallpaper","Wallpaper","Living ASCII over a softened source.",{backdrop:{mode:"blurred",blur:18,opacity:.55},ink:"#f2e9c9",motion:{type:"caustics",speed:.5}}],["blueprint","Blueprint","Linework in a cool monochrome.",{style:"lines",ink:"#9bccdf",backdrop:{mode:"solid",color:"#0a1e30"}}]].map(([t,e,i,a])=>({id:t,name:e,description:i,settings:l(a)}));function M(t){const e=p.find(e=>e.id===t);if(!e)throw new Error("Unknown studio look.");return l(e.settings)}function x(t,e=Math.random){const i=p.filter(e=>e.settings.style!==t.style||e.settings.ink!==t.ink),a=M(i[Math.min(i.length-1,Math.max(0,Math.floor(e()*i.length)))].id);return a.aspectRatio=t.aspectRatio,a.crop={...t.crop},a.mask=l(t).mask,a}var v=new Set(["none","floyd-steinberg","atkinson","stucki","sierra","sierra-lite","burkes","jarvis"]);function b(t){return"dither"===t.style&&("shimmer"===t.dither.motion||"drift"===t.dither.motion&&t.dither.amount>0&&!v.has(t.dither.algorithm))}function w(t){return"none"!==t.motion.type&&(t.motion.amount??1)>0}var y=[{value:"none",mode:"none",label:"Off",description:"Keep the image still. Hover stays available."},{value:"caustics",mode:"caustics",label:"Caustics",description:"Soft ribbons of light travel across a stationary character grid."},{value:"current",mode:"current",label:"Slow Current",description:"A gentle flow through the image, with steady edges."},{value:"reform",mode:"reform",label:"Reveal & Reform",description:"Disperse, gather, then hold the complete image."},{value:"sheen",mode:"sheen",label:"Sheen",description:"A broad diagonal light sweep; characters stay anchored."},{value:"tidal",mode:"tidal",label:"Tidal Rings",description:"Concentric currents travel outward, with protected edges."},{value:"grain",mode:"grain",label:"Living Grain",description:"Slow, individually phased tonal changes on a stationary grid."}],S=t=>Math.max(0,Math.min(1,t)),k=(t,e,i)=>{const a=S((i-t)/(e-t));return a*a*(3-2*a)},E=(t,e)=>{const i=(.06711056*t+.00583715*e)%1*52.9829189;return i-Math.floor(i)},A=class{constructor(){this.last=null,this.mode="none",this.phase=0}update(t,e,i=1){return Number.isFinite(t)?((e!==this.mode||null!==this.last&&t<this.last)&&(this.mode=e,this.phase=0,this.last=t),null!==this.last&&(this.phase+=Math.max(0,t-this.last)*Math.max(.1,Math.min(3,Number.isFinite(i)?i:1))),this.last=t,this.phase):this.phase}};function F(t,e,i,a,s){if(s[0]=s[1]=s[2]=0,s[3]=1,"none"===t)return s;const r=a*Math.PI/6,o=k(0,.12,Math.min(e,1-e,i,1-i));if("caustics"===t){const t=Math.sin(9*e+5*i+r)+Math.sin(8*i-3*e-r),a=Math.pow(Math.max(0,1-.72*Math.abs(t)),3);s[2]=(.22*a-.045)*o}else if("current"===t)s[0]=Math.sin(9*i+r)*Math.cos(7*e-r)*10*o,s[1]=Math.cos(8*e+r)*Math.sin(6*i-r)*7*o;else if("sheen"===t)s[2]=.2*Math.pow(.5+.5*Math.cos(5*e+3*i-r),8)-.025;else if("tidal"===t){const t=e-.5,a=i-.5,n=Math.hypot(t,a),h=Math.sin(22*n-r)*o;s[0]=t*h*9,s[1]=a*h*9,s[2]=.035*h}else if("grain"===t){const t=E(Math.floor(160*e),Math.floor(100*i))*Math.PI*2;s[2]=.07*Math.sin(r+t)}else if("reform"===t){const t=(a%12+12)%12,n=E(Math.floor(160*e),Math.floor(100*i)),h=.65*e+.2*i+.15*n+.08*Math.sin(8*e-5*i),l=k(1.8+1.2*h,3.8+1.2*h,t)*(1-k(5+1.7*h,7.2+1.7*h,t));s[0]=Math.sin(9*i+r)*l*(1-l)*24*o,s[1]=-l*(1-l)*(8+12*n)*o,s[2]=.45*-l,s[3]=1-.9*l}return s}function T(t,e){const i=Math.max(0,Math.min(2,Number.isFinite(e)?e:1));return t[0]*=i,t[1]*=i,t[2]*=i,t[3]=S(1+(t[3]-1)*i),t}var I=(t,e)=>{const i=43758.5453*Math.sin(127.1*t+311.7*e);return i-Math.floor(i)};var P=new Map([2,4,8,16].map(t=>[t,Float64Array.from({length:t*t},(e,i)=>function(t,e,i){let a=0;for(let s=1;s<i;s*=2){const i=(Math.floor(t/s)%2+2)%2;a=4*a+[[0,2],[3,1]][(Math.floor(e/s)%2+2)%2][i]}return(a+.5)/(i*i)-.5}(i%t,Math.floor(i/t),t))])),R={"floyd-steinberg":[[1,0,7/16],[-1,1,3/16],[0,1,5/16],[1,1,1/16]],atkinson:[[1,0,.125],[2,0,.125],[-1,1,.125],[0,1,.125],[1,1,.125],[0,2,.125]],stucki:[[1,0,8/42],[2,0,4/42],[-2,1,2/42],[-1,1,4/42],[0,1,8/42],[1,1,4/42],[2,1,2/42],[-2,2,1/42],[-1,2,2/42],[0,2,4/42],[1,2,2/42],[2,2,1/42]],sierra:[[1,0,5/32],[2,0,3/32],[-2,1,2/32],[-1,1,4/32],[0,1,5/32],[1,1,4/32],[2,1,2/32],[-1,2,2/32],[0,2,3/32],[1,2,2/32]],"sierra-lite":[[1,0,.5],[-1,1,.25],[0,1,.25]],burkes:[[1,0,.25],[2,0,4/32],[-2,1,2/32],[-1,1,4/32],[0,1,.25],[1,1,4/32],[2,1,2/32]],jarvis:[[1,0,7/48],[2,0,5/48],[-2,1,3/48],[-1,1,5/48],[0,1,7/48],[1,1,5/48],[2,1,3/48],[-2,2,1/48],[-1,2,3/48],[0,2,5/48],[1,2,3/48],[2,2,1/48]]},C=t=>[parseInt(t.slice(1,3),16),parseInt(t.slice(3,5),16),parseInt(t.slice(5,7),16)];function L(t,e,i){const a=Math.min(t*Math.max(1,i),(1-t)*Math.max(1,i),e*Math.max(1,1/i),(1-e)*Math.max(1,1/i)),s=Math.max(0,Math.min(1,(a-.035)/.105));return s*s*s*(s*(6*s-15)+10)}var D=class{constructor(t,e){this.edgeSafe=!1,this.previous=null,this.peak=0,this.scatterDirections=null,this.mode="dissolve",this.width=t,this.height=e;const i=t*e;this.pixels=new Uint8Array(4*i),this.ink=new Float32Array(i),this.targetX=new Float32Array(i),this.targetY=new Float32Array(i),this.offsetX=new Float32Array(i),this.offsetY=new Float32Array(i),this.edge=new Float32Array(i);for(let i=0;i<e;i++)for(let a=0;a<t;a++)this.edge[i*t+a]=L(a/(t-1),i/(e-1),t/e);this.clear()}get active(){return this.peak>2e-4}setMode(t){if("scatter"===t&&!this.scatterDirections){const t=this.scatterDirections=new Float64Array(this.width*this.height*2);for(let e=0;e<this.height;e++)for(let i=0;i<this.width;i++){const a=43758.5453*Math.sin(127.1*i+311.7*e),s=(a-Math.floor(a))*Math.PI*2,r=2*(e*this.width+i);t[r]=.7*Math.cos(s),t[r+1]=.7*Math.sin(s)}}t!==this.mode&&(this.mode=t,this.clear())}clear(){for(const t of[this.ink,this.targetX,this.targetY,this.offsetX,this.offsetY,this.pixels])t.fill(0);if(this.previous=null,this.peak=0,"dissolve"!==this.mode)for(let t=0;t<this.pixels.length;t+=4)this.pixels[t]=this.pixels[t+2]=128}leave(){this.previous=null}move(t,e,i){if(!Number.isFinite(t+e+i))return;t=Math.max(0,Math.min(1,t)),e=Math.max(0,Math.min(1,e));const a=this.previous;this.previous={x:t,y:e};const s=this.width,r=this.height,o=Math.max(1,s/r),n=Math.max(1,r/s),h=a?(t-a.x)*o:0,l=a?(e-a.y)*n:0,c=Math.hypot(h,l);if(!a&&"dissolve"!==this.mode)return;if(a&&c<1e-4)return;const f=.035+.11*Math.max(.1,Math.min(1,i)),m=Math.min(128,Math.max(1,Math.ceil(c/(.45*f)))),d=c?h/c:1,u=c?l/c:0,g=Math.min(1,c/(f*m)*2.2);for(let i=0;i<m;i++){const h=(i+.5)/m,l=a?a.x+(t-a.x)*h:t,c=a?a.y+(e-a.y)*h:e,p=Math.max(0,Math.floor((l-2.5*f/o)*(s-1))),M=Math.min(s-1,Math.ceil((l+2.5*f/o)*(s-1))),x=Math.max(0,Math.floor((c-2.5*f/n)*(r-1))),v=Math.min(r-1,Math.ceil((c+2.5*f/n)*(r-1)));for(let t=x;t<=v;t++)for(let e=p;e<=M;e++){const i=(e/(s-1)-l)*o/f,a=(t/(r-1)-c)*n/f,h=Math.exp(1.5*-(i*i+a*a)),m=t*s+e;if("dissolve"===this.mode)this.ink[m]=Math.max(this.ink[m],h);else{const t=(-i*u+a*d)*h*2.8,e="magnetic"===this.mode?-i*h*2.5:"scatter"===this.mode?(i+this.scatterDirections[2*m])*h*2:"silk"===this.mode?d*t:-a*h*2.5,s="magnetic"===this.mode?-a*h*2.5:"scatter"===this.mode?(a+this.scatterDirections[2*m+1])*h*2:"silk"===this.mode?u*t:i*h*2.5;this.targetX[m]=Math.max(-1,Math.min(1,this.targetX[m]+e*g)),this.targetY[m]=Math.max(-1,Math.min(1,this.targetY[m]+s*g))}}}this.peak=1}step(t){if(!this.active||!Number.isFinite(t))return;const e=Math.max(0,Math.min(.05,t)),i=Math.exp(-("dissolve"===this.mode?2.15:3.2)*e),a=1-Math.exp(-22*e);let s=0;for(let t=0;t<this.ink.length;t++)if("dissolve"===this.mode){this.ink[t]*=i,s=Math.max(s,this.ink[t]);const e=Math.round(65535*this.ink[t]);this.pixels[4*t]=e>>>8,this.pixels[4*t+1]=255&e}else{this.targetX[t]*=i,this.targetY[t]*=i,this.offsetX[t]+=(this.targetX[t]-this.offsetX[t])*a,this.offsetY[t]+=(this.targetY[t]-this.offsetY[t])*a,s=Math.max(s,Math.abs(this.offsetX[t]),Math.abs(this.offsetY[t]),Math.abs(this.targetX[t]),Math.abs(this.targetY[t]));const e=Math.round(this.offsetX[t]*(this.edgeSafe?this.edge[t]:1)*32767+32768),r=Math.round(this.offsetY[t]*(this.edgeSafe?this.edge[t]:1)*32767+32768);this.pixels[4*t]=e>>>8,this.pixels[4*t+1]=255&e,this.pixels[4*t+2]=r>>>8,this.pixels[4*t+3]=255&r}this.peak=s,this.active||this.clear()}sample(t,e,i){const a=Math.max(0,Math.min(this.width-1.001,t*(this.width-1))),s=Math.max(0,Math.min(this.height-1.001,e*(this.height-1))),r=Math.floor(a),o=Math.floor(s),n=a-r,h=s-o,l=o*this.width+r,c=l+this.width,f=t=>(t[l]*(1-n)+t[l+1]*n)*(1-h)+(t[c]*(1-n)+t[c+1]*n)*h;i[0]=f(this.offsetX)*(this.edgeSafe?L(t,e,this.width/this.height):1)*.08,i[1]=f(this.offsetY)*(this.edgeSafe?L(t,e,this.width/this.height):1)*.08,i[2]="dissolve"===this.mode?6*-f(this.ink):0}},N=class{constructor(t,e){this.rings=[],this.previous=null,this.elapsed=1,this.echoPeak=0,this.width=t,this.height=e,this.pixels=new Uint8Array(t*e*4),this.echo=new Float32Array(t*e)}get active(){return this.rings.length>0||this.echoPeak>2e-4}clear(){this.rings=[],this.previous=null,this.elapsed=1,this.pixels.fill(0),this.echo.fill(0),this.echoPeak=0}leave(){this.previous=null}move(t,e,i){const a=Math.max(1,this.width/this.height),s=Math.max(1,this.height/this.width);this.previous&&(Math.hypot((t-this.previous.x)*a,(e-this.previous.y)*s)<.065||this.elapsed<.3)||4!==this.rings.length&&(this.previous={x:t,y:e},this.elapsed=0,this.rings.push({x:t,y:e,age:0,radius:i}))}ringSample(t,e){const i=Math.max(1,this.width/this.height),a=Math.max(1,this.height/this.width);let s=0;for(const r of this.rings){const o=.015+r.age*(.15+.22*r.radius),n=.009+.014*r.radius,h=(Math.hypot((t-r.x)*i,(e-r.y)*a)-o)/n,l=Math.sin(Math.min(1,r.age/.07)*Math.PI/2)*Math.max(0,1-r.age/1.35)**2;s=Math.max(s,Math.exp(-h*h)*l)}return s}sample(t,e){const i=Math.max(0,Math.min(this.width-1,Math.round(t*(this.width-1)))),a=Math.max(0,Math.min(this.height-1,Math.round(e*(this.height-1))));return Math.max(this.ringSample(t,e),this.echo[a*this.width+i])}step(t){if(!Number.isFinite(t))return;const e=Math.max(0,Math.min(.05,t));this.elapsed+=e;for(const t of this.rings)t.age+=e;if(this.rings=this.rings.filter(t=>t.age<1.35),!this.active)return void this.pixels.fill(0);const i=Math.exp(-5.5*e);let a=0;for(let t=0;t<this.height;t++)for(let e=0;e<this.width;e++){const s=t*this.width+e;this.echo[s]=Math.max(this.ringSample(e/(this.width-1),t/(this.height-1)),this.echo[s]*i),a=Math.max(a,this.echo[s]);const r=Math.round(65535*this.echo[s]),o=4*s;this.pixels[o]=r>>>8,this.pixels[o+1]=255&r}this.echoPeak=a,this.active||(this.echo.fill(0),this.pixels.fill(0))}},U=class{constructor(t,e){this.previous=null,this.peak=0,this.width=t,this.height=e;const i=t*e;this.pixels=new Uint8Array(4*i),this.signal=new Float32Array(i),this.offsetX=new Float32Array(i),this.offsetY=new Float32Array(i),this.driftX=new Float32Array(i),this.driftY=new Float32Array(i),this.u=new Float32Array(i),this.v=new Float32Array(i),this.ink=new Float32Array(i),this.a=new Float32Array(i),this.b=new Float32Array(i),this.c=new Float32Array(i),this.pressure=new Float32Array(i),this.pressureNext=new Float32Array(i),this.divergence=new Float32Array(i),this.curl=new Float32Array(i)}get active(){return this.peak>2e-4}clear(){for(const t of[this.u,this.v,this.ink,this.a,this.b,this.c,this.pressure,this.pressureNext,this.divergence,this.curl,this.signal,this.pixels,this.offsetX,this.offsetY,this.driftX,this.driftY])t.fill(0);this.previous=null,this.peak=0}leave(){this.previous=null}move(t,e,i,a=performance.now()){if(!Number.isFinite(t+e+i+a))return;t=Math.max(0,Math.min(1,t)),e=Math.max(0,Math.min(1,e)),i=Math.max(.1,Math.min(1,i));const s=this.previous;if(this.previous={x:t,y:e,time:a},!s)return;const r=(t-s.x)*(this.width-1),o=(e-s.y)*(this.height-1),n=Math.hypot(r,o);if(n<.001)return;const h=Math.min(this.width,this.height),l=a>s.time?Math.max(1/240,(a-s.time)/1e3):1/60,c=Math.min(12,n/h/l),f=1-Math.exp(-c/2.2),m=h*(.022+.075*i)*(.65+.95*f),d=Math.min(128,Math.max(1,Math.ceil(n/(.4*m)))),u=n/m*(.32+.22*f)/d,g=r/n,p=o/n,M=-p,x=g,v=n*(2+18*f)/d;for(let i=0;i<d;i++){const a=(i+.5)/d,r=(s.x+(t-s.x)*a)*(this.width-1),o=(s.y+(e-s.y)*a)*(this.height-1),n=Math.max(1,Math.floor(r-3*m)),h=Math.min(this.width-2,Math.ceil(r+3*m)),l=Math.max(1,Math.floor(o-3*m)),c=Math.min(this.height-2,Math.ceil(o+3*m));for(let t=l;t<=c;t++)for(let e=n;e<=h;e++){const i=((e-r)*g+(t-o)*p)/m,a=((e-r)*M+(t-o)*x)/m,s=Math.exp(-i*i*.65-a*a*1.7),n=t*this.width+e;this.ink[n]=1-(1-this.ink[n])*Math.exp(-s*u);const h=1-a*a*.55,l=i*a*.7;this.u[n]=Math.max(-80,Math.min(80,this.u[n]+(g*h+M*l)*s*v)),this.v[n]=Math.max(-80,Math.min(80,this.v[n]+(p*h+x*l)*s*v))}}this.peak=1}read(t,e,i){const a=0|(e=Math.max(.5,Math.min(this.width-1.5,e))),s=0|(i=Math.max(.5,Math.min(this.height-1.5,i))),r=e-a,o=i-s,n=s*this.width+a;return(t[n]*(1-r)+t[n+1]*r)*(1-o)+(t[n+this.width]*(1-r)+t[n+this.width+1]*r)*o}sample(t,e){return this.read(this.signal,t*(this.width-1),e*(this.height-1))}displacement(t,e,i){i[0]=this.read(this.offsetX,t*(this.width-1),e*(this.height-1)),i[1]=this.read(this.offsetY,t*(this.width-1),e*(this.height-1))}step(t){if(!this.active||!Number.isFinite(t))return;let e=Math.max(0,Math.min(.05,t));const i=this.width,a=this.height;for(;e>1e-7;){const t=Math.min(1/60,e);e-=t;const s=Math.exp(-1.9*t),r=Math.exp(-3.2*t);for(let e=1;e<a-1;e++)for(let o=1;o<i-1;o++){const n=e*i+o,h=Math.max(.5,Math.min(i-1.5,o-this.u[n]*t)),l=Math.max(.5,Math.min(a-1.5,e-this.v[n]*t)),c=0|h,f=0|l,m=h-c,d=l-f,u=f*i+c,g=(1-m)*(1-d),p=m*(1-d),M=(1-m)*d,x=m*d;this.a[n]=(this.u[u]*g+this.u[u+1]*p+this.u[u+i]*M+this.u[u+i+1]*x)*s,this.b[n]=(this.v[u]*g+this.v[u+1]*p+this.v[u+i]*M+this.v[u+i+1]*x)*s,this.c[n]=(this.ink[u]*g+this.ink[u+1]*p+this.ink[u+i]*M+this.ink[u+i+1]*x)*r}[this.u,this.a]=[this.a,this.u],[this.v,this.b]=[this.b,this.v],[this.ink,this.c]=[this.c,this.ink];for(let t=1;t<a-1;t++)for(let e=1;e<i-1;e++){const a=t*i+e;this.curl[a]=.5*(this.v[a+1]-this.v[a-1]-this.u[a+i]+this.u[a-i])}for(let e=1;e<a-1;e++)for(let a=1;a<i-1;a++){const s=e*i+a,r=Math.abs(this.curl[s+1])-Math.abs(this.curl[s-1]),o=Math.abs(this.curl[s+i])-Math.abs(this.curl[s-i]),n=Math.hypot(r,o)+1e-4,h=this.curl[s]*t*10,l=Math.hypot(this.u[s],this.v[s]);this.u[s]+=o/n*h,this.v[s]-=r/n*h;const c=Math.min(1,l/(Math.hypot(this.u[s],this.v[s])+1e-6));this.u[s]*=c,this.v[s]*=c,this.divergence[s]=-.5*(this.u[s+1]-this.u[s-1]+this.v[s+i]-this.v[s-i])}this.pressure.fill(0);for(let t=0;t<4;t++){for(let t=1;t<a-1;t++)for(let e=1;e<i-1;e++){const a=t*i+e;this.pressureNext[a]=.25*(this.divergence[a]+this.pressure[a-1]+this.pressure[a+1]+this.pressure[a-i]+this.pressure[a+i])}[this.pressure,this.pressureNext]=[this.pressureNext,this.pressure]}let o=0;for(let e=1;e<a-1;e++)for(let a=1;a<i-1;a++){const s=e*i+a;this.u[s]=Math.max(-80,Math.min(80,this.u[s]-.5*(this.pressure[s+1]-this.pressure[s-1]))),this.v[s]=Math.max(-80,Math.min(80,this.v[s]-.5*(this.pressure[s+i]-this.pressure[s-i]))),this.driftX[s]+=(3.6*this.u[s]-58*this.offsetX[s]-12*this.driftX[s])*t,this.driftY[s]+=(3.6*this.v[s]-58*this.offsetY[s]-12*this.driftY[s])*t,this.offsetX[s]=Math.max(-2,Math.min(2,this.offsetX[s]+this.driftX[s]*t)),this.offsetY[s]=Math.max(-2,Math.min(2,this.offsetY[s]+this.driftY[s]*t)),this.signal[s]=1-Math.exp(.06*-Math.hypot(this.u[s],this.v[s])-.16*this.ink[s]),o=Math.max(o,this.signal[s])}this.peak=o}if(this.active)for(let t=0;t<this.ink.length;t++){const e=Math.round(65535*Math.min(1,this.signal[t]));this.pixels[4*t]=e>>>8,this.pixels[4*t+1]=255&e,this.pixels[4*t+2]=Math.round(128+63.5*this.offsetX[t]),this.pixels[4*t+3]=Math.round(128+63.5*this.offsetY[t])}else this.clear()}};function X(t,e){const i=Math.max(0,Math.min(1,e/.38));return t+(1-2*t)*(i*i*(3-2*i))}var B=class{constructor(t=1){this.mode="water",this.amount=.55,this.radius=.2,this.lensX=.5,this.lensY=.5,this.lensStrength=0,this.previous=null,this.energy=0,this.pending=!1,this.remainder=0,t=Math.max(.25,Math.min(4,t)),this.columns=Math.round(t>=1?128:128*t),this.rows=Math.round(t>=1?128/t:128),this.xScale=Math.max(1,t),this.yScale=Math.max(1,1/t),this.heights=new Float32Array(this.columns*this.rows),this.velocity=new Float32Array(this.heights.length),this.next=new Float32Array(this.heights.length),this.normals=new Float32Array(2*this.heights.length),this.edgeMask=new Float32Array(this.heights.length);for(let e=0;e<this.rows;e++)for(let i=0;i<this.columns;i++)this.edgeMask[e*this.columns+i]=L(i/(this.columns-1),e/(this.rows-1),t);this.refraction={width:this.columns,height:this.rows,pixels:new Uint8Array(4*this.heights.length),strength:30,mode:"water",focus:[.5,.5,0,.2]},this.waterPixels=this.refraction.pixels,this.trail=new U(this.columns,this.rows),this.contour=new N(this.columns,this.rows),this.afterimage=new D(this.columns,this.rows),this.clear()}get invertsDensity(){return"trail"===this.mode}get densityTrail(){return["trail","contour","dissolve"].includes(this.mode)}get hasRefraction(){return this.active||this.lensStrength>15e-5}get active(){return["dissolve","silk","vortex","magnetic","scatter"].includes(this.mode)?this.afterimage.active:"contour"===this.mode?this.contour.active:"trail"===this.mode?this.trail.active:this.pending||this.energy>15e-5}setMode(t){t!==this.mode&&(this.clear(),this.mode=t,this.refraction.mode=t,"dissolve"!==t&&"silk"!==t&&"vortex"!==t&&"magnetic"!==t&&"scatter"!==t||this.afterimage.setMode(t),this.refraction.pixels="trail"===t?this.trail.pixels:"contour"===t?this.contour.pixels:["dissolve","silk","vortex","magnetic","scatter"].includes(t)?this.afterimage.pixels:this.waterPixels,this.refraction.strength="water"===t?30*this.amount/.55:"silk"===t?28*this.amount:"vortex"===t?38*this.amount:"magnetic"===t||"scatter"===t?32*this.amount:0)}configure(t,e=.55,i=.2,a=!1){this.setMode(t),this.refraction.edgeSafe=a,this.afterimage.edgeSafe=a,this.amount=Number.isFinite(e)?Math.max(0,Math.min(1,e)):.55,this.radius=Number.isFinite(i)?Math.max(.1,Math.min(1,i)):.2,this.refraction.strength="water"===t?30*this.amount/.55:"silk"===t?28*this.amount:"vortex"===t?38*this.amount:"magnetic"===t||"scatter"===t?32*this.amount:0,this.refraction.focus[2]=this.densityTrail?this.amount:this.lensStrength*this.amount,this.refraction.focus[3]=this.radius}move(t,e,i){if("none"===this.mode||0===this.amount)return;if(!Number.isFinite(t+e))return;if(t=Math.max(0,Math.min(1,t)),e=Math.max(0,Math.min(1,e)),["dissolve","silk","vortex","magnetic","scatter"].includes(this.mode))return void this.afterimage.move(t,e,this.radius);if("contour"===this.mode)return void this.contour.move(t,e,this.radius);if("trail"===this.mode)return void this.trail.move(t,e,this.radius,i);if("water"!==this.mode)return!this.previous&&this.lensStrength<.001&&(this.lensX=t,this.lensY=e),this.previous={x:t,y:e},void(this.pending=!0);if(!this.previous)return void(this.previous={x:t,y:e});const a=this.previous;this.previous={x:t,y:e};const s=(t-a.x)*this.xScale,r=(e-a.y)*this.yScale,o=Math.hypot(s,r);if(o<1e-5)return;const n=.035+.1*this.radius,h=3*n,l=Math.min(192,Math.max(1,Math.ceil(o/(.5*n)))),c=95*Math.min(o,1)/l;for(let i=0;i<l;i++){const s=(i+.5)/l,r=a.x+(t-a.x)*s,o=a.y+(e-a.y)*s,f=Math.max(1,Math.floor((r-h/this.xScale)*(this.columns-1))),m=Math.min(this.columns-2,Math.ceil((r+h/this.xScale)*(this.columns-1))),d=Math.max(1,Math.floor((o-h/this.yScale)*(this.rows-1))),u=Math.min(this.rows-2,Math.ceil((o+h/this.yScale)*(this.rows-1)));for(let t=d;t<=u;t++)for(let e=f;e<=m;e++){const i=(e/(this.columns-1)-r)*this.xScale,a=(t/(this.rows-1)-o)*this.yScale,s=Math.exp(-(i*i+a*a)/(n*n))*c,h=t*this.columns+e;this.velocity[h]=Math.max(-12,this.velocity[h]-s)}}this.pending=!0}leave(){this.afterimage.leave(),this.contour.leave(),this.trail.leave(),this.previous=null,"water"!==this.mode&&this.lensStrength&&(this.pending=!0)}clear(){this.trail.clear(),this.contour.clear(),this.afterimage.clear(),this.heights.fill(0),this.velocity.fill(0),this.next.fill(0),this.normals.fill(0),this.refraction.focus[2]=0,this.previous=null,this.lensStrength=0,this.energy=0,this.pending=!1,this.remainder=0;for(let t=0;t<this.waterPixels.length;t+=4)this.waterPixels[t]=this.waterPixels[t+2]=128,this.waterPixels[t+1]=this.waterPixels[t+3]=0}step(t){if(!this.active||!Number.isFinite(t))return;if(["dissolve","silk","vortex","magnetic","scatter"].includes(this.mode))return void this.afterimage.step(t);if("contour"===this.mode)return void this.contour.step(t);if("trail"===this.mode)return void this.trail.step(t);if("water"!==this.mode)return void this.stepLight(Math.max(0,Math.min(.05,t)));this.remainder+=Math.max(0,Math.min(.05,t));const e=1/120,i=Math.exp(-3.8*e),a=.3*Math.min(this.columns,this.rows);for(;this.remainder+1e-8>=e;){this.remainder-=e;let t=0;for(let s=1;s<this.rows-1;s++)for(let r=1;r<this.columns-1;r++){const o=s*this.columns+r,n=this.heights[o],h=this.heights[o-1]+this.heights[o+1]+this.heights[o-this.columns]+this.heights[o+this.columns]-4*n,l=Math.min(r,s,this.columns-1-r,this.rows-1-s),c=l<5?.88+.024*l:1,f=(this.velocity[o]+(h*a*a-8*n)*e)*i*c;this.velocity[o]=f,this.next[o]=(n+f*e)*c,t=Math.max(t,Math.abs(this.next[o]),.1*Math.abs(f))}this.heights.set(this.next),this.energy=t,this.pending=!1}if(!this.active)return void this.clear();const s=.18*Math.min(this.columns,this.rows);for(let t=0;t<this.rows;t++)for(let e=0;e<this.columns;e++){const i=t*this.columns+e,a=(this.heights[t*this.columns+Math.max(0,e-1)]-this.heights[t*this.columns+Math.min(this.columns-1,e+1)])*s,r=(this.heights[Math.max(0,t-1)*this.columns+e]-this.heights[Math.min(this.rows-1,t+1)*this.columns+e])*s,o=Math.tanh(a)*(this.refraction.edgeSafe?this.edgeMask[i]:1),n=Math.tanh(r)*(this.refraction.edgeSafe?this.edgeMask[i]:1);this.normals[2*i]=o,this.normals[2*i+1]=n;const h=Math.round(32767*o+32768),l=Math.round(32767*n+32768);this.refraction.pixels[4*i]=h>>>8,this.refraction.pixels[4*i+1]=255&h,this.refraction.pixels[4*i+2]=l>>>8,this.refraction.pixels[4*i+3]=255&l}}stepLight(t){const e=this.previous,i=1-Math.exp(-30*t),a=1-Math.exp(-12*t);e&&(this.lensX+=(e.x-this.lensX)*i,this.lensY+=(e.y-this.lensY)*i),this.lensStrength+=((e?1:0)-this.lensStrength)*a,this.energy=Math.max(e?Math.abs(e.x-this.lensX)+Math.abs(e.y-this.lensY):0,Math.abs((e?1:0)-this.lensStrength)),this.pending=!1,e||this.active?this.refraction.focus=[this.lensX,this.lensY,this.lensStrength*this.amount,this.radius]:this.clear()}sample(t,e,i){if(["dissolve","silk","vortex","magnetic","scatter"].includes(this.mode))return this.afterimage.sample(t,e,i),i[0]*=this.amount,i[1]*=this.amount,void(i[2]*=this.amount);if("contour"===this.mode)return i[0]=i[1]=0,void(i[2]=this.contour.sample(t,e)*this.amount*3);if("trail"===this.mode)return i[0]=i[1]=0,void(i[2]=this.trail.sample(t,e)*this.amount*3);const a=Math.max(0,Math.min(this.columns-1.001,t*(this.columns-1))),s=Math.max(0,Math.min(this.rows-1.001,e*(this.rows-1))),r=Math.floor(a),o=Math.floor(s),n=a-r,h=s-o,l=2*(o*this.columns+r),c=l+2*this.columns;for(let t=0;t<2;t++)i[t]=.08*((this.normals[l+t]*(1-n)+this.normals[l+2+t]*n)*(1-h)+(this.normals[c+t]*(1-n)+this.normals[c+2+t]*n)*h);i[0]*=this.amount/.55,i[1]*=this.amount/.55,i[2]="light"===this.mode||"scan"===this.mode?function(t,e,i,a){const[s,r,o,n]=i.focus,h=(t-s)*Math.max(1,a)/n,l=(e-r)*Math.max(1,1/a)/n;return"light"===i.mode?Math.exp(-3*(h*h+l*l))*o:"scan"===i.mode?(1.3*Math.exp(-h*h*110)-.3*Math.exp(-h*h*12))*Math.exp(-l*l*1.8)*o:0}(t,e,this.refraction,this.xScale/this.yScale):0}};function O(t){return"videoWidth"in t?[t.videoWidth,t.videoHeight]:"naturalWidth"in t?[t.naturalWidth,t.naturalHeight]:[t.width,t.height]}var Y=(t=!1)=>{const e=document.createElement("canvas"),i=e.getContext("2d",{willReadFrequently:t});if(!i)throw new Error("Canvas 2D is unavailable.");return{canvas:e,ctx:i}},z=(t,e,i)=>{t.width===e&&t.height===i||(t.width=e,t.height=i)},_={blocks:" ░▒▓█",braille:" ⠁⠃⠇⡇⣇⣧⣷⣿",lines:" -═╪╬",cross:" ·+xX#",diagonal:" /╱╳",diamond:" ·◇◈◆",mixed:" .:+/◇#@"};function W(t,e={},i={}){const s=t.getContext("2d");if(!s)throw new Error("Canvas is already owned by another renderer.");const r=Y(),o=Y(!0),n=Y(),h=Y(),c=Y(),f=Y(),d=Y(),u=Y(),p=Y(),M=Y();let x,v=0,y=l(e),S=0,k="",E="",A="",L=new Uint8ClampedArray(0),D=new Uint8ClampedArray(0),N=0,U=0,W=0,q=null,G="",j=new B,V=0;const H=[0,0,0],$=[0,0,0,1],Q=Math.max(64,Math.min(4096,Number.isFinite(i.maxDimension)?i.maxDimension:960)),J=Math.max(256,Math.min(1048576,Number.isFinite(i.maxCells)?i.maxCells:12e3));let K=J,Z=Math.max(.1,Math.min(8,i.pixelRatio??1));return{canvas:t,render:function(e,i=0,l=960,Y=540,J=i*y.motion.speed){if(Number.isFinite(i)||(i=0),!Number.isFinite(l)||!Number.isFinite(Y)||l<1||Y<1)throw new Error("Render dimensions must be positive finite numbers.");const[tt,et]=O(e);if(!tt||!et)return;const it=Math.min(1,Q/Math.max(l,Y)),at=Math.max(2,Math.round(l*it)),st=Math.max(2,Math.round(Y*it));z(t,at,st),z(r.canvas,at,st),z(n.canvas,at,st),z(h.canvas,at,st),z(c.canvas,at,st),V!==at/st&&(V=at/st,j=new B(V)),j.configure(y.hover.effect,y.hover.strength,y.hover.radius,y.hover.edgeSafe),j.step(Math.max(0,Math.min(.05,i-W))),W=i;const rt="currentTime"in e?e.getVideoPlaybackQuality?.().totalVideoFrames??e.currentTime:0,ot=[S,at,st,rt].join(":"),nt="dither"===y.style,ht=g(at,st,y,K,Z),lt=ht.cell,ct=ht.rowHeight,ft=ht.columns,mt=ht.rows;if(k!==ot||q!==e||N!==ft||U!==mt){q=e,k=ot,E="",A="",N=ft,U=mt,r.ctx.clearRect(0,0,at,st),r.ctx.save();const t=m(tt,et,at,st,y.crop);r.ctx.translate(t.x+t.width/2,t.y+t.height/2),r.ctx.rotate(y.crop.rotation*Math.PI/180),r.ctx.drawImage(e,-tt*t.scale/2,-et*t.scale/2,tt*t.scale,et*t.scale),r.ctx.restore(),z(o.canvas,N,U),o.ctx.clearRect(0,0,N,U),o.ctx.drawImage(r.canvas,0,0,N,U),L=o.ctx.getImageData(0,0,N,U).data;const i=y.color;for(let t=0;t<L.length;t+=4){const e=.299*L[t]+.587*L[t+1]+.114*L[t+2];for(let a=0;a<3;a++){const s=e+(L[t+a]-e)*i.saturation;L[t+a]=((s*(1-i.grayscale)+e*i.grayscale)/255-.5)*i.contrast*255+127.5+255*i.brightness}}}const dt=w(y),ut=b(y),gt=!j.active&&!dt&&!ut,pt=[ot,N,U,lt,ct,Z].join(":");if(!gt||A!==pt){n.ctx.clearRect(0,0,at,st),n.ctx.globalAlpha=1,n.ctx.globalCompositeOperation="source-over";const t=J,e=dt?y.motion.type:"none",s="none"===e||"caustics"===e||"sheen"===e||"grain"===e;if(nt){const s=gt;if(!s||E!==ot){D.length!==L.length&&(D=new Uint8ClampedArray(L.length));const r=D;if(r.set(L),j.active||"none"!==e)for(let i=0;i<U;i++)for(let a=0;a<N;a++){const s=a/N,o=i/U;let n=a,h=i,l=0,c=1,f=1;j.active&&(j.sample(s,o,H),n-=H[0]*N,h-=H[1]*U,l=H[2]/3),F(e,s,o,t,$),T($,y.motion.amount??1),n-=$[0]/960*N,h-=$[1]/960*U,c=1+$[2],f=$[3],n=Math.max(0,Math.min(N-1,n)),h=Math.max(0,Math.min(U-1,h));const m=Math.floor(n),d=Math.floor(h),u=n-m,g=h-d,p=4*(i*N+a),M=4*(d*N+m),x=4*(d*N+Math.min(N-1,m+1)),v=4*(Math.min(U-1,d+1)*N+m),b=4*(Math.min(U-1,d+1)*N+Math.min(N-1,m+1));for(let t=0;t<3;t++){const e=((L[M+t]*(1-u)+L[x+t]*u)*(1-g)+(L[v+t]*(1-u)+L[b+t]*u)*g)/255;let i=(["trail","contour"].includes(y.hover.effect)?X(e,l):"dissolve"===y.hover.effect?e*Math.max(0,1+l):e)*c;r[p+t]=255*i}r[p+3]=L[M+3]*f}!function(t,e,i,s,r=0){const{algorithm:o,amount:n,threshold:h}=s,l=o.startsWith("bayer")?Number(o.slice(5)):0,c=P.get(l),f=l-1,m="luminance"===s.colorSpace,d=("custom"===s.palette?s.colors:a[s.palette]??a.mono).map(C),u=R[o],g=u?new Float32Array(3*e*3):null,p="drift"===s.motion?r*s.speed*3:0,M=(s.direction??45)*Math.PI/180,x=Math.round(Math.cos(M)*p),v=Math.round(Math.sin(M)*p),b=(t,e)=>(t%e+e)%e,w=d.map(t=>.299*t[0]+.587*t[1]+.114*t[2]),y="shimmer"===s.motion?.15*Math.sin(r*s.speed*2):0;for(let a=0;a<i;a++)for(let s=0;s<e;s++){const r=4*(a*e+s),M=3*(a%3*e+s);if(0===t[r+3]){g&&(g[M]=g[M+1]=g[M+2]=0);continue}let S=0;c?S=c[(a-v&f)*l+(s-x&f)]:"noise"===o?S=I(s-x,a-v)-.5:"lines"===o?S=b(a-v,4)/3-.5:"vertical-lines"===o?S=b(s-x,4)/3-.5:"diagonal-lines"===o?S=b(s-x+a-v,4)/3-.5:"radial"===o?S=b(Math.hypot(s-e/2,a-i/2)-p,6)/6-.5:"halftone"===o&&(S=Math.hypot(b(s-x,6)-2.5,b(a-v,6)-2.5)/3.54-.5);const k=128*(S*n+y+(.5-h)),E=t[r]+k+(g?.[M]??0),A=t[r+1]+k+(g?.[M+1]??0),F=t[r+2]+k+(g?.[M+2]??0);let T=0,P=0,R=0;if(d.length){let t=1/0;const e=.299*E+.587*A+.114*F;for(let i=0;i<d.length;i++){const a=d[i],s=m?(e-w[i])**2:(E-a[0])**2*.299+(A-a[1])**2*.587+(F-a[2])**2*.114;s<t&&(t=s,[T,P,R]=a)}}else T=51*Math.round(E/51),P=51*Math.round(A/51),R=51*Math.round(F/51);if(t[r]=T,t[r+1]=P,t[r+2]=R,g&&u){g[M]=g[M+1]=g[M+2]=0;for(const[t,r,o]of u){const h=s+t;if(h<0||h>=e||a+r>=i)continue;const l=3*((a+r)%3*e+h);g[l]+=(E-T)*o*n,g[l+1]+=(A-P)*o*n,g[l+2]+=(F-R)*o*n}}}}(r,N,U,y.dither,i),o.ctx.putImageData(new ImageData(r,N,U),0,0),E=s?ot:""}n.ctx.imageSmoothingEnabled=!1,n.ctx.drawImage(o.canvas,0,0,at,st)}else{const i=Array.from(_[y.style]??y.charset),a=n.ctx,r=C(y.ink),o="ascii"===y.style||!!_[y.style],h=Math.max(3,Math.round(.92*ct)),l=Math.ceil(lt+4),c=Math.ceil(ct+4),f=[i.join(""),h,l,c,y.ink,y.colorMode].join(":");if(o&&f!==G&&(G=f,z(d.canvas,l*i.length,c),d.ctx.clearRect(0,0,d.canvas.width,c),d.ctx.font=`${h}px monospace`,d.ctx.textBaseline="middle",d.ctx.textAlign="center",d.ctx.fillStyle="accent"===y.colorMode?y.ink:"#ffffff",i.forEach((t,e)=>d.ctx.fillText(t,e*l+l/2,c/2))),"lego"===y.style&&v!==lt){v=lt,z(M.canvas,Math.ceil(lt),Math.ceil(ct));const t=M.ctx;t.clearRect(0,0,M.canvas.width,M.canvas.height),t.fillStyle="rgba(255,255,255,.2)",t.strokeStyle="rgba(0,0,0,.25)",t.lineWidth=Math.max(.5,.6*Z),t.beginPath(),t.arc(lt/2,ct/2,.28*lt,0,2*Math.PI),t.fill(),t.stroke()}a.font=`${h}px monospace`,a.textBaseline="middle",a.textAlign="center";for(let n=0;n<U;n++)for(let h=0;h<N;h++){let f=h,m=n,u=0;j.active&&(j.sample(h/N,n/U,H),f=Math.max(0,Math.min(N-1,h-H[0]*N)),m=Math.max(0,Math.min(U-1,n-H[1]*U)),u=H[2]/3);const g=4*(Math.floor(m)*N+Math.floor(f));if(!L[g+3])continue;const p=L[g],x=L[g+1],v=L[g+2],b=(.299*p+.587*x+.114*v)/255;let w=b;["trail","contour"].includes(y.hover.effect)&&u?w=X(w,u):"dissolve"===y.hover.effect&&(w*=Math.max(0,1+u));let S=h*lt,k=n*ct,E=1;F(e,h/N,n/U,t,$),T($,y.motion.amount??1),S+=$[0]*at/960,k+=$[1]*st/960,w=Math.max(0,Math.min(1,w+$[2])),E=$[3],o||"accent"!==y.colorMode||"cmyk"===y.style||(E*=w),a.globalAlpha=E;const A=255*(w-b),P="source"===y.colorMode?`rgb(${p+A},${x+A},${v+A})`:"gray"===y.colorMode?`rgb(${Math.round(255*w)},${Math.round(255*w)},${Math.round(255*w)})`:y.ink;if(a.fillStyle=P,o){const t=Math.max(0,Math.min(i.length-1,Math.round(w*(i.length-1))));"accent"===y.colorMode?a.drawImage(d.canvas,t*l,0,l,c,s?Math.round(S+(lt-l)/2):S+(lt-l)/2,s?Math.round(k+(ct-c)/2):k+(ct-c)/2,l,c):a.fillText(i[t],S+lt/2,k+ct/2)}else if("dots"===y.style)a.beginPath(),a.arc(S+lt/2,k+ct/2,.48*lt*Math.sqrt(w),0,2*Math.PI),a.fill();else if("led"===y.style)a.beginPath(),a.arc(S+lt/2,k+ct/2,.39*lt,0,2*Math.PI),a.fill(),a.globalAlpha=.35*E,a.fillStyle="#ffffff",a.beginPath(),a.arc(S+.43*lt,k+.4*ct,.11*lt,0,2*Math.PI),a.fill();else if("hex"===y.style){const t=S+lt/2,e=k+ct/2;a.beginPath();for(let i=0;i<6;i++){const s=Math.PI/3*i,r=t+Math.cos(s)*lt*.49,o=e+Math.sin(s)*ct*.49;0===i?a.moveTo(r,o):a.lineTo(r,o)}a.closePath(),a.fill()}else if("cmyk"===y.style){a.fillStyle="#ffffff",a.fillRect(S,k,lt,ct);const t=(t,e)=>Math.max(0,Math.min(255,"source"===y.colorMode?t+A:"gray"===y.colorMode?255*w:e*w)),e=t(p,r[0]),i=t(x,r[1]),s=t(v,r[2]),o=1-Math.max(e,i,s)/255,n=Math.max(.001,1-o),h=[["#00ffff",(1-e/255-o)/n,.36,.36],["#ff00ff",(1-i/255-o)/n,.64,.36],["#ffff00",(1-s/255-o)/n,.5,.66],["#000000",o,.5,.5]];a.globalCompositeOperation="multiply";for(const[t,e,i,s]of h)a.fillStyle=t,a.beginPath(),a.arc(S+lt*i,k+ct*s,.48*lt*Math.sqrt(Math.max(0,e)),0,2*Math.PI),a.fill();a.globalCompositeOperation="source-over"}else if("voxel"===y.style){const t=S+lt/2,e=k+ct/2;a.beginPath(),a.moveTo(t,k),a.lineTo(S+lt,k+.25*ct),a.lineTo(t,e),a.lineTo(S,k+.25*ct),a.closePath(),a.fill(),a.globalAlpha=.72*E,a.beginPath(),a.moveTo(S,k+.25*ct),a.lineTo(t,e),a.lineTo(t,k+ct),a.lineTo(S,k+.75*ct),a.closePath(),a.fill(),a.globalAlpha=.42*E,a.beginPath(),a.moveTo(t,e),a.lineTo(S+lt,k+.25*ct),a.lineTo(S+lt,k+.75*ct),a.lineTo(t,k+ct),a.closePath(),a.fill()}else{const t="pixel"===y.style?0:Math.max(.5,.06*lt);a.fillRect(S,k,lt-t,ct-t),"lego"===y.style&&a.drawImage(M.canvas,S,k),"disco"===y.style&&(a.fillStyle=`rgba(255,255,255,${.1+.25*I(h,n)})`,a.fillRect(S,k,lt-t,.35*ct),a.fillStyle="rgba(0,0,0,.25)",a.fillRect(S,k+.65*ct,lt-t,.35*ct-t))}}}n.ctx.globalAlpha=1,(y.color.amount||y.lights.length)&&(z(u.canvas,at,st),u.ctx.clearRect(0,0,at,st),u.ctx.drawImage(n.canvas,0,0)),y.color.amount&&(n.ctx.save(),n.ctx.globalCompositeOperation=y.color.blend,n.ctx.globalAlpha=y.color.amount,n.ctx.fillStyle=y.color.tint,n.ctx.fillRect(0,0,at,st),n.ctx.restore());for(const t of y.lights){const e=t.radius*Math.max(at,st),i=n.ctx.createRadialGradient(t.x*at,t.y*st,0,t.x*at,t.y*st,e);i.addColorStop(0,t.color),i.addColorStop(1,"transparent"),n.ctx.save(),n.ctx.globalCompositeOperation="screen",n.ctx.globalAlpha=t.intensity,n.ctx.fillStyle=i,n.ctx.fillRect(0,0,at,st),n.ctx.restore()}if((y.color.amount||y.lights.length)&&(n.ctx.globalCompositeOperation="destination-in",n.ctx.drawImage(u.canvas,0,0),n.ctx.globalCompositeOperation="source-over"),y.mask.enabled&&y.mask.shapes.length){const t=c.ctx;t.clearRect(0,0,at,st),t.fillStyle="#fff",t.strokeStyle="#fff",t.lineCap="round",t.lineJoin="round";for(const e of y.mask.shapes){if(t.beginPath(),"ellipse"===e.kind)t.ellipse((e.x+e.width/2)*at,(e.y+e.height/2)*st,e.width*at/2,e.height*st/2,0,0,2*Math.PI);else{if("rectangle"!==e.kind){if(t.lineWidth=(e.size??.05)*at,1===e.points?.length){t.arc(e.points[0][0]*at,e.points[0][1]*st,t.lineWidth/2,0,2*Math.PI),t.fill();continue}e.points?.forEach(([e,i],a)=>a?t.lineTo(e*at,i*st):t.moveTo(e*at,i*st)),t.stroke();continue}t.rect(e.x*at,e.y*st,e.width*at,e.height*st)}t.fill()}n.ctx.globalCompositeOperation=y.mask.invert?"destination-out":"destination-in",n.ctx.drawImage(c.canvas,0,0),n.ctx.globalCompositeOperation="source-over"}const l=h.ctx;l.clearRect(0,0,at,st),l.globalAlpha=y.backdrop.opacity;const m=y.backdrop;if("solid"===m.mode&&(l.fillStyle=m.color,l.fillRect(0,0,at,st)),"gradient"===m.mode){const t=l.createLinearGradient(0,0,at,st);t.addColorStop(0,m.color),t.addColorStop(1,m.color2),l.fillStyle=t,l.fillRect(0,0,at,st)}if("source"===m.mode&&l.drawImage(r.canvas,0,0),"blurred"===m.mode){const t=Math.max(1,m.blur);z(f.canvas,Math.max(2,Math.round(at/t)),Math.max(2,Math.round(st/t))),f.ctx.drawImage(r.canvas,0,0,f.canvas.width,f.canvas.height),l.imageSmoothingEnabled=!0,l.drawImage(f.canvas,0,0,at,st)}l.globalAlpha=1,y.effects.characterBloom>0&&(z(p.canvas,Math.max(2,Math.round(at/3)),Math.max(2,Math.round(st/3))),p.ctx.clearRect(0,0,p.canvas.width,p.canvas.height),p.ctx.drawImage(n.canvas,0,0,p.canvas.width,p.canvas.height),l.save(),l.globalAlpha=.8*y.effects.characterBloom,l.globalCompositeOperation="screen",l.filter=`blur(${1+4*y.effects.characterBloom}px)`,l.drawImage(p.canvas,0,0,at,st),l.restore()),l.drawImage(n.canvas,0,0),A=gt?pt:""}const Mt=Object.entries(y.effects).some(([t,e])=>!["angle","focus","blurType","characterBloom"].includes(t)&&"number"==typeof e&&e>0);s.clearRect(0,0,at,st),Mt?(void 0===x&&(x=function(){const t=document.createElement("canvas"),e=t.getContext("webgl",{alpha:!0,premultipliedAlpha:!1,antialias:!1,preserveDrawingBuffer:!0});if(!e)return null;const i=[],a=(t,a)=>{const s=e.createShader(t);if(i.push(s),e.shaderSource(s,a),e.compileShader(s),!e.getShaderParameter(s,e.COMPILE_STATUS))throw new Error(e.getShaderInfoLog(s)||"Finish shader failed");return s},s=e.createProgram();if(e.attachShader(s,a(e.VERTEX_SHADER,"attribute vec2 p;varying vec2 uv;void main(){uv=(p+1.)*.5;gl_Position=vec4(p,0.,1.);}")),e.attachShader(s,a(e.FRAGMENT_SHADER,"precision mediump float;varying vec2 uv;uniform sampler2D src;uniform vec2 size;uniform float t,bloom,grain,dust,scanlines,crt,prism,vignette,glitch,pixelate,blur,blurMode,angle,focus,halftone,rgbSplit,sharpen;\n float hash(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}\n vec4 sampleAt(vec2 p){if(min(min(p.x,p.y),min(1.-p.x,1.-p.y))<0.)return vec4(0.);return texture2D(src,vec2(p.x,1.-p.y));}\n void main(){vec2 q=uv,c=q-.5;q=c*(1.+dot(c,c)*crt*.25)+.5;\n if(glitch>0.)q.x+=(hash(vec2(floor(q.y*35.),floor(t*5.)))-.5)*.07*glitch*step(.82,hash(vec2(floor(q.y*35.),floor(t*5.)+3.)));\n if(pixelate>0.){vec2 grid=size/(1.+pixelate*20.);q=(floor(q*grid)+.5)/grid;}\n vec4 base=sampleAt(q);float edge=smoothstep(.12,.65,length(c));\n vec2 shift=vec2((prism*3.*edge+rgbSplit*8.)/size.x,prism*edge/size.y);\n if(prism>0.||rgbSplit>0.)base.rgb=vec3(sampleAt(q+shift).r,base.g,sampleAt(q-shift).b);\n vec2 dir=vec2(cos(angle),sin(angle))/size;float amount=blur;\n if(blurMode>1.5&&blurMode<2.5){dir=normalize(c+vec2(.0001))/size;amount*=length(c)*2.;}\n if(blurMode>2.5)amount*=smoothstep(.05,.5,abs(q.y-focus));\n vec4 blurred=base;\n if(amount>0.){blurred=base*.2;for(int i=1;i<=4;i++){float f=float(i)*amount*.35;vec2 off=dir*f;if(blurMode<.5)off=vec2(cos(float(i)*1.57),sin(float(i)*1.57))*amount/size;blurred+=sampleAt(q+off)*.1+sampleAt(q-off)*.1;}}\n vec3 light=vec3(0.);if(bloom>0.)for(int i=0;i<4;i++){float a=float(i)*1.5708;vec3 s=sampleAt(q+vec2(cos(a),sin(a))*6./size).rgb;light+=max(vec3(0.),s-.35)*.25;}\n vec3 col=blurred.rgb+light*bloom;\n if(sharpen>0.){vec2 px=1./size;vec3 near=sampleAt(q+vec2(px.x,0.)).rgb+sampleAt(q-vec2(px.x,0.)).rgb+sampleAt(q+vec2(0.,px.y)).rgb+sampleAt(q-vec2(0.,px.y)).rgb;col+=(base.rgb*4.-near)*sharpen*.5;}\ncol*=1.-scanlines*.4*(.5+.5*cos(q.y*size.y*3.14159));\n col*=1.-vignette*smoothstep(.15,.72,length(c));\n col+=(hash(floor(q*size)+floor(t*12.))-.5)*grain*.2;\n if(dust>0.)col+=step(1.-dust*.002,hash(floor(q*size*.25)+floor(t*2.)))*.4;\n if(halftone>0.)col*=mix(1.,smoothstep(.1,.65,length(fract(q*size/6.)-.5)),halftone);\n gl_FragColor=vec4(clamp(col,0.,1.),blurred.a);}")),e.linkProgram(s),!e.getProgramParameter(s,e.LINK_STATUS))throw new Error("Finish link failed");e.useProgram(s);const r=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,r),e.bufferData(e.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,1,1]),e.STATIC_DRAW);const o=e.getAttribLocation(s,"p");e.enableVertexAttribArray(o),e.vertexAttribPointer(o,2,e.FLOAT,!1,0,0);const n=e.createTexture();e.bindTexture(e.TEXTURE_2D,n),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE);const h=["t","bloom","grain","dust","scanlines","crt","prism","rgbSplit","sharpen","vignette","glitch","pixelate","blur","blurMode","angle","focus","halftone"],l=Object.fromEntries(h.map(t=>[t,e.getUniformLocation(s,t)])),c=e.getUniformLocation(s,"size");return{canvas:t,render(i,a,r,o=1){t.width===i.width&&t.height===i.height||(t.width=i.width,t.height=i.height,e.viewport(0,0,t.width,t.height)),e.useProgram(s),e.bindTexture(e.TEXTURE_2D,n),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,e.RGBA,e.UNSIGNED_BYTE,i),e.uniform2f(c,t.width/o,t.height/o);for(const t of h)e.uniform1f(l[t],"t"===t?r:"blurMode"===t?["gaussian","directional","radial","progressive"].indexOf(a.blurType):"angle"===t?a.angle*Math.PI/180:a[t]??0);return e.drawArrays(e.TRIANGLE_STRIP,0,4),t},destroy(){e.deleteTexture(n),e.deleteBuffer(r),e.deleteProgram(s),i.forEach(t=>e.deleteShader(t)),t.width=t.height=1,e.getExtension("WEBGL_lose_context")?.loseContext()}}}()),x?s.drawImage(x.render(h.canvas,y.effects,i,Z),0,0):s.drawImage(h.canvas,0,0),t.dataset.studioFinish=x?"gpu":"unavailable"):(s.drawImage(h.canvas,0,0),t.dataset.studioFinish="none")},setPixelRatio(t){Z=Math.max(.1,Math.min(8,Number.isFinite(t)?t:1))},get pixelRatio(){return Z},setBudget(t){K=Math.max(256,Math.min(J,Number.isFinite(t)?Math.round(t):J))},get maxCells(){return K},configure(t){y=l(t),S++},pointer:(t,e,i)=>{j.configure(y.hover.effect,y.hover.strength,y.hover.radius,y.hover.edgeSafe),j.move(t,e,i)},leave(){j.leave()},get active(){return j.active},get settings(){return l(y)},get capabilities(){return{gpuFinish:null!==x}},invalidate(){k=""},destroy(){x?.destroy();for(const t of[r,o,n,h,c,f,d,u,p,M])t.canvas.width=t.canvas.height=1;q=null,L=new Uint8ClampedArray(0)}}}function q(t,e,i){return new Promise((a,s)=>{const r=()=>{l(),a()},o=()=>{l(),s(new Error("Unable to load this media. Check its format and cross-origin permissions."))},n=()=>{l(),s(new DOMException("Cancelled","AbortError"))},h=setTimeout(o,2e4),l=()=>{clearTimeout(h),t.removeEventListener(e,r),t.removeEventListener("error",o),i?.removeEventListener("abort",n)};t.addEventListener(e,r,{once:!0}),t.addEventListener("error",o,{once:!0}),i?.addEventListener("abort",n,{once:!0}),i?.aborted&&n()})}async function G(t,e){if("string"==typeof t&&t.startsWith("blob:")){const i=await fetch(t,{signal:e}),a=await i.blob();return G(new File([a],"Imported media",{type:a.type}),e)}const i="string"!=typeof t,a=i?t.name:t;if(i?"image/gif"===t.type:/\.gif(?:[?#]|$)/i.test(a)){const a=await import("gifuct-js"),{parseGIF:s,decompressFrames:r}="function"==typeof a.parseGIF?a:a.default,o=i?await t.arrayBuffer():await fetch(t,{signal:e}).then(t=>{if(!t.ok)throw new Error("GIF download failed.");return t.arrayBuffer()});if(e?.aborted)throw new DOMException("Cancelled","AbortError");const n=s(o),h=n.lsd.width,l=n.lsd.height;if(h*l>4e6)throw new Error("GIF is too large. Use a smaller GIF or an MP4.");const c=n.frames.filter(t=>"image"in t).length;if(!c||c*h*l*4>128e6)throw new Error("GIF exceeds the decoded memory budget. Use MP4 for long animations.");const f=r(n,!0);if(f.length*h*l*4>128e6)throw new Error("GIF exceeds the 128 MB decoded budget. Use MP4 for long animations.");const m=document.createElement("canvas");m.width=h,m.height=l;const d=m.getContext("2d"),u=document.createElement("canvas"),g=u.getContext("2d"),p=f.reduce((t,e)=>t+Math.max(20,e.delay),0)/1e3;let M=-1,x=null;const v=t=>{const e=(t%p+p)%p*1e3;let i=0,a=0;for(;a<f.length-1&&(i+=Math.max(20,f[a].delay),!(e<i));a++);a<M&&(d.clearRect(0,0,h,l),M=-1,x=null);for(let t=M+1;t<=a;t++){const e=f[t-1];2===e?.disposalType&&d.clearRect(e.dims.left,e.dims.top,e.dims.width,e.dims.height),3===e?.disposalType&&x&&d.putImageData(x,0,0);const i=f[t];x=3===i.disposalType?d.getImageData(0,0,h,l):null,u.width=i.dims.width,u.height=i.dims.height,g.putImageData(new ImageData(new Uint8ClampedArray(i.patch),u.width,u.height),0,0),d.drawImage(u,i.dims.left,i.dims.top)}return M=a,m};return v(0),{source:m,width:h,height:l,duration:p,animated:!0,seek:async()=>{},frame:v,destroy(){m.width=u.width=1,f.length=0,x=null}}}const s=i?t.type.startsWith("video/"):/\.(mp4|webm|mov)(?:[?#]|$)/i.test(a),r=i?URL.createObjectURL(t):t,o=s?document.createElement("video"):new Image;o.crossOrigin="anonymous",o instanceof HTMLVideoElement&&(o.muted=!0,o.playsInline=!0,o.preload="auto");const n=()=>{o instanceof HTMLVideoElement?(o.pause(),o.removeAttribute("src"),o.load()):o.src="",i&&URL.revokeObjectURL(r)};try{const t=q(o,s?"loadeddata":"load",e);o.src=r,await t;const i=o instanceof HTMLVideoElement?o.videoWidth:o.naturalWidth,a=o instanceof HTMLVideoElement?o.videoHeight:o.naturalHeight;return{source:o,width:i,height:a,duration:o instanceof HTMLVideoElement?o.duration:0,animated:s,async seek(t,e){if(!(o instanceof HTMLVideoElement))return;if(!Number.isFinite(o.duration)||o.duration<=0)throw new Error("Video duration is unavailable.");const i=Math.max(0,Math.min(o.duration-.001,t%o.duration));if(Math.abs(o.currentTime-i)<.001&&o.readyState>=2)return;const a=q(o,"seeked",e);o.currentTime=i,await a},frame:()=>o,destroy:n}}catch(t){throw n(),t}}var j=t=>{if(t?.aborted)throw new DOMException("Export cancelled","AbortError")};async function V(t,e,i){const{format:a,signal:s}=i,r=Math.round(i.width),o=Math.round(i.height);if(!Number.isFinite(r)||!Number.isFinite(o)||r<2||o<2||r>4096||o>4096||r*o>16777216)throw new Error("Choose export dimensions between 2 and 4096 pixels.");if(!["png","jpeg","mp4","webm","gif"].includes(a))throw new Error("Unknown export format.");if(void 0!==i.fps&&!Number.isFinite(i.fps))throw new Error("Frames per second must be finite.");const n=Math.max(1,Math.min(60,Math.round(i.fps??24))),h=i.duration??5;if(!Number.isFinite(h)||h<=0||h>60)throw new Error("Choose a duration from 0 to 60 seconds.");const c="png"===a||"jpeg"===a,f=i.time??0;if(!Number.isFinite(f)||f<0)throw new Error("Image time must be a finite non-negative number.");if(void 0!==i.motionTime&&(!Number.isFinite(i.motionTime)||i.motionTime<0))throw new Error("Motion time must be a finite non-negative number.");if(!c&&r*o>8294400)throw new Error("Video exports support up to 3840 × 2160 pixels.");if("gif"===a&&(r*o>921600||h*n>600))throw new Error("GIF export supports up to 1280 × 720 and 600 frames. Use MP4 for larger exports.");if(void 0!==i.referenceWidth&&(!Number.isFinite(i.referenceWidth)||i.referenceWidth<1))throw new Error("Reference width must be a positive number.");if("gif"===a&&n>30)throw new Error("GIF exports support up to 30 fps. Choose MP4 or WebM for higher frame rates.");const m=document.createElement("canvas"),d=W(m,l(e),{maxDimension:4096,maxCells:i.maxCells??12e3,pixelRatio:r/(i.referenceWidth??Math.min(r,960))});let u;try{if(j(s),i.onProgress?.(0),c){if(d.render(await t.frame(f,s),f,r,o,i.motionTime),"unavailable"===m.dataset.studioFinish)throw new Error("Optical effects need WebGL. Disable these effects or export in a supported browser.");j(s);const e=await new Promise((t,e)=>m.toBlob(i=>i?t(i):e(new Error("Image export failed.")),"png"===a?"image/png":"image/jpeg",i.quality??.92));return i.onProgress?.(1),e}const e=Math.ceil(h*n);if("gif"===a){const a=await import("gifenc"),{GIFEncoder:l,quantize:c,applyPalette:f}="function"==typeof a.GIFEncoder?a:a.default,u=l(),g=m.getContext("2d");for(let a=0;a<e;a++){j(s);const l=a/n;if(d.invalidate(),d.render(await t.frame(l,s),l,r,o),"unavailable"===m.dataset.studioFinish)throw new Error("Optical effects need WebGL. Disable these effects or export in a supported browser.");const p=g.getImageData(0,0,r,o).data,M=c(p,256),x=f(p,M);u.writeFrame(x,r,o,{palette:M,delay:10*(Math.round(100*Math.min((a+1)/n,h))-Math.round(100*a/n)),repeat:0}),i.onProgress?.((a+1)/e*.98),await new Promise(t=>setTimeout(t,0))}return u.finish(),i.onProgress?.(1),new Blob([new Uint8Array(u.bytes())],{type:"image/gif"})}const{Output:l,Mp4OutputFormat:g,WebMOutputFormat:p,BufferTarget:M,CanvasSource:x,canEncodeVideo:v}=await import("mediabunny"),b="mp4"===a?"avc":"vp9";if(!await v(b,{width:r,height:o}))throw new Error(`${a.toUpperCase()} encoding is unavailable in this browser. Try another format or a current browser with WebCodecs.`);const w=new l({format:"mp4"===a?new g({fastStart:"in-memory"}):new p,target:new M});m.width=r,m.height=o,u=()=>w.cancel();const y=new x(m,{codec:b,bitrate:Math.max(5e5,Math.round(r*o*n*.15))});w.addVideoTrack(y,{frameRate:n}),m.width=r,m.height=o,await w.start();for(let a=0;a<e;a++){j(s);const l=a/n;if(d.invalidate(),d.render(await t.frame(l,s),l,r,o),"unavailable"===m.dataset.studioFinish)throw new Error("Optical effects need WebGL. Disable these effects or export in a supported browser.");await y.add(l,Math.min(1/n,h-l)),i.onProgress?.((a+1)/e*.98),a%4==0&&await new Promise(t=>setTimeout(t,0))}return y.close(),j(s),await w.finalize(),u=void 0,i.onProgress?.(1),new Blob([w.target.buffer],{type:"mp4"===a?"video/mp4":"video/webm"})}catch(t){throw await(u?.().catch(()=>{})),t}finally{d.destroy(),m.width=m.height=1}}async function H(t,e,i={}){const a=await G(e,i.signal);try{return $(t,a,i)}catch(t){throw a.destroy(),t}}function $(t,e,i={}){let a=l(i.settings),s=i.width??e.width,r=i.height??e.height;const o=W(t,a,i),n=new A;let h=0,c=!1,f=!0,m=!1,g=!0,p=0,M=0,x=0,v=0,y=-1,S=!1!==i.adaptive,k=0,E=0,F=0;const T=matchMedia("(prefers-reduced-motion: reduce)"),I=e.source instanceof HTMLVideoElement?e.source:null,P=()=>{c||h||!g||document.hidden||(h=requestAnimationFrame(L))},R=()=>!m&&!T.matches&&g&&!document.hidden,C=()=>{I&&(I.loop=!0,R()?I.play().catch(()=>{m=!0,f=!0,P()}):I.pause()),x=0,f=!0,P()};function L(t){if(h=0,c||!g||document.hidden)return;const l=R();l&&(p+=x?Math.min(.1,(t-x)/1e3):0),x=t;const u=function(t){return w(t)||b(t)||t.effects.grain>0||t.effects.dust>0||t.effects.glitch>0}(a),A=l&&(e.animated||u),T=I?.getVideoPlaybackQuality?.().totalVideoFrames??-1,C=!I||T<0||T!==y;if(f||o.active||A&&(C||u)&&t-v>=1e3/Math.max(1,Math.min(60,i.fps??60))-.5){const h=performance.now();try{e.animated&&!I&&o.invalidate();const[t,l]=d(s,r,a);M=n.update(p,a.motion.type,a.motion.speed),o.render(e.frame(I?.currentTime??p),p,t,l,M);const c=performance.now()-h;i.onFrame?.(I?.currentTime??p,c),E=E?.9*E+.1*c:c,++k>20&&S&&(A||o.active)&&(F=E>13?F+1:0,F>=12&&o.maxCells>3e3&&(o.setBudget(Math.max(3e3,.8*o.maxCells)),i.onQualityChange?.(o.maxCells),F=0,E=0))}catch(t){m=!0,i.onError?.(t instanceof Error?t:new Error(String(t)))}f=!1,v=t,y=T}l&&(A||o.active)&&P()}const D=e=>{if(!R())return;const i=t.getBoundingClientRect();o.pointer((e.clientX-i.left)/i.width,(e.clientY-i.top)/i.height,e.timeStamp),P()},N=()=>{o.leave(),P()},U=new IntersectionObserver(([t])=>{g=t.isIntersecting,C()});U.observe(t),t.addEventListener("pointermove",D),t.addEventListener("pointerleave",N),t.addEventListener("pointercancel",N),document.addEventListener("visibilitychange",C),T.addEventListener("change",C);const X=()=>{c||(c=!0,cancelAnimationFrame(h),U.disconnect(),t.removeEventListener("pointermove",D),t.removeEventListener("pointerleave",N),t.removeEventListener("pointercancel",N),document.removeEventListener("visibilitychange",C),T.removeEventListener("change",C),i.signal?.removeEventListener("abort",X),o.destroy(),e.destroy())};if(i.signal?.addEventListener("abort",X,{once:!0}),i.signal?.aborted)throw X(),new DOMException("Cancelled","AbortError");return C(),{canvas:t,media:e,update(t){a=u(a,t),o.configure(a),f=!0,P()},resize(t,e,i){void 0!==i&&o.setPixelRatio(i),s=Math.max(2,t),r=Math.max(2,e),f=!0,P()},pause(t=!0){m=t,C()},async seek(t){p=Math.max(0,t),await e.seek(p),o.invalidate(),f=!0,P()},redraw(){o.invalidate(),f=!0,P()},get time(){return I?.currentTime??p},get motionTime(){return M},get paused(){return m||T.matches},setAdaptive(t){S=t,k=F=E=0},setBudget(t){o.setBudget(t),i.onQualityChange?.(o.maxCells),f=!0,P()},get pixelRatio(){return o.pixelRatio},get maxCells(){return o.maxCells},get capabilities(){return o.capabilities},destroy:X}}var Q={Standard:()=>import("figlet/fonts/Standard"),Slant:()=>import("figlet/fonts/Slant"),Small:()=>import("figlet/fonts/Small"),Big:()=>import("figlet/fonts/Big"),Banner:()=>import("figlet/fonts/Banner"),Block:()=>import("figlet/fonts/Block"),Bubble:()=>import("figlet/fonts/Bubble"),Digital:()=>import("figlet/fonts/Digital"),Doom:()=>import("figlet/fonts/Doom"),Lean:()=>import("figlet/fonts/Lean"),Mini:()=>import("figlet/fonts/Mini"),Script:()=>import("figlet/fonts/Script"),Shadow:()=>import("figlet/fonts/Shadow"),Speed:()=>import("figlet/fonts/Speed"),Starwars:()=>import("figlet/fonts/Star Wars"),Stop:()=>import("figlet/fonts/Stop"),Straight:()=>import("figlet/fonts/Straight"),Thin:()=>import("figlet/fonts/Thin"),ThreeD:()=>import("figlet/fonts/3-D"),Univers:()=>import("figlet/fonts/Univers"),Wavy:()=>import("figlet/fonts/Wavy")},J=Object.keys(Q);async function K(t,e="Standard",i="#ffffff"){if(!Object.prototype.hasOwnProperty.call(Q,e))throw new Error("Unknown text font.");const{default:a}=await import("figlet/browser"),s=await Q[e]();a.parseFont(e,s.default),a.defaults({fetchFontIfMissing:!1});const r=a.textSync(t.slice(0,120),{font:e}),o=r.split("\n"),n=Math.max(1,...o.map(t=>t.length)),h=Math.max(1,o.length),l=Math.min(1,2048/(9*n)),c=document.createElement("canvas");c.width=Math.ceil(9*n*l+32),c.height=Math.ceil(15*h*l+32);const f=c.getContext("2d");return f.fillStyle="#080808",f.fillRect(0,0,c.width,c.height),f.fillStyle=i,f.font=15*l+"px monospace",f.textBaseline="top",o.forEach((t,e)=>f.fillText(t,16,16+15*e*l)),{canvas:c,ascii:r}}var Z={standard:{label:"Classic ASCII",chars:" .:-=+*#%@"},asciify:{label:"Asciify",chars:" .:-=+ASCIIFY#@"},minimal:{label:"Minimal",chars:" .:+"},detailed:{label:"Detailed",chars:" .,:;i1tfLCG08@"},letters:{label:"Letters",chars:" .ilLCFTYASMW"},technical:{label:"Technical",chars:" .:;>_/[{}]=+*#@$"},blocks:{label:"Tonal blocks",chars:" ░▒▓█"},braille:{label:"Braille",chars:" ⠁⠃⠇⡇⣇⣧⣷⣿"}},tt=Z;export{i as BLEND_MODES,Z as CHARACTER_SETS,s as DEFAULT_STUDIO_SETTINGS,e as DITHER_ALGORITHMS,tt as STUDIO_CHARACTER_SETS,p as STUDIO_LOOKS,y as STUDIO_MOTIONS,a as STUDIO_PALETTES,t as STUDIO_STYLES,J as STUDIO_TEXT_FONTS,W as createStudioRenderer,K as createStudioText,V as exportStudio,G as loadStudioMedia,H as mountStudio,$ as mountStudioMedia,l as normalizeStudioSettings,f as parseStudioSettings,x as restyleStudio,c as serializeStudioSettings,m as studioCrop,d as studioDimensions,g as studioGrid,M as studioLook,O as studioSourceSize,u as updateStudioSettings};//# sourceMappingURL=studio.js.map
@@ -1,6 +1,6 @@
1
1
  type ColorMode = 'grayscale' | 'fullcolor' | 'matrix' | 'accent';
2
2
  type RenderMode = 'ascii' | 'dots';
3
- type AnimationStyle = 'none' | 'caustics' | 'current' | 'reform';
3
+ type AnimationStyle = 'none' | 'caustics' | 'current' | 'reform' | 'sheen' | 'tidal' | 'grain';
4
4
  /** Core media draws ASCII/dots; use StudioStyle for the 15 render styles. */
5
5
  type ArtStyle = 'classic';
6
6
  type HoverEffect = 'spotlight' | 'magnify' | 'repel' | 'glow' | 'colorShift' | 'attract' | 'shatter' | 'trail' | 'glitchText';
@@ -1,6 +1,6 @@
1
1
  type ColorMode = 'grayscale' | 'fullcolor' | 'matrix' | 'accent';
2
2
  type RenderMode = 'ascii' | 'dots';
3
- type AnimationStyle = 'none' | 'caustics' | 'current' | 'reform';
3
+ type AnimationStyle = 'none' | 'caustics' | 'current' | 'reform' | 'sheen' | 'tidal' | 'grain';
4
4
  /** Core media draws ASCII/dots; use StudioStyle for the 15 render styles. */
5
5
  type ArtStyle = 'classic';
6
6
  type HoverEffect = 'spotlight' | 'magnify' | 'repel' | 'glow' | 'colorShift' | 'attract' | 'shatter' | 'trail' | 'glitchText';
@@ -7,21 +7,51 @@ import {
7
7
  serializeStudioSettings,
8
8
  parseStudioSettings,
9
9
  type StudioInput,
10
- } from "asciify-engine/studio";
10
+ type StudioExportFormat,
11
+ } from 'asciify-engine/studio';
11
12
 
12
- /** Supply your own local File or permitted CORS URL. */
13
+ /** One live composition. Source pixels stay in the browser. */
13
14
  export async function createComposition(
14
15
  canvas: HTMLCanvasElement,
15
16
  source: File | string,
16
17
  signal?: AbortSignal,
17
18
  ) {
18
19
  let settings = normalizeStudioSettings({
19
- aspectRatio: "16:9",
20
- style: "ascii",
21
- ink: "#e8b900",
22
- backdrop: { mode: "blurred", opacity: 0.4 },
20
+ style: 'ascii',
21
+ cellSize: 6,
22
+ ink: '#b3ed82',
23
+ backdrop: { mode: 'solid', color: '#0c1711' },
24
+ hover: { effect: 'trail', strength: .55, radius: .45 },
23
25
  });
24
- const player = await mountStudio(canvas, source, { settings, signal });
26
+ const player = await mountStudio(canvas, source, {settings, maxDimension: 960, signal});
27
+ async function exportComposition(format: StudioExportFormat, cancellation?: AbortSignal) {
28
+ // Snapshot before loading another media instance. Export never seeks the
29
+ // live preview or picks up slider edits made while encoding.
30
+ const frozen = normalizeStudioSettings(settings);
31
+ const time = player.time;
32
+ const motionTime = player.motionTime;
33
+ const referenceWidth = canvas.width / player.pixelRatio;
34
+ const maxCells = player.maxCells;
35
+ const scale = 1280 / Math.max(canvas.width, canvas.height);
36
+ const width = Math.max(2, Math.round(canvas.width * scale / 2) * 2);
37
+ const height = Math.max(2, Math.round(canvas.height * scale / 2) * 2);
38
+ const media = await loadStudioMedia(source, cancellation);
39
+ try {
40
+ const still = format === 'png' || format === 'jpeg';
41
+ const duration = !media.animated && frozen.motion.type !== 'none' && (frozen.motion.amount ?? 1) > 0
42
+ ? 12 / frozen.motion.speed : Math.max(1, Math.min(60, media.duration || 5));
43
+ // A low-speed ambient loop may exceed the export limit. Explain that
44
+ // instead of silently cutting its cycle and calling the result seamless.
45
+ if (!still && duration > 60) throw new Error('Raise motion speed to at least 0.2 for a complete loop within 60 seconds.');
46
+ return await exportStudio({frame: async (t, signal) => {
47
+ await media.seek(t, signal);
48
+ return media.frame(t);
49
+ }}, frozen, {format, width, height, referenceWidth, maxCells,
50
+ time, motionTime, duration: still ? 5 : duration, fps: 24, signal: cancellation});
51
+ } finally {
52
+ media.destroy();
53
+ }
54
+ }
25
55
  return {
26
56
  player,
27
57
  update(patch: StudioInput) {
@@ -33,33 +63,8 @@ export async function createComposition(
33
63
  settings = parseStudioSettings(json);
34
64
  player.update(settings);
35
65
  },
36
- async exportMp4(signal?: AbortSignal) {
37
- const frozen = normalizeStudioSettings(settings);
38
- const media = await loadStudioMedia(source, signal);
39
- try {
40
- return await exportStudio(
41
- {
42
- frame: async (time, signal) => {
43
- await media.seek(time, signal);
44
- return media.frame(time);
45
- },
46
- },
47
- frozen,
48
- {
49
- format: "mp4",
50
- width: 1280,
51
- height: 720,
52
- referenceWidth: canvas.width,
53
- maxCells: player.maxCells,
54
- duration: 8,
55
- fps: 24,
56
- signal,
57
- },
58
- );
59
- } finally {
60
- media.destroy();
61
- }
62
- },
66
+ exportPng: (signal?: AbortSignal) => exportComposition('png', signal),
67
+ exportMp4: (signal?: AbortSignal) => exportComposition('mp4', signal),
63
68
  destroy: () => player.destroy(),
64
69
  };
65
70
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "asciify-engine",
3
- "version": "4.2.0",
3
+ "version": "4.3.1",
4
4
  "description": "Framework-agnostic ASCII art engine. Convert images, videos, and GIFs into ASCII art rendered on canvas.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -70,7 +70,9 @@
70
70
  "test:browser": "node tests/serve-browser.mjs",
71
71
  "test:hover": "node tests/hover-browser.mjs",
72
72
  "test:finish": "node tests/hero-finish-browser.mjs",
73
- "test:studio": "node tests/studio-browser.mjs"
73
+ "test:studio": "node tests/studio-browser.mjs",
74
+ "bench:dither": "node scripts/benchmark-dither.mjs",
75
+ "bench:hover": "node scripts/benchmark-hover-fields.mjs"
74
76
  },
75
77
  "keywords": [
76
78
  "ascii",
@@ -2,13 +2,13 @@
2
2
  name: asciify-engine
3
3
  description: Build and tune browser ASCII images, video, GIFs, text, and interactive heroes with asciify-engine. Use for media conversion, character and color choices, hover integration, layered HTML typography, scroll-synced video, and rendering performance in apps using this package.
4
4
  metadata:
5
- tested-engine: "4.2.0"
5
+ tested-engine: "4.3.1"
6
6
  updated: "2026-10-07"
7
7
  ---
8
8
 
9
9
  # Asciify Engine
10
10
 
11
- Use the published `asciify-engine` npm package for browser canvas rendering. Tested against **4.2.0**. Check the application's installed version and its public types before using newer options; a local checkout can contain unpublished changes.
11
+ Use the published `asciify-engine` npm package for browser canvas rendering. Tested against **4.3.1**. Check the application's installed version and its public types before using newer options; a local checkout can contain unpublished changes.
12
12
 
13
13
  - Agent entry: https://asciify.org/skill (redirects to this Markdown document).
14
14
  - Playground: https://asciify.org/editor
@@ -31,6 +31,9 @@ This uses the Vercel Skills CLI to install the repository skill and its linked r
31
31
 
32
32
  ## Choose the integration
33
33
 
34
+ For a live project component, start with [project-integration.md](references/project-integration.md): it maps each use case to one rendering pipeline, covers instance ownership, and keeps PNG/video export independent of live playback. Use `/studio` for a new composition with art styles, masks, backdrops and saved settings. It already owns hover/motion; do not add another `/hover` instance on top.
35
+
36
+
34
37
  - Static image: `asciify(source, canvas, config)` returns `Promise<() => void>`. Call the cleanup on unmount, including for a still image.
35
38
  - Video: `asciifyVideo(source, canvas, config)` returns `Promise<() => void>`. Call that function on unmount. Use `fitTo` and `objectFit: 'cover'` for full-bleed heroes; `contain` for a whole-subject preview.
36
39
  - GIF: `asciifyGif(source, canvas, config)` returns a cleanup function asynchronously.
@@ -89,7 +92,7 @@ Preview it: letter glyph densities differ, so arbitrary words are not automatica
89
92
 
90
93
  The public npm `HoverEffect` IDs in 1.2.0 are `spotlight`, `magnify`, `repel`, `glow`, `colorShift`, `attract`, `shatter`, `trail`, and `glitchText`. Those remain valid APIs, but they are **not identical to the current website's enhanced interactions**.
91
94
 
92
- The website offers **Trail, Water, Contour, Dissolve, Silk, Vortex, and Off** through an optional studio surface module. Its Caustics, Slow Current, Reveal & Reform, and fine-dither treatments ship in the same `asciify-engine/hover` module. Use its typed options rather than converting display names into engine `hoverEffect` or `animationStyle` values.
95
+ The website offers **Trail, Water, Contour, Dissolve, Silk, Vortex, Magnetic Pull, Scatter, and Off** through an optional studio surface module. Its six still-image motions—Caustics, Slow Current, Reveal & Reform, Sheen, Tidal Rings, Living Grain—and fine-dither treatments ship in the same `asciify-engine/hover` module. Use its typed options rather than converting display names into engine `hoverEffect` or `animationStyle` values.
93
96
 
94
97
  To reproduce those interactions, read [references/studio-effects.md](references/studio-effects.md). Import `mountHover` from `asciify-engine/hover` (1.2.0+); no separate download is needed. Keep engine `hoverStrength: 0` and `animationStyle: 'none'` when using it, to avoid applying two effects. Trail is the site's default. Prefer a stationary-grid effect such as Trail or Dissolve when the user wants a wake without spatial displacement.
95
98
 
@@ -136,7 +139,7 @@ Version 1.3.0 adds text-shaped charcoal cutouts (`textMask`), peripheral scanlin
136
139
 
137
140
  For optional wallpaper/backdrops, alternate renderers, palettes, masks, saved looks, text fonts, or deterministic MP4/GIF export, read [references/studio-workspace.md](references/studio-workspace.md). The new `/studio` API is separate from `/hover` and `AsciiOptions`; do not replace an existing hero pipeline just to add a backdrop. Use version 1.4.0 or newer for these APIs after verifying the installed package.
138
141
 
139
- ## Current catalogs (4.2)
142
+ ## Current catalogs
140
143
 
141
144
  Legacy `CHARSETS`, `ART_STYLE_PRESETS`, `CHARSET_SEQUENCES`, `LIVING_STYLE_PRESETS` and their key types are removed. Do not import them or generate old names such as Katakana, Binary, Waves or Smoke. Use the 18 `STUDIO_STYLES` for render-style selectors and `/studio` rendering. For ASCII-only character ramps use `CHARACTER_SETS` from `/core` or `STUDIO_CHARACTER_SETS` from `/studio`; custom Unicode and user-provided frame arrays remain supported. Core `artStyle` only accepts `classic`; use explicit options instead of preset shortcuts. Despite the requested 4.1.0 version number, migrating these removed APIs is a breaking change.
142
145
 
@@ -0,0 +1,80 @@
1
+ # Live project integration, then export
2
+
3
+ Use this decision guide for an application that should keep its ASCII interactive.
4
+ A downloaded PNG is one output; it is not a substitute for a live component.
5
+
6
+ | Desired result | API | Motion/hover owner |
7
+ | --- | --- | --- |
8
+ | Image/video/GIF hero with HTML titles and text cutouts | `/core` media API + optional `/hover` | Disable core hover/motion when `/hover` owns them. |
9
+ | Art-style editor, live still image, saved look, backdrop, masks, PNG/video export | `/studio` → `mountStudio` | Studio owns motion, hover and finishing. Do not stack `mountHover` on it. |
10
+ | Application-owned render loop | `/studio` → `createStudioRenderer` | Caller passes time, pointer input, invalidation and cleanup. |
11
+ | ASCII text data | Core frame/text conversion | No animation loop needed unless requested. |
12
+
13
+ ## Small live component
14
+
15
+ ```ts
16
+ import { mountStudio, normalizeStudioSettings, updateStudioSettings } from 'asciify-engine/studio';
17
+
18
+ const cancellation = new AbortController();
19
+ let settings = normalizeStudioSettings({
20
+ style: 'ascii', cellSize: 6,
21
+ ink: '#b3ed82', backdrop: { mode: 'solid', color: '#0c1711' },
22
+ hover: { effect: 'trail', radius: .45, strength: .55 },
23
+ });
24
+ const player = await mountStudio(canvas, '/portrait.jpg', {
25
+ settings, width: 960, height: 540, maxDimension: 960,
26
+ maxCells: 12000, signal: cancellation.signal,
27
+ });
28
+
29
+ // Add life to a still without changing the source or remounting.
30
+ settings = updateStudioSettings(settings, {motion: {type: 'sheen', speed: .7, amount: .6}});
31
+ player.update(settings);
32
+ // User controls:
33
+ player.pause(true);
34
+ player.pause(false);
35
+ // On unmount, including while setup is pending:
36
+ cancellation.abort();
37
+ player.destroy();
38
+ ```
39
+
40
+ Keep the instance outside render-time state updates. React effects should abort
41
+ pending setup and destroy a late-resolving instance after unmount. Use a
42
+ ResizeObserver to call `resize`; avoid remounting on size or slider changes.
43
+ `mountStudio` owns the loaded media and its own reduced-motion/visibility handling.
44
+ Keep readable HTML content and an error state beside a decorative canvas.
45
+
46
+ ## Export without disturbing live playback
47
+
48
+ Read [studio-workspace.md](studio-workspace.md#export-the-same-composition).
49
+ Snapshot settings, preview time, `player.motionTime`, `maxCells`, logical reference width
50
+ (`canvas.width / player.pixelRatio`), and aspect ratio before an asynchronous export.
51
+ Load a second `StudioMedia`, seek it for each exported frame, and always destroy it
52
+ in `finally`. PNG/JPEG accept `time` and `motionTime`; pass both from the mounted preview so speed edits do not change the exported phase. Animation exports start at time zero.
53
+ `exportStudio` returns a Blob; download it with an object URL and revoke that URL.
54
+
55
+ For a still with ambient motion, one complete cycle is `12 / motion.speed`
56
+ seconds. The 60-second export limit means a whole cycle needs speed ≥ 0.2.
57
+ Dither shimmer/drift and noise/glitch finishing have independent clocks; stacking
58
+ them with an ambient cycle does not guarantee that the combined result loops.
59
+ Source video must itself loop to produce a seamless video export.
60
+ Hover depends on live pointer input and is not baked into offline export.
61
+
62
+ A typed reusable helper, including live updates, settings import/export and PNG/
63
+ MP4 output, ships at `asciify-engine/examples/studio-composition.ts` in the package
64
+ files. Copy that source from the installed package; it is an example, not a public
65
+ JavaScript export. Choose source URLs and style settings for the host project.
66
+
67
+ ## Evidence to gather
68
+
69
+ - Verify a recognizable source, small/large cells, source/accent/gray colors,
70
+ a hover, a still motion and the requested finishing combination.
71
+ - Check pause, hidden/offscreen suspension, remount cleanup, and reduced motion.
72
+ - Use `studioGrid` for actual density. Settings may request a finer grid than the
73
+ current budget permits; show that explicitly.
74
+ - `onFrame(time, costMs)` measures synchronous rendering cost. It does not measure
75
+ GPU completion or displayed FPS. Measure frame pacing in a browser on the target
76
+ device before publishing a performance claim.
77
+ - Export a selected still and an entire animation. Compare framing, density,
78
+ colors, transparency and the loop boundary with the preview.
79
+ - If browser inspection is unavailable, report code/unit/build evidence separately
80
+ and leave visual, GPU and device-performance claims unverified.
@@ -33,18 +33,20 @@ The positioned host must fit the displayed source canvas. The surface appends a
33
33
  | Module `effect` | Behavior |
34
34
  | --- | --- |
35
35
  | `trail` | Density wake with a fading tail; glyph grid remains fixed. |
36
- | `water` | Local refraction with aligned outer boundaries. |
36
+ | `water` | Local refraction; opt into protected boundaries with `edgeSafe: true`. |
37
37
  | `contour` | Expanding ring echoes along the pointer path. |
38
38
  | `dissolve` | Fading/reforming density trace with a lingering tail. |
39
39
  | `silk` | Directional folds following the stroke. |
40
40
  | `vortex` | Local rotational wake. |
41
+ | `magnetic` | Attracts the field toward a stroke, then relaxes. |
42
+ | `scatter` | Spatially varied radial scatter with a decaying wake. |
41
43
  | `none` | No hover effect. |
42
44
 
43
45
  These are `/hover` module `effect` IDs, not the root engine’s legacy `HoverEffect` values. For example, passing `hoverEffect: 'water'` to the published engine is invalid.
44
46
 
45
47
  ## Still-image motion and dither
46
48
 
47
- The module's `motion` IDs are `none`, `caustics` (traveling light on a fixed grid), `current` (Slow Current), and `reform` (Reveal & Reform). Keep source-video motion alone unless the user intentionally wants another animation layered onto it. For a still, select one motion and a restrained `motionSpeed`, rather than stacking effects.
49
+ The module's `motion` IDs are `none`, `caustics` (traveling light on a fixed grid), `current` (Slow Current), `reform` (Reveal & Reform), `sheen` (diagonal light sweep), `tidal` (Tidal Rings), and `grain` (Living Grain). Sheen and Living Grain keep the grid anchored. Keep source-video motion alone unless the user intentionally wants another animation layered onto it. For a still, select one motion and a restrained `motionSpeed`, rather than stacking effects.
48
50
 
49
51
  For the website's fine-dither treatment, use the module's `fineDither: true` and `ditherStrength` after matching the backing canvas's character settings. This is separate from the core renderer’s built-in dither behavior. Check a still and a moving scene for flicker before choosing the default.
50
52