asciify-engine 4.5.0 → 4.7.0
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/README.md +28 -0
- package/dist/core.d.cts +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/{model-DVm46H2S.d.ts → model-CMKdRB30.d.ts} +92 -1
- package/dist/{model-BLvrvOKA.d.cts → model-fP9NdG-w.d.cts} +92 -1
- package/dist/studio.cjs +1 -1
- package/dist/studio.d.cts +6 -2
- package/dist/studio.d.ts +6 -2
- package/dist/studio.js +1 -1
- package/examples/studio-composition.ts +5 -8
- package/package.json +1 -1
- package/skills/asciify-engine/SKILL.md +6 -2
- package/skills/asciify-engine/references/source-shaping.md +54 -0
- package/skills/asciify-engine/references/studio-workspace.md +33 -0
- package/skills/asciify-engine/references/upgrading.md +8 -0
package/dist/studio.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var t=[{value:"none",label:"Off",group:"Still",description:"Keep the artwork still under the pointer."},{value:"trail",label:"Trail",group:"Change characters",description:"A fluid wake changes character density and settles behind your cursor."},{value:"dissolve",label:"Dissolve",group:"Change characters",description:"Erase a soft path through the characters; the image gradually reforms."},{value:"contour",label:"Contour",group:"Change characters",description:"Fine tonal rings trace your path without moving the grid."},{value:"etch",label:"Emboss",group:"Change characters",description:"Press a light-and-shadow impression into the characters; the grid stays fixed."},{value:"water",label:"Water",group:"Move the image",description:"Soft ripples refract the image and settle after your stroke."},{value:"silk",label:"Silk",group:"Move the image",description:"Directional folds follow the movement of your hand."},{value:"vortex",label:"Vortex",group:"Move the image",description:"A swirling wake curls through the image."},{value:"magnetic",label:"Magnetic Pull",group:"Move the image",description:"The image gathers toward your stroke, then relaxes."},{value:"scatter",label:"Scatter",group:"Move the image",description:"A textured outward scatter settles behind your cursor."},{value:"elastic",label:"Elastic",group:"Move the image",description:"Push the image with your stroke; it recoils and springs back into place."},{value:"rake",label:"Rake",group:"Move the image",description:"Comb the image into alternating ribbons that glide back into alignment."}],e=[{value:"none",mode:"none",group:"Still",label:"Off",description:"Keep the image still. Hover stays available."},{value:"caustics",mode:"caustics",group:"Light and texture",label:"Caustics",description:"Soft ribbons of light travel across a stationary character grid."},{value:"current",mode:"current",group:"Flow and depth",label:"Slow Current",description:"A gentle flow through the image, with steady edges."},{value:"reform",mode:"reform",group:"Rebuild",label:"Reveal & Reform",description:"Disperse, gather, then hold the complete image."},{value:"sheen",mode:"sheen",group:"Light and texture",label:"Sheen",description:"A broad diagonal light sweep; characters stay anchored."},{value:"tidal",mode:"tidal",group:"Flow and depth",label:"Tidal Rings",description:"Concentric currents travel outward, with protected edges."},{value:"grain",mode:"grain",group:"Light and texture",label:"Living Grain",description:"Slow, individually phased tonal changes on a stationary grid."},{value:"parallax",mode:"parallax",group:"Flow and depth",label:"Parallax",description:"A shallow, continuous drift through the image, with the edges held in place."},{value:"weave",mode:"weave",group:"Flow and depth",label:"Woven Flow",description:"Interlaced bands move in opposing directions, like a flexible printed fabric."},{value:"print",mode:"print",group:"Rebuild",label:"Print Shift",description:"Staggered rows lift, soften and register back into the image."},{value:"trace",mode:"trace",group:"Light and texture",label:"Contour Light",description:"Light follows the source image’s tonal contours; every character stays anchored."}],i=t=>Math.max(0,Math.min(1,t)),a=(t,e,a)=>{const s=i((a-t)/(e-t));return s*s*(3-2*s)},s=(t,e)=>{const i=(.06711056*t+.00583715*e)%1*52.9829189;return i-Math.floor(i)},r=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 o(t,e,r,o,n,h=.5){if(n[0]=n[1]=n[2]=0,n[3]=1,"none"===t)return n;const l=o*Math.PI/6,c=a(0,.12,Math.min(e,1-e,r,1-r));if("caustics"===t){const t=Math.sin(9*e+5*r+l)+Math.sin(8*r-3*e-l),i=Math.pow(Math.max(0,1-.72*Math.abs(t)),3);n[2]=(.22*i-.045)*c}else if("current"===t)n[0]=Math.sin(9*r+l)*Math.cos(7*e-l)*10*c,n[1]=Math.cos(8*e+l)*Math.sin(6*r-l)*7*c;else if("sheen"===t)n[2]=.2*Math.pow(.5+.5*Math.cos(5*e+3*r-l),8)-.025;else if("tidal"===t){const t=e-.5,i=r-.5,a=Math.hypot(t,i),s=Math.sin(22*a-l)*c;n[0]=t*s*9,n[1]=i*s*9,n[2]=.035*s}else if("grain"===t){const t=s(Math.floor(160*e),Math.floor(100*r))*Math.PI*2;n[2]=.07*Math.sin(l+t)}else if("parallax"===t){const t=.45+.55*Math.max(0,1-1.4*Math.hypot(e-.5,r-.5));n[0]=12*Math.sin(l)*t*c,n[1]=6*(Math.cos(l)-1)*t*c}else if("weave"===t)n[0]=Math.sin(r*Math.PI*6+l)*Math.sin(l)*8*c,n[1]=Math.sin(e*Math.PI*6-l)*Math.sin(l)*6*c;else if("print"===t){const t=Math.floor(24*r),e=((o+3*s(t,3))%12+12)%12,i=a(1,2.5,e)*(1-a(3.5,5,e));n[0]=Math.sin(2.4*t)*i*9*c,n[1]=5*-i*c,n[2]=.22*-i,n[3]=1-.45*i}else if("trace"===t){const t=.5+.5*Math.cos(l);n[2]=.22*Math.pow(Math.max(0,1-Math.abs(i(h)-t)/.16),3)-.015}else if("reform"===t){const t=(o%12+12)%12,i=s(Math.floor(160*e),Math.floor(100*r)),h=.65*e+.2*r+.15*i+.08*Math.sin(8*e-5*r),m=a(1.8+1.2*h,3.8+1.2*h,t)*(1-a(5+1.7*h,7.2+1.7*h,t));n[0]=Math.sin(9*r+l)*m*(1-m)*24*c,n[1]=-m*(1-m)*(8+12*i)*c,n[2]=.45*-m,n[3]=1-.9*m}return n}function n(t,e){const a=Math.max(0,Math.min(2,Number.isFinite(e)?e:1));return t[0]*=a,t[1]*=a,t[2]*=a,t[3]=i(1+(t[3]-1)*a),t}var h=["ascii","blocks","braille","dots","lines","cross","diagonal","diamond","mixed","pixel","mosaic","lego","voxel","disco","dither","hex","led","cmyk"],l=["none","bayer2","bayer4","bayer8","bayer16","floyd-steinberg","atkinson","stucki","sierra","sierra-lite","burkes","jarvis","halftone","lines","noise","vertical-lines","diagonal-lines","radial","blue-noise"],c=["source-over","multiply","screen","overlay","soft-light","hard-light","color-dodge","color-burn","hue","saturation","color","luminosity"],m={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"]},f={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",blackPoint:0,whitePoint:1,gamma:1,shadows:0,highlights:0},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}},d=t=>t&&"object"==typeof t&&!Array.isArray(t)?t:{},u=(t,e,i,a)=>"number"==typeof t&&Number.isFinite(t)?Math.min(a,Math.max(i,t)):e,g=(t,e)=>"string"==typeof t&&/^#[\da-f]{6}$/i.test(t)?t:e,p=(t,e,i)=>e.includes(t)?t:i;function M(i={}){const a=d(i),s=f;if(void 0!==a.version&&1!==a.version)throw new Error("Unsupported studio settings version. Update asciify-engine.");const r=d(a.crop),o=d(a.backdrop),n=d(a.color),M=d(a.dither),x=d(a.mask),v=d(a.effects),b=d(a.motion),w=d(a.hover),y={...s.effects};for(const t of["bloom","characterBloom","grain","dust","scanlines","crt","prism","rgbSplit","sharpen","vignette","glitch","pixelate","halftone"])y[t]=u(v[t],0,0,1);y.blur=u(v.blur,0,0,24),y.blurType=p(v.blurType,["gaussian","directional","radial","progressive"],s.effects.blurType),y.angle=u(v.angle,0,-180,180),y.focus=u(v.focus,.5,0,1);let k=2048;return{version:1,aspectRatio:p(a.aspectRatio,["original","16:9","9:16","1:1","4:3","3:4","21:9"],"original"),style:p(a.style,h,s.style),cellSize:u(a.cellSize,s.cellSize,["pixel","mosaic","lego","voxel","disco","dots","hex","led","cmyk"].includes(String(a.style))?1:3,60),charset:"string"==typeof a.charset&&a.charset.length?Array.from(a.charset).slice(0,128).join(""):s.charset,colorMode:p(a.colorMode,["accent","source","gray"],s.colorMode),ink:g(a.ink,s.ink),crop:{x:u(r.x,.5,0,1),y:u(r.y,.5,0,1),zoom:u(r.zoom,1,1,5),rotation:90*Math.round(u(r.rotation,0,-360,360)/90)},backdrop:{mode:p(o.mode,["solid","transparent","source","blurred","gradient"],s.backdrop.mode),color:g(o.color,s.backdrop.color),color2:g(o.color2,s.backdrop.color2),blur:u(o.blur,12,0,32),opacity:u(o.opacity,1,0,1)},color:{brightness:u(n.brightness,0,-1,1),contrast:u(n.contrast,1,0,3),saturation:u(n.saturation,1,0,3),grayscale:u(n.grayscale,0,0,1),tint:g(n.tint,s.color.tint),amount:u(n.amount,0,0,1),blend:p(n.blend,c,s.color.blend),blackPoint:u(n.blackPoint,0,0,.99),whitePoint:Math.max(u(n.blackPoint,0,0,.99)+1/255,u(n.whitePoint,1,1/255,1)),gamma:u(n.gamma,1,.25,4),shadows:u(n.shadows,0,-1,1),highlights:u(n.highlights,0,-1,1)},dither:{algorithm:p(M.algorithm,l,"none"),palette:"string"==typeof M.palette&&Object.prototype.hasOwnProperty.call(m,M.palette)?M.palette:"mono",colors:Array.isArray(M.colors)&&M.colors.length>=2?M.colors.slice(0,32).map(t=>g(t,"#000000")):[...s.dither.colors],amount:u(M.amount,1,0,2),scale:Math.round(u(M.scale,2,1,12)),threshold:u(M.threshold,.5,0,1),motion:p(M.motion,["none","drift","shimmer"],"none"),speed:u(M.speed,1,.1,3),direction:u(M.direction,45,-180,180),colorSpace:p(M.colorSpace,["rgb","luminance"],"rgb")},mask:{enabled:!0===x.enabled,invert:!0===x.invert,shapes:Array.isArray(x.shapes)?x.shapes.slice(0,64).map(t=>{const e=d(t),i=p(e.kind,["rectangle","ellipse","brush"],"rectangle"),a="brush"===i&&Array.isArray(e.points)?e.points.slice(0,k).map(t=>Array.isArray(t)?[Math.round(1e4*u(t[0],0,0,1))/1e4,Math.round(1e4*u(t[1],0,0,1))/1e4]:[0,0]):[];return k-=a.length,{kind:i,x:u(e.x,0,0,1),y:u(e.y,0,0,1),width:u(e.width,.5,0,1),height:u(e.height,.5,0,1),size:u(e.size,.05,.005,.5),points:a}}):[]},lights:Array.isArray(a.lights)?a.lights.slice(0,4).map(t=>{const e=d(t);return{x:u(e.x,.5,0,1),y:u(e.y,.5,0,1),radius:u(e.radius,.3,.02,1),intensity:u(e.intensity,.5,0,1),color:g(e.color,"#ffffff")}}):[],effects:y,motion:{type:p(b.type,e.map(t=>t.mode),"none"),speed:u(b.speed,1,.1,3),amount:u(b.amount,1,0,2)},hover:{effect:p(w.effect,t.map(t=>t.value),"trail"),strength:u(w.strength,.55,0,1),radius:u(w.radius,.2,.1,1),edgeSafe:!0===w.edgeSafe}}}function x(t){return JSON.stringify(M(t))}function v(t){if(t.length>1e5)throw new Error("Settings exceed 100 KB.");return M(JSON.parse(t))}function b(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 w(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 y(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 M(i)}function k(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 m="dither"===i.style?i.dither.scale:i.cellSize,f=Math.max(c,m)*h;return{cell:f,rowHeight:f*l,columns:Math.ceil(r/f),rows:Math.ceil(o/(f*l)),minimum:c,limited:m<c}}var S=[["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:M(a)}));function A(t){const e=S.find(e=>e.id===t);if(!e)throw new Error("Unknown studio look.");return M(e.settings)}function E(t,e=Math.random){const i=S.filter(e=>e.settings.style!==t.style||e.settings.ink!==t.ink),a=A(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=M(t).mask,a}function F(t,e,i,a,s,r){a=Math.max(0,Math.min(e-1,a)),s=Math.max(0,Math.min(i-1,s));const o=Math.floor(a),n=Math.floor(s),h=a-o,l=s-n,c=4*(n*e+o);if(0===h&&0===l){for(let e=0;e<4;e++)r[e]=t[c+e];return}const m=4*(n*e+Math.min(e-1,o+1)),f=4*(Math.min(i-1,n+1)*e+o),d=4*(Math.min(i-1,n+1)*e+Math.min(e-1,o+1)),u=(1-h)*(1-l)*t[c+3],g=h*(1-l)*t[m+3],p=(1-h)*l*t[f+3],M=h*l*t[d+3],x=u+g+p+M;for(let e=0;e<3;e++)r[e]=x?(t[c+e]*u+t[m+e]*g+t[f+e]*p+t[d+e]*M)/x:0;r[3]=x}function T(t){const e=t.blackPoint??0,i=t.whitePoint??1,a=t.gamma??1,s=t.shadows??0,r=t.highlights??0;if(0===e&&1===i&&1===a&&0===s&&0===r)return null;const o=[0,.25+.2*s,.5,.75+.2*r,1],n=o.slice(1).map((t,e)=>4*(t-o[e])),h=[n[0],...n.slice(1).map((t,e)=>2*t*n[e]/(t+n[e])),n[3]],l=new Uint8Array(256);for(let t=0;t<256;t++){const s=Math.pow(Math.max(0,Math.min(1,(t/255-e)/(i-e))),1/a),r=Math.min(3,Math.floor(4*s)),n=4*s-r,c=n*n,m=c*n,f=(2*m-3*c+1)*o[r]+(m-2*c+n)*h[r]/4+(-2*m+3*c)*o[r+1]+(m-c)*h[r+1]/4;l[t]=Math.round(255*Math.max(0,Math.min(1,f)))}return l}var P=new Set(["none","floyd-steinberg","atkinson","stucki","sierra","sierra-lite","burkes","jarvis"]);function I(t){return"dither"===t.style&&("shimmer"===t.dither.motion||"drift"===t.dither.motion&&t.dither.amount>0&&!P.has(t.dither.algorithm))}function R(t){return"none"!==t.motion.type&&(t.motion.amount??1)>0}var C="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",L="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",X=new Uint16Array(4096);for(let t=0;t<X.length;t++)X[t]=C.indexOf(L[2*t])<<6|C.indexOf(L[2*t+1]);var D=(t,e)=>{const i=43758.5453*Math.sin(127.1*t+311.7*e);return i-Math.floor(i)};var N=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))])),U={"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]]},O=t=>[parseInt(t.slice(1,3),16),parseInt(t.slice(3,5),16),parseInt(t.slice(5,7),16)];function Y(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 B=t=>["dissolve","silk","vortex","magnetic","scatter","etch","elastic","rake"].includes(t),z=class{constructor(t,e){this.edgeSafe=!1,this.previous=null,this.peak=0,this.scatterDirections=null,this.springX=null,this.springY=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]=Y(a/(t-1),i/(e-1),t/e);this.clear()}get active(){return this.peak>2e-4}setMode(t){if("elastic"!==t||this.springX||(this.springX=new Float32Array(this.ink.length),this.springY=new Float32Array(this.ink.length)),"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,this.springX?.fill(0),this.springY?.fill(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&&"etch"!==this.mode)return;if(a&&c<1e-4)return;const m=.035+.11*Math.max(.1,Math.min(1,i)),f=Math.min(128,Math.max(1,Math.ceil(c/(.45*m)))),d=c?h/c:1,u=c?l/c:0,g=Math.min(1,c/(m*f)*2.2);for(let i=0;i<f;i++){const h=(i+.5)/f,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*m/o)*(s-1))),M=Math.min(s-1,Math.ceil((l+2.5*m/o)*(s-1))),x=Math.max(0,Math.floor((c-2.5*m/n)*(r-1))),v=Math.min(r-1,Math.ceil((c+2.5*m/n)*(r-1)));for(let t=x;t<=v;t++)for(let e=p;e<=M;e++){const i=(e/(s-1)-l)*o/m,a=(t/(r-1)-c)*n/m,h=Math.exp(1.5*-(i*i+a*a)),f=t*s+e;if("dissolve"===this.mode||"etch"===this.mode)this.ink[f]=Math.max(this.ink[f],h);else if("elastic"===this.mode)this.springX[f]=Math.max(-24,Math.min(24,this.springX[f]+d*h*g*8)),this.springY[f]=Math.max(-24,Math.min(24,this.springY[f]+u*h*g*8));else{const s=(-i*u+a*d)*h*2.8,r="rake"===this.mode?d*Math.sin(t*Math.PI/3)*h*2:"magnetic"===this.mode?-i*h*2.5:"scatter"===this.mode?(i+this.scatterDirections[2*f])*h*2:"silk"===this.mode?d*s:-a*h*2.5,o="rake"===this.mode?u*Math.sin(e*Math.PI/3)*h*2:"magnetic"===this.mode?-a*h*2.5:"scatter"===this.mode?(a+this.scatterDirections[2*f+1])*h*2:"silk"===this.mode?u*s:i*h*2.5;this.targetX[f]=Math.max(-1,Math.min(1,this.targetX[f]+r*g)),this.targetY[f]=Math.max(-1,Math.min(1,this.targetY[f]+o*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;const r=Math.sqrt(336),o=Math.exp(-8*e),n=Math.cos(r*e),h=Math.sin(r*e)/r;for(let t=0;t<this.ink.length;t++)if("etch"===this.mode)this.ink[t]*=i,s=Math.max(s,this.ink[t]);else 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{if("elastic"===this.mode){const e=this.offsetX[t],i=this.offsetY[t],a=this.springX[t],r=this.springY[t];this.offsetX[t]=Math.max(-1,Math.min(1,o*(e*n+(a+8*e)*h))),this.offsetY[t]=Math.max(-1,Math.min(1,o*(i*n+(r+8*i)*h))),this.springX[t]=o*(a*n-(8*a+400*e)*h),this.springY[t]=o*(r*n-(8*r+400*i)*h),s=Math.max(s,.05*Math.abs(this.springX[t]),.05*Math.abs(this.springY[t]))}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}if("etch"===this.mode)for(let t=0;t<this.height;t++)for(let e=0;e<this.width;e++){const i=t*this.width+e,a=3*(this.ink[t*this.width+Math.max(0,e-1)]-this.ink[t*this.width+Math.min(this.width-1,e+1)]+this.ink[Math.max(0,t-1)*this.width+e]-this.ink[Math.min(this.height-1,t+1)*this.width+e]);this.offsetX[i]=Math.max(-1,Math.min(1,a))*(this.edgeSafe?this.edge[i]:1);const s=Math.round(32767*this.offsetX[i]+32768);this.pixels[4*i]=s>>>8,this.pixels[4*i+1]=255&s}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,m=t=>(t[l]*(1-n)+t[l+1]*n)*(1-h)+(t[c]*(1-n)+t[c+1]*n)*h;if("etch"===this.mode)return i[0]=i[1]=0,void(i[2]=3*m(this.offsetX));i[0]=m(this.offsetX)*(this.edgeSafe?Y(t,e,this.width/this.height):1)*.08,i[1]=m(this.offsetY)*(this.edgeSafe?Y(t,e,this.width/this.height):1)*.08,i[2]="dissolve"===this.mode?6*-m(this.ink):0}},W=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))}},q=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),m=1-Math.exp(-c/2.2),f=h*(.022+.075*i)*(.65+.95*m),d=Math.min(128,Math.max(1,Math.ceil(n/(.4*f)))),u=n/f*(.32+.22*m)/d,g=r/n,p=o/n,M=-p,x=g,v=n*(2+18*m)/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*f)),h=Math.min(this.width-2,Math.ceil(r+3*f)),l=Math.max(1,Math.floor(o-3*f)),c=Math.min(this.height-2,Math.ceil(o+3*f));for(let t=l;t<=c;t++)for(let e=n;e<=h;e++){const i=((e-r)*g+(t-o)*p)/f,a=((e-r)*M+(t-o)*x)/f,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,m=0|l,f=h-c,d=l-m,u=m*i+c,g=(1-f)*(1-d),p=f*(1-d),M=(1-f)*d,x=f*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 G(t,e){const i=Math.max(0,Math.min(1,e/.38));return t+(1-2*t)*(i*i*(3-2*i))}var j=(t,e)=>"water"===t?30*e/.55:"silk"===t?28*e:"vortex"===t?38*e:["magnetic","scatter","elastic","rake"].includes(t)?32*e:0,V=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]=Y(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 q(this.columns,this.rows),this.contour=new W(this.columns,this.rows),this.afterimage=new z(this.columns,this.rows),this.clear()}get invertsDensity(){return"trail"===this.mode}get densityTrail(){return["trail","contour","dissolve","etch"].includes(this.mode)}get hasRefraction(){return this.active||this.lensStrength>15e-5}get active(){return B(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,B(t)&&this.afterimage.setMode(t),this.refraction.pixels="trail"===t?this.trail.pixels:"contour"===t?this.contour.pixels:B(t)?this.afterimage.pixels:this.waterPixels,this.refraction.strength=j(t,this.amount))}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=j(t,this.amount),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)),B(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,m=Math.max(1,Math.floor((r-h/this.xScale)*(this.columns-1))),f=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=m;e<=f;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(B(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,m=(this.velocity[o]+(h*a*a-8*n)*e)*i*c;this.velocity[o]=m,this.next[o]=(n+m*e)*c,t=Math.max(t,Math.abs(this.next[o]),.1*Math.abs(m))}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(B(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 H(t){return"videoWidth"in t?[t.videoWidth,t.videoHeight]:"naturalWidth"in t?[t.naturalWidth,t.naturalHeight]:[t.width,t.height]}var K=(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}},Q=(t,e,i)=>{t.width===e&&t.height===i||(t.width=e,t.height=i)},J={blocks:" ░▒▓█",braille:" ⠁⠃⠇⡇⣇⣧⣷⣿",lines:" -═╪╬",cross:" ·+xX#",diagonal:" /╱╳",diamond:" ·◇◈◆",mixed:" .:+/◇#@"};function Z(t,e={},i={}){const a=t.getContext("2d");if(!a)throw new Error("Canvas is already owned by another renderer.");const s=K(),r=K(!0),h=K(),l=K(),c=K(),f=K(),d=K(),u=K(),g=K(),p=K();let x,v=0,w=M(e),y=0,S="",A="",E="",P=new Uint8ClampedArray(0),C=new Uint8ClampedArray(0),L=0,Y=0,B=0,z=null,W="",q=new V,j=0;const Z=[0,0,0],_=[0,0,0,1],$=[0,0,0,0];let tt=T(w.color);const et=Math.max(64,Math.min(4096,Number.isFinite(i.maxDimension)?i.maxDimension:960)),it=Math.max(256,Math.min(1048576,Number.isFinite(i.maxCells)?i.maxCells:12e3));let at=it,st=Math.max(.1,Math.min(8,i.pixelRatio??1));return{canvas:t,render:function(e,i=0,M=960,T=540,K=i*w.motion.speed){if(Number.isFinite(i)||(i=0),!Number.isFinite(M)||!Number.isFinite(T)||M<1||T<1)throw new Error("Render dimensions must be positive finite numbers.");const[it,rt]=H(e);if(!it||!rt)return;const ot=Math.min(1,et/Math.max(M,T)),nt=Math.max(2,Math.round(M*ot)),ht=Math.max(2,Math.round(T*ot));Q(t,nt,ht),Q(s.canvas,nt,ht),Q(h.canvas,nt,ht),Q(l.canvas,nt,ht),Q(c.canvas,nt,ht),j!==nt/ht&&(j=nt/ht,q=new V(j)),q.configure(w.hover.effect,w.hover.strength,w.hover.radius,w.hover.edgeSafe),q.step(Math.max(0,Math.min(.05,i-B))),B=i;const lt="currentTime"in e?e.getVideoPlaybackQuality?.().totalVideoFrames??e.currentTime:0,ct=[y,nt,ht,lt].join(":"),mt="dither"===w.style,ft=k(nt,ht,w,at,st),dt=ft.cell,ut=ft.rowHeight,gt=ft.columns,pt=ft.rows;if(S!==ct||z!==e||L!==gt||Y!==pt){z=e,S=ct,A="",E="",L=gt,Y=pt,s.ctx.clearRect(0,0,nt,ht),s.ctx.save();const t=b(it,rt,nt,ht,w.crop);s.ctx.translate(t.x+t.width/2,t.y+t.height/2),s.ctx.rotate(w.crop.rotation*Math.PI/180),s.ctx.drawImage(e,-it*t.scale/2,-rt*t.scale/2,it*t.scale,rt*t.scale),s.ctx.restore(),Q(r.canvas,L,Y),r.ctx.clearRect(0,0,L,Y),r.ctx.drawImage(s.canvas,0,0,L,Y),P=r.ctx.getImageData(0,0,L,Y).data;const i=w.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 a=0;a<3;a++){const s=e+(P[t+a]-e)*i.saturation;P[t+a]=((s*(1-i.grayscale)+e*i.grayscale)/255-.5)*i.contrast*255+127.5+255*i.brightness}}tt&&function(t,e){for(let i=0;i<t.length;i+=4)0!==t[i+3]&&(t[i]=e[t[i]],t[i+1]=e[t[i+1]],t[i+2]=e[t[i+2]])}(P,tt)}const Mt=R(w),xt=I(w),vt=!q.active&&!Mt&&!xt,bt=[ct,L,Y,dt,ut,st].join(":");if(!vt||E!==bt){h.ctx.clearRect(0,0,nt,ht),h.ctx.globalAlpha=1,h.ctx.globalCompositeOperation="source-over";const t=K,e=Mt?w.motion.type:"none",a=["none","caustics","sheen","grain","trace"].includes(e);if(mt){const a=vt;if(!a||A!==ct){C.length!==P.length&&(C=new Uint8ClampedArray(P.length));const s=C;if(s.set(P),q.active||"none"!==e)for(let i=0;i<Y;i++)for(let a=0;a<L;a++){const r=a/L,h=i/Y;let l=a,c=i,m=0,f=1,d=1;q.active&&(q.sample(r,h,Z),l-=Z[0]*L,c-=Z[1]*Y,m=Z[2]/3);const u=4*(i*L+a);o(e,r,h,t,_,(.299*P[u]+.587*P[u+1]+.114*P[u+2])/255),n(_,w.motion.amount??1),l-=_[0]/960*L,c-=_[1]/960*Y,f=1+_[2],d=_[3],l=Math.max(0,Math.min(L-1,l)),c=Math.max(0,Math.min(Y-1,c));const g=Math.floor(l),p=Math.floor(c),M=l-g,x=c-p,v=4*(i*L+a),b=4*(p*L+g),y=4*(p*L+Math.min(L-1,g+1)),k=4*(Math.min(Y-1,p+1)*L+g),S=4*(Math.min(Y-1,p+1)*L+Math.min(L-1,g+1));for(let t=0;t<3;t++){const e=((P[b+t]*(1-M)+P[y+t]*M)*(1-x)+(P[k+t]*(1-M)+P[S+t]*M)*x)/255;let i=(["trail","contour"].includes(w.hover.effect)?G(e,m):"etch"===w.hover.effect?Math.max(0,Math.min(1,e+.35*m)):"dissolve"===w.hover.effect?e*Math.max(0,1+m):e)*f;s[v+t]=255*i}s[v+3]=P[b+3]*d}!function(t,e,i,a,s=0){const{algorithm:r,amount:o,threshold:n}=a,h="blue-noise"===r,l=r.startsWith("bayer")?Number(r.slice(5)):0,c=N.get(l),f=l-1,d="luminance"===a.colorSpace,u=("custom"===a.palette?a.colors:m[a.palette]??m.mono).map(O),g=U[r],p=g?new Float32Array(3*e*3):null,M="drift"===a.motion?s*a.speed*3:0,x=(a.direction??45)*Math.PI/180,v=Math.round(Math.cos(x)*M),b=Math.round(Math.sin(x)*M),w=(t,e)=>(t%e+e)%e,y=u.map(t=>.299*t[0]+.587*t[1]+.114*t[2]),k="shimmer"===a.motion?.15*Math.sin(s*a.speed*2):0;for(let a=0;a<i;a++)for(let s=0;s<e;s++){const m=4*(a*e+s),x=3*(a%3*e+s);if(0===t[m+3]){p&&(p[x]=p[x+1]=p[x+2]=0);continue}let S=0;const A=h?(X[64*(a-b&63)+(s-v&63)]+.5)/X.length:0;c?S=c[(a-b&f)*l+(s-v&f)]:"noise"===r?S=D(s-v,a-b)-.5:"lines"===r?S=w(a-b,4)/3-.5:"vertical-lines"===r?S=w(s-v,4)/3-.5:"diagonal-lines"===r?S=w(s-v+a-b,4)/3-.5:"radial"===r?S=w(Math.hypot(s-e/2,a-i/2)-M,6)/6-.5:"halftone"===r&&(S=Math.hypot(w(s-v,6)-2.5,w(a-b,6)-2.5)/3.54-.5);const E=128*(S*o+k+(.5-n)),F=t[m]+E+(p?.[x]??0),T=t[m+1]+E+(p?.[x+1]??0),P=t[m+2]+E+(p?.[x+2]??0);let I=0,R=0,C=0;if(u.length){let t=1/0,e=1/0,i=0,a=0;const s=.299*F+.587*T+.114*P;for(let r=0;r<u.length;r++){const o=u[r],n=d?(s-y[r])**2:(F-o[0])**2*.299+(T-o[1])**2*.587+(P-o[2])**2*.114;n<t?(h&&(e=t,a=i,i=r),t=n,[I,R,C]=o):h&&n<e&&(e=n,a=r)}if(h&&o>0&&Number.isFinite(e)){const t=u[a],e=t[0]-I,r=t[1]-R,n=t[2]-C,h=y[a]-y[i],l=d?h*h:e*e*.299+r*r*.587+n*n*.114,c=d?(s-y[i])*h:(F-I)*e*.299+(T-R)*r*.587+(P-C)*n*.114;A<(l?Math.max(0,Math.min(1,c/l*o)):0)&&([I,R,C]=t)}}else if(h){const t=Math.max(0,Math.min(1,.5+(A-.5)*o));I=51*Math.floor(F/51+1-t),R=51*Math.floor(T/51+1-t),C=51*Math.floor(P/51+1-t)}else I=51*Math.round(F/51),R=51*Math.round(T/51),C=51*Math.round(P/51);if(t[m]=I,t[m+1]=R,t[m+2]=C,p&&g){p[x]=p[x+1]=p[x+2]=0;for(const[t,r,n]of g){const h=s+t;if(h<0||h>=e||a+r>=i)continue;const l=3*((a+r)%3*e+h);p[l]+=(F-I)*n*o,p[l+1]+=(T-R)*n*o,p[l+2]+=(P-C)*n*o}}}}(s,L,Y,w.dither,i),r.ctx.putImageData(new ImageData(s,L,Y),0,0),A=a?ct:""}h.ctx.imageSmoothingEnabled=!1,h.ctx.drawImage(r.canvas,0,0,nt,ht)}else{const i=Array.from(J[w.style]??w.charset),s=h.ctx,r=O(w.ink),l="ascii"===w.style||!!J[w.style],c=Math.max(3,Math.round(.92*ut)),m=Math.ceil(dt+4),f=Math.ceil(ut+4),u=[i.join(""),c,m,f,w.ink,w.colorMode].join(":");if(l&&u!==W&&(W=u,Q(d.canvas,m*i.length,f),d.ctx.clearRect(0,0,d.canvas.width,f),d.ctx.font=`${c}px monospace`,d.ctx.textBaseline="middle",d.ctx.textAlign="center",d.ctx.fillStyle="accent"===w.colorMode?w.ink:"#ffffff",i.forEach((t,e)=>d.ctx.fillText(t,e*m+m/2,f/2))),"lego"===w.style&&v!==dt){v=dt,Q(p.canvas,Math.ceil(dt),Math.ceil(ut));const t=p.ctx;t.clearRect(0,0,p.canvas.width,p.canvas.height),t.fillStyle="rgba(255,255,255,.2)",t.strokeStyle="rgba(0,0,0,.25)",t.lineWidth=Math.max(.5,.6*st),t.beginPath(),t.arc(dt/2,ut/2,.28*dt,0,2*Math.PI),t.fill(),t.stroke()}s.font=`${c}px monospace`,s.textBaseline="middle",s.textAlign="center";for(let h=0;h<Y;h++)for(let c=0;c<L;c++){let u=c,g=h,M=0;if(q.active&&(q.sample(c/L,h/Y,Z),u=Math.max(0,Math.min(L-1,c-Z[0]*L)),g=Math.max(0,Math.min(Y-1,h-Z[1]*Y)),M=Z[2]/3),F(P,L,Y,u,g,$),!$[3])continue;const x=$[0],v=$[1],b=$[2],y=(.299*x+.587*v+.114*b)/255;let k=y;["trail","contour"].includes(w.hover.effect)&&M?k=G(k,M):"etch"===w.hover.effect?k=Math.max(0,Math.min(1,k+.35*M)):"dissolve"===w.hover.effect&&(k*=Math.max(0,1+M));let S=c*dt,A=h*ut,E=1;o(e,c/L,h/Y,t,_,y),n(_,w.motion.amount??1),S+=_[0]*nt/960,A+=_[1]*ht/960,k=Math.max(0,Math.min(1,k+_[2])),E=_[3]*$[3]/255,l||"accent"!==w.colorMode||"cmyk"===w.style||(E*=k),s.globalAlpha=E;const T=255*(k-y),I="source"===w.colorMode?`rgb(${x+T},${v+T},${b+T})`:"gray"===w.colorMode?`rgb(${Math.round(255*k)},${Math.round(255*k)},${Math.round(255*k)})`:w.ink;if(s.fillStyle=I,l){const t=Math.max(0,Math.min(i.length-1,Math.round(k*(i.length-1))));"accent"===w.colorMode?s.drawImage(d.canvas,t*m,0,m,f,a?Math.round(S+(dt-m)/2):S+(dt-m)/2,a?Math.round(A+(ut-f)/2):A+(ut-f)/2,m,f):s.fillText(i[t],S+dt/2,A+ut/2)}else if("dots"===w.style)s.beginPath(),s.arc(S+dt/2,A+ut/2,.48*dt*Math.sqrt(k),0,2*Math.PI),s.fill();else if("led"===w.style)s.beginPath(),s.arc(S+dt/2,A+ut/2,.39*dt,0,2*Math.PI),s.fill(),s.globalAlpha=.35*E,s.fillStyle="#ffffff",s.beginPath(),s.arc(S+.43*dt,A+.4*ut,.11*dt,0,2*Math.PI),s.fill();else if("hex"===w.style){const t=S+dt/2,e=A+ut/2;s.beginPath();for(let i=0;i<6;i++){const a=Math.PI/3*i,r=t+Math.cos(a)*dt*.49,o=e+Math.sin(a)*ut*.49;0===i?s.moveTo(r,o):s.lineTo(r,o)}s.closePath(),s.fill()}else if("cmyk"===w.style){s.fillStyle="#ffffff",s.fillRect(S,A,dt,ut);const t=(t,e)=>Math.max(0,Math.min(255,"source"===w.colorMode?t+T:"gray"===w.colorMode?255*k:e*k)),e=t(x,r[0]),i=t(v,r[1]),a=t(b,r[2]),o=1-Math.max(e,i,a)/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-a/255-o)/n,.5,.66],["#000000",o,.5,.5]];s.globalCompositeOperation="multiply";for(const[t,e,i,a]of h)s.fillStyle=t,s.beginPath(),s.arc(S+dt*i,A+ut*a,.48*dt*Math.sqrt(Math.max(0,e)),0,2*Math.PI),s.fill();s.globalCompositeOperation="source-over"}else if("voxel"===w.style){const t=S+dt/2,e=A+ut/2;s.beginPath(),s.moveTo(t,A),s.lineTo(S+dt,A+.25*ut),s.lineTo(t,e),s.lineTo(S,A+.25*ut),s.closePath(),s.fill(),s.globalAlpha=.72*E,s.beginPath(),s.moveTo(S,A+.25*ut),s.lineTo(t,e),s.lineTo(t,A+ut),s.lineTo(S,A+.75*ut),s.closePath(),s.fill(),s.globalAlpha=.42*E,s.beginPath(),s.moveTo(t,e),s.lineTo(S+dt,A+.25*ut),s.lineTo(S+dt,A+.75*ut),s.lineTo(t,A+ut),s.closePath(),s.fill()}else{const t="pixel"===w.style?0:Math.max(.5,.06*dt);s.fillRect(S,A,dt-t,ut-t),"lego"===w.style&&s.drawImage(p.canvas,S,A),"disco"===w.style&&(s.fillStyle=`rgba(255,255,255,${.1+.25*D(c,h)})`,s.fillRect(S,A,dt-t,.35*ut),s.fillStyle="rgba(0,0,0,.25)",s.fillRect(S,A+.65*ut,dt-t,.35*ut-t))}}}h.ctx.globalAlpha=1,(w.color.amount||w.lights.length)&&(Q(u.canvas,nt,ht),u.ctx.clearRect(0,0,nt,ht),u.ctx.drawImage(h.canvas,0,0)),w.color.amount&&(h.ctx.save(),h.ctx.globalCompositeOperation=w.color.blend,h.ctx.globalAlpha=w.color.amount,h.ctx.fillStyle=w.color.tint,h.ctx.fillRect(0,0,nt,ht),h.ctx.restore());for(const t of w.lights){const e=t.radius*Math.max(nt,ht),i=h.ctx.createRadialGradient(t.x*nt,t.y*ht,0,t.x*nt,t.y*ht,e);i.addColorStop(0,t.color),i.addColorStop(1,"transparent"),h.ctx.save(),h.ctx.globalCompositeOperation="screen",h.ctx.globalAlpha=t.intensity,h.ctx.fillStyle=i,h.ctx.fillRect(0,0,nt,ht),h.ctx.restore()}if((w.color.amount||w.lights.length)&&(h.ctx.globalCompositeOperation="destination-in",h.ctx.drawImage(u.canvas,0,0),h.ctx.globalCompositeOperation="source-over"),w.mask.enabled&&w.mask.shapes.length){const t=c.ctx;t.clearRect(0,0,nt,ht),t.fillStyle="#fff",t.strokeStyle="#fff",t.lineCap="round",t.lineJoin="round";for(const e of w.mask.shapes){if(t.beginPath(),"ellipse"===e.kind)t.ellipse((e.x+e.width/2)*nt,(e.y+e.height/2)*ht,e.width*nt/2,e.height*ht/2,0,0,2*Math.PI);else{if("rectangle"!==e.kind){if(t.lineWidth=(e.size??.05)*nt,1===e.points?.length){t.arc(e.points[0][0]*nt,e.points[0][1]*ht,t.lineWidth/2,0,2*Math.PI),t.fill();continue}e.points?.forEach(([e,i],a)=>a?t.lineTo(e*nt,i*ht):t.moveTo(e*nt,i*ht)),t.stroke();continue}t.rect(e.x*nt,e.y*ht,e.width*nt,e.height*ht)}t.fill()}h.ctx.globalCompositeOperation=w.mask.invert?"destination-out":"destination-in",h.ctx.drawImage(c.canvas,0,0),h.ctx.globalCompositeOperation="source-over"}const M=l.ctx;M.clearRect(0,0,nt,ht),M.globalAlpha=w.backdrop.opacity;const x=w.backdrop;if("solid"===x.mode&&(M.fillStyle=x.color,M.fillRect(0,0,nt,ht)),"gradient"===x.mode){const t=M.createLinearGradient(0,0,nt,ht);t.addColorStop(0,x.color),t.addColorStop(1,x.color2),M.fillStyle=t,M.fillRect(0,0,nt,ht)}if("source"===x.mode&&M.drawImage(s.canvas,0,0),"blurred"===x.mode){const t=Math.max(1,x.blur);Q(f.canvas,Math.max(2,Math.round(nt/t)),Math.max(2,Math.round(ht/t))),f.ctx.drawImage(s.canvas,0,0,f.canvas.width,f.canvas.height),M.imageSmoothingEnabled=!0,M.drawImage(f.canvas,0,0,nt,ht)}M.globalAlpha=1,w.effects.characterBloom>0&&(Q(g.canvas,Math.max(2,Math.round(nt/3)),Math.max(2,Math.round(ht/3))),g.ctx.clearRect(0,0,g.canvas.width,g.canvas.height),g.ctx.drawImage(h.canvas,0,0,g.canvas.width,g.canvas.height),M.save(),M.globalAlpha=.8*w.effects.characterBloom,M.globalCompositeOperation="screen",M.filter=`blur(${1+4*w.effects.characterBloom}px)`,M.drawImage(g.canvas,0,0,nt,ht),M.restore()),M.drawImage(h.canvas,0,0),E=vt?bt:""}const wt=Object.entries(w.effects).some(([t,e])=>!["angle","focus","blurType","characterBloom"].includes(t)&&"number"==typeof e&&e>0);a.clearRect(0,0,nt,ht),wt?(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?a.drawImage(x.render(l.canvas,w.effects,i,st),0,0):a.drawImage(l.canvas,0,0),t.dataset.studioFinish=x?"gpu":"unavailable"):(a.drawImage(l.canvas,0,0),t.dataset.studioFinish="none")},setPixelRatio(t){st=Math.max(.1,Math.min(8,Number.isFinite(t)?t:1))},get pixelRatio(){return st},setBudget(t){at=Math.max(256,Math.min(it,Number.isFinite(t)?Math.round(t):it))},get maxCells(){return at},configure(t){w=M(t),tt=T(w.color),y++},pointer:(t,e,i)=>{q.configure(w.hover.effect,w.hover.strength,w.hover.radius,w.hover.edgeSafe),q.move(t,e,i)},leave(){q.leave()},get active(){return q.active},get settings(){return M(w)},get capabilities(){return{gpuFinish:null!==x}},invalidate(){S=""},destroy(){x?.destroy();for(const t of[s,r,h,l,c,f,d,u,g,p])t.canvas.width=t.canvas.height=1;z=null,P=new Uint8ClampedArray(0)}}}function _(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 $(t,e){if("string"==typeof t&&t.startsWith("blob:")){const i=await fetch(t,{signal:e}),a=await i.blob();return $(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 m=r(n,!0);if(m.length*h*l*4>128e6)throw new Error("GIF exceeds the 128 MB decoded budget. Use MP4 for long animations.");const f=document.createElement("canvas");f.width=h,f.height=l;const d=f.getContext("2d"),u=document.createElement("canvas"),g=u.getContext("2d"),p=m.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<m.length-1&&(i+=Math.max(20,m[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=m[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=m[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,f};return v(0),{source:f,width:h,height:l,duration:p,animated:!0,seek:async()=>{},frame:v,destroy(){f.width=u.width=1,m.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=_(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=_(o,"seeked",e);o.currentTime=i,await a},frame:()=>o,destroy:n}}catch(t){throw n(),t}}var tt=t=>{if(t?.aborted)throw new DOMException("Export cancelled","AbortError")};async function et(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 l="png"===a||"jpeg"===a,c=i.time??0;if(!Number.isFinite(c)||c<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(!l&&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"),f=Z(m,M(e),{maxDimension:4096,maxCells:i.maxCells??12e3,pixelRatio:r/(i.referenceWidth??Math.min(r,960))});let d;try{if(tt(s),i.onProgress?.(0),l){if(f.render(await t.frame(c,s),c,r,o,i.motionTime),"unavailable"===m.dataset.studioFinish)throw new Error("Optical effects need WebGL. Disable these effects or export in a supported browser.");tt(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:d}="function"==typeof a.GIFEncoder?a:a.default,u=l(),g=m.getContext("2d");for(let a=0;a<e;a++){tt(s);const l=a/n;if(f.invalidate(),f.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=d(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:u,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 u({format:"mp4"===a?new g({fastStart:"in-memory"}):new p,target:new M});m.width=r,m.height=o,d=()=>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++){tt(s);const l=a/n;if(f.invalidate(),f.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(),tt(s),await w.finalize(),d=void 0,i.onProgress?.(1),new Blob([w.target.buffer],{type:"mp4"===a?"video/mp4":"video/webm"})}catch(t){throw await(d?.().catch(()=>{})),t}finally{f.destroy(),m.width=m.height=1}}async function it(t,e,i={}){const a=await $(e,i.signal);try{return at(t,a,i)}catch(t){throw a.destroy(),t}}function at(t,e,i={}){let a=M(i.settings),s=i.width??e.width,o=i.height??e.height;const n=Z(t,a,i),h=new r;let l=0,c=!1,m=!0,f=!1,d=!0,u=0,g=0,p=0,x=0,v=-1,b=!1!==i.adaptive,k=0,S=0,A=0;const E=matchMedia("(prefers-reduced-motion: reduce)"),F=e.source instanceof HTMLVideoElement?e.source:null,T=()=>{c||l||!d||document.hidden||(l=requestAnimationFrame(L))},P=()=>!f&&!E.matches&&d&&!document.hidden,C=()=>{F&&(F.loop=!0,P()?F.play().catch(()=>{f=!0,m=!0,T()}):F.pause()),p=0,m=!0,T()};function L(t){if(l=0,c||!d||document.hidden)return;const r=P();r&&(u+=p?Math.min(.1,(t-p)/1e3):0),p=t;const M=function(t){return R(t)||I(t)||t.effects.grain>0||t.effects.dust>0||t.effects.glitch>0}(a),y=r&&(e.animated||M),E=F?.getVideoPlaybackQuality?.().totalVideoFrames??-1,C=!F||E<0||E!==v;if(m||n.active||y&&(C||M)&&t-x>=1e3/Math.max(1,Math.min(60,i.fps??60))-.5){const r=performance.now();try{e.animated&&!F&&n.invalidate();const[t,l]=w(s,o,a);g=h.update(u,a.motion.type,a.motion.speed),n.render(e.frame(F?.currentTime??u),u,t,l,g);const c=performance.now()-r;i.onFrame?.(F?.currentTime??u,c),S=S?.9*S+.1*c:c,++k>20&&b&&(y||n.active)&&(A=S>13?A+1:0,A>=12&&n.maxCells>3e3&&(n.setBudget(Math.max(3e3,.8*n.maxCells)),i.onQualityChange?.(n.maxCells),A=0,S=0))}catch(t){f=!0,i.onError?.(t instanceof Error?t:new Error(String(t)))}m=!1,x=t,v=E}r&&(y||n.active)&&T()}const X=e=>{if(!P())return;const i=t.getBoundingClientRect();n.pointer((e.clientX-i.left)/i.width,(e.clientY-i.top)/i.height,e.timeStamp),T()},D=()=>{n.leave(),T()},N=new IntersectionObserver(([t])=>{d=t.isIntersecting,C()});N.observe(t),t.addEventListener("pointermove",X),t.addEventListener("pointerleave",D),t.addEventListener("pointercancel",D),document.addEventListener("visibilitychange",C),E.addEventListener("change",C);const U=()=>{c||(c=!0,cancelAnimationFrame(l),N.disconnect(),t.removeEventListener("pointermove",X),t.removeEventListener("pointerleave",D),t.removeEventListener("pointercancel",D),document.removeEventListener("visibilitychange",C),E.removeEventListener("change",C),i.signal?.removeEventListener("abort",U),n.destroy(),e.destroy())};if(i.signal?.addEventListener("abort",U,{once:!0}),i.signal?.aborted)throw U(),new DOMException("Cancelled","AbortError");return C(),{canvas:t,media:e,update(t){a=y(a,t),n.configure(a),m=!0,T()},resize(t,e,i){void 0!==i&&n.setPixelRatio(i),s=Math.max(2,t),o=Math.max(2,e),m=!0,T()},pause(t=!0){f=t,C()},async seek(t){u=Math.max(0,t),await e.seek(u),n.invalidate(),m=!0,T()},redraw(){n.invalidate(),m=!0,T()},get time(){return F?.currentTime??u},get motionTime(){return g},get paused(){return f||E.matches},setAdaptive(t){b=t,k=A=S=0},setBudget(t){n.setBudget(t),i.onQualityChange?.(n.maxCells),m=!0,T()},get pixelRatio(){return n.pixelRatio},get maxCells(){return n.maxCells},get capabilities(){return n.capabilities},destroy:U}}var st={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")},rt=Object.keys(st);async function ot(t,e="Standard",i="#ffffff"){if(!Object.prototype.hasOwnProperty.call(st,e))throw new Error("Unknown text font.");const{default:a}=await import("figlet/browser"),s=await st[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 m=c.getContext("2d");return m.fillStyle="#080808",m.fillRect(0,0,c.width,c.height),m.fillStyle=i,m.font=15*l+"px monospace",m.textBaseline="top",o.forEach((t,e)=>m.fillText(t,16,16+15*e*l)),{canvas:c,ascii:r}}var nt={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:" ⠁⠃⠇⡇⣇⣧⣷⣿"}},ht=nt;export{c as BLEND_MODES,nt as CHARACTER_SETS,f as DEFAULT_STUDIO_SETTINGS,l as DITHER_ALGORITHMS,ht as STUDIO_CHARACTER_SETS,t as STUDIO_HOVERS,S as STUDIO_LOOKS,e as STUDIO_MOTIONS,m as STUDIO_PALETTES,h as STUDIO_STYLES,rt as STUDIO_TEXT_FONTS,Z as createStudioRenderer,ot as createStudioText,et as exportStudio,$ as loadStudioMedia,it as mountStudio,at as mountStudioMedia,M as normalizeStudioSettings,v as parseStudioSettings,E as restyleStudio,x as serializeStudioSettings,b as studioCrop,w as studioDimensions,k as studioGrid,A as studioLook,H as studioSourceSize,y as updateStudioSettings};//# sourceMappingURL=studio.js.map
|
|
1
|
+
var t=[{value:"none",label:"Off",group:"Still",description:"Keep the artwork still under the pointer."},{value:"trail",label:"Trail",group:"Change characters",description:"A fluid wake changes character density and settles behind your cursor."},{value:"dissolve",label:"Dissolve",group:"Change characters",description:"Erase a soft path through the characters; the image gradually reforms."},{value:"contour",label:"Contour",group:"Change characters",description:"Fine tonal rings trace your path without moving the grid."},{value:"etch",label:"Emboss",group:"Change characters",description:"Press a light-and-shadow impression into the characters; the grid stays fixed."},{value:"water",label:"Water",group:"Move the image",description:"Soft ripples refract the image and settle after your stroke."},{value:"silk",label:"Silk",group:"Move the image",description:"Directional folds follow the movement of your hand."},{value:"vortex",label:"Vortex",group:"Move the image",description:"A swirling wake curls through the image."},{value:"magnetic",label:"Magnetic Pull",group:"Move the image",description:"The image gathers toward your stroke, then relaxes."},{value:"scatter",label:"Scatter",group:"Move the image",description:"A textured outward scatter settles behind your cursor."},{value:"elastic",label:"Elastic",group:"Move the image",description:"Push the image with your stroke; it recoils and springs back into place."},{value:"rake",label:"Rake",group:"Move the image",description:"Comb the image into alternating ribbons that glide back into alignment."}],e=[{value:"none",mode:"none",group:"Still",label:"Off",description:"Keep the image still. Hover stays available."},{value:"caustics",mode:"caustics",group:"Light and texture",label:"Caustics",description:"Soft ribbons of light travel across a stationary character grid."},{value:"current",mode:"current",group:"Flow and depth",label:"Slow Current",description:"A gentle flow through the image, with steady edges."},{value:"reform",mode:"reform",group:"Rebuild",label:"Reveal & Reform",description:"Disperse, gather, then hold the complete image."},{value:"sheen",mode:"sheen",group:"Light and texture",label:"Sheen",description:"A broad diagonal light sweep; characters stay anchored."},{value:"tidal",mode:"tidal",group:"Flow and depth",label:"Tidal Rings",description:"Concentric currents travel outward, with protected edges."},{value:"grain",mode:"grain",group:"Light and texture",label:"Living Grain",description:"Slow, individually phased tonal changes on a stationary grid."},{value:"parallax",mode:"parallax",group:"Flow and depth",label:"Parallax",description:"A shallow, continuous drift through the image, with the edges held in place."},{value:"weave",mode:"weave",group:"Flow and depth",label:"Woven Flow",description:"Interlaced bands move in opposing directions, like a flexible printed fabric."},{value:"print",mode:"print",group:"Rebuild",label:"Print Shift",description:"Staggered rows lift, soften and register back into the image."},{value:"trace",mode:"trace",group:"Light and texture",label:"Contour Light",description:"Light follows the source image’s tonal contours; every character stays anchored."}],i=t=>Math.max(0,Math.min(1,t)),a=(t,e,a)=>{const s=i((a-t)/(e-t));return s*s*(3-2*s)},s=(t,e)=>{const i=(.06711056*t+.00583715*e)%1*52.9829189;return i-Math.floor(i)},r=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 o(t,e,r,o,n,h=.5){if(n[0]=n[1]=n[2]=0,n[3]=1,"none"===t)return n;const l=o*Math.PI/6,c=a(0,.12,Math.min(e,1-e,r,1-r));if("caustics"===t){const t=Math.sin(9*e+5*r+l)+Math.sin(8*r-3*e-l),i=Math.pow(Math.max(0,1-.72*Math.abs(t)),3);n[2]=(.22*i-.045)*c}else if("current"===t)n[0]=Math.sin(9*r+l)*Math.cos(7*e-l)*10*c,n[1]=Math.cos(8*e+l)*Math.sin(6*r-l)*7*c;else if("sheen"===t)n[2]=.2*Math.pow(.5+.5*Math.cos(5*e+3*r-l),8)-.025;else if("tidal"===t){const t=e-.5,i=r-.5,a=Math.hypot(t,i),s=Math.sin(22*a-l)*c;n[0]=t*s*9,n[1]=i*s*9,n[2]=.035*s}else if("grain"===t){const t=s(Math.floor(160*e),Math.floor(100*r))*Math.PI*2;n[2]=.07*Math.sin(l+t)}else if("parallax"===t){const t=.45+.55*Math.max(0,1-1.4*Math.hypot(e-.5,r-.5));n[0]=12*Math.sin(l)*t*c,n[1]=6*(Math.cos(l)-1)*t*c}else if("weave"===t)n[0]=Math.sin(r*Math.PI*6+l)*Math.sin(l)*8*c,n[1]=Math.sin(e*Math.PI*6-l)*Math.sin(l)*6*c;else if("print"===t){const t=Math.floor(24*r),e=((o+3*s(t,3))%12+12)%12,i=a(1,2.5,e)*(1-a(3.5,5,e));n[0]=Math.sin(2.4*t)*i*9*c,n[1]=5*-i*c,n[2]=.22*-i,n[3]=1-.45*i}else if("trace"===t){const t=.5+.5*Math.cos(l);n[2]=.22*Math.pow(Math.max(0,1-Math.abs(i(h)-t)/.16),3)-.015}else if("reform"===t){const t=(o%12+12)%12,i=s(Math.floor(160*e),Math.floor(100*r)),h=.65*e+.2*r+.15*i+.08*Math.sin(8*e-5*r),f=a(1.8+1.2*h,3.8+1.2*h,t)*(1-a(5+1.7*h,7.2+1.7*h,t));n[0]=Math.sin(9*r+l)*f*(1-f)*24*c,n[1]=-f*(1-f)*(8+12*i)*c,n[2]=.45*-f,n[3]=1-.9*f}return n}function n(t,e){const a=Math.max(0,Math.min(2,Number.isFinite(e)?e:1));return t[0]*=a,t[1]*=a,t[2]*=a,t[3]=i(1+(t[3]-1)*a),t}var h=["rgb","red","green","blue"],l=t=>Math.max(0,Math.min(1,t));function c(t){const e=new Map([[0,0],[1,1]]);if(Array.isArray(t))for(const i of t.slice(0,64)){if(!Array.isArray(i)||i.length<2||"number"!=typeof i[0]||!Number.isFinite(i[0])||"number"!=typeof i[1]||!Number.isFinite(i[1]))continue;const t=Math.round(255*l(i[0]))/255;!e.has(t)&&e.size>=16||e.set(t,l(i[1]))}return[...e].sort((t,e)=>t[0]-e[0])}function f(t){const e=t&&"object"==typeof t?t:{};return Object.fromEntries(h.map(t=>[t,c(e[t])]))}function m(t){const e=c(t),i=e.length,a=e.slice(1).map((t,i)=>t[0]-e[i][0]),s=e.slice(1).map((t,i)=>(t[1]-e[i][1])/a[i]),r=new Float64Array(i);r[0]=s[0],r[i-1]=s[i-2];for(let t=1;t<i-1;t++){const e=s[t-1],i=s[t];if(e*i<=0)continue;const o=2*a[t]+a[t-1],n=a[t]+2*a[t-1];r[t]=(o+n)/(o/e+n/i)}const o=new Uint8Array(256);let n=0;for(let t=0;t<256;t++){const s=t/255;for(;n<i-2&&s>e[n+1][0];)n++;const h=a[n],c=(s-e[n][0])/h,f=c*c,m=f*c,d=(2*m-3*f+1)*e[n][1]+(m-2*f+c)*h*r[n]+(-2*m+3*f)*e[n+1][1]+(m-f)*h*r[n+1];o[t]=Math.round(255*l(d))}return o}function d(t,e,i,a,s,r){a=Math.max(0,Math.min(e-1,a)),s=Math.max(0,Math.min(i-1,s));const o=Math.floor(a),n=Math.floor(s),h=a-o,l=s-n,c=4*(n*e+o);if(0===h&&0===l){for(let e=0;e<4;e++)r[e]=t[c+e];return}const f=4*(n*e+Math.min(e-1,o+1)),m=4*(Math.min(i-1,n+1)*e+o),d=4*(Math.min(i-1,n+1)*e+Math.min(e-1,o+1)),u=(1-h)*(1-l)*t[c+3],g=h*(1-l)*t[f+3],p=(1-h)*l*t[m+3],M=h*l*t[d+3],v=u+g+p+M;for(let e=0;e<3;e++)r[e]=v?(t[c+e]*u+t[f+e]*g+t[m+e]*p+t[d+e]*M)/v:0;r[3]=v}function u(t,e,i,a,s,r){if(a>=0&&a<=e-1&&s>=0&&s<=i-1)return d(t,e,i,a,s,r);if(r[0]=r[1]=r[2]=r[3]=0,a<=-1||a>=e||s<=-1||s>=i)return;const o=Math.floor(a),n=Math.floor(s),h=a-o,l=s-n;for(let a=0;a<=1;a++)for(let s=0;s<=1;s++){const c=o+s,f=n+a;if(c<0||c>=e||f<0||f>=i)continue;const m=4*(f*e+c),d=(s?h:1-h)*(a?l:1-l)*t[m+3];r[0]+=t[m]*d,r[1]+=t[m+1]*d,r[2]+=t[m+2]*d,r[3]+=d}r[3]&&(r[0]/=r[3],r[1]/=r[3],r[2]/=r[3])}var g=[{value:"twirl",label:"Twirl",description:"Rotate the center into a smooth spiral; the outer boundary stays fixed.",direction:!1,frequency:!1},{value:"pinch",label:"Pinch",description:"Contract the center, or expand it with negative strength.",direction:!1,frequency:!1},{value:"sphere",label:"Spherize",description:"Magnify a rounded lens through the source; negative strength recedes.",direction:!1,frequency:!1},{value:"ripple",label:"Ripple",description:"Bend the image through concentric, soft-edged waves.",direction:!1,frequency:!0},{value:"zigzag",label:"Zigzag",description:"Alternate the twist in sharp, concentric bands.",direction:!1,frequency:!0},{value:"shear",label:"Shear",description:"Slant a region along an adjustable direction.",direction:!0,frequency:!1},{value:"smudge",label:"Smudge",description:"Draw a soft region of the source along one direction.",direction:!0,frequency:!1},{value:"polar",label:"Polar",description:"Unwrap a region into polar coordinates; negative strength wraps it.",direction:!0,frequency:!1},{value:"fragment",label:"Fragment",description:"Offset small source tiles into a stable fractured texture.",direction:!0,frequency:!0}],p=8,M=(t,e,i,a)=>"number"==typeof t&&Number.isFinite(t)?Math.max(i,Math.min(a,t)):e;function v(t){if(!Array.isArray(t))return[];const e=[];for(const i of t.slice(0,64))if(i&&"object"==typeof i&&g.some(t=>t.value===i.type)&&(e.push({type:i.type,enabled:!1!==i.enabled,amount:M(i.amount,.35,-1,1),x:M(i.x,.5,0,1),y:M(i.y,.5,0,1),radius:M(i.radius,.55,.05,2),angle:M(i.angle,0,-180,180),frequency:M(i.frequency,5,1,24)}),8===e.length))break;return e}var x=2*Math.PI,b=(t,e)=>{let i=Math.imul(t,374761393)^Math.imul(e,668265263);return i=Math.imul(i^i>>>13,1274126177),((i^i>>>16)>>>0)/4294967296},w=class{constructor(t=[],e="clamp"){this.warps=[],this.signature="",this.geometry="",this.coordinates=new Float32Array(0),this.output=new Uint8ClampedArray(0),this.sampled=[0,0,0,0],this.edge="clamp",this.configure(t,e)}configure(t=[],e="clamp"){const i=v(t).filter(t=>t.enabled&&0!==t.amount),a=JSON.stringify(i);this.edge="transparent"===e?"transparent":"clamp",a!==this.signature&&(this.signature=a,this.geometry="",this.warps=i.map(t=>({...t,cos:Math.cos(t.angle*Math.PI/180),sin:Math.sin(t.angle*Math.PI/180)})),i.length||this.clear())}get active(){return this.warps.length>0}apply(t,e,i,a=e/i){if(!this.active)return t;if(!Number.isInteger(e)||!Number.isInteger(i)||e<1||i<1||e*i*4!==t.length||!Number.isFinite(a)||a<=0)throw new Error("Warp dimensions do not match the source.");const s=`${e}:${i}:${a}`;if(s!==this.geometry){this.coordinates.length!==e*i*2&&(this.coordinates=new Float32Array(e*i*2));const t=Math.max(1,a),r=Math.max(1,1/a);for(let a=0;a<i;a++)for(let s=0;s<e;s++){let o=(s+.5)/e,n=(a+.5)/i;for(let e=this.warps.length-1;e>=0;e--){const i=this.warps[e],a=(o-i.x)*t/i.radius,s=(n-i.y)*r/i.radius,h=a*a+s*s;if(h>=1)continue;const l=Math.sqrt(h),c=(1-h)*(1-h),f=i.amount;let m=a,d=s;if("twirl"===i.type||"zigzag"===i.type){const t="twirl"===i.type?-f*x*c:.65*f*(2/Math.PI*Math.asin(Math.sin(l*x*i.frequency)))*c,e=Math.cos(t),r=Math.sin(t);m=a*e-s*r,d=a*r+s*e}else if("pinch"===i.type){const t=Math.exp(2*f*c);m=a*t,d=s*t}else if("sphere"===i.type&&l>0){const t=1+f*c*(2*Math.asin(l)/Math.PI/l-1);m=a*t,d=s*t}else if("ripple"===i.type&&l>0){const t=1+.12*f*Math.sin(l*x*i.frequency)*c/l;m=a*t,d=s*t}else if("shear"===i.type){const t=-a*i.sin+s*i.cos;m-=f*t*.9*c*i.cos,d-=f*t*.9*c*i.sin}else if("smudge"===i.type)m-=.65*f*c*i.cos,d-=.65*f*c*i.sin;else if("polar"===i.type){const t=a*i.cos+s*i.sin,e=-a*i.sin+s*i.cos,r=Math.atan2(e,t),o=f>0?r/Math.PI:Math.cos(t*Math.PI)*(e+1)/2,n=f>0?2*l-1:Math.sin(t*Math.PI)*(e+1)/2,h=o*i.cos-n*i.sin,u=o*i.sin+n*i.cos;m+=(h-a)*Math.abs(f)*c,d+=(u-s)*Math.abs(f)*c}else if("fragment"===i.type){const t=Math.floor((a*i.cos+s*i.sin)*i.frequency),e=Math.floor((-a*i.sin+s*i.cos)*i.frequency),r=(b(t,e)-.5)*f*.5*c,o=(b(e+71,t-31)-.5)*f*.5*c;m+=r*i.cos-o*i.sin,d+=r*i.sin+o*i.cos}o=i.x+m*i.radius/t,n=i.y+d*i.radius/r}const h=2*(a*e+s);this.coordinates[h]=o*e-.5,this.coordinates[h+1]=n*i-.5}this.geometry=s}this.output.length!==t.length&&(this.output=new Uint8ClampedArray(t.length));const r=t===this.output?t.slice():t,o="transparent"===this.edge?u:d;for(let a=0,s=0;a<t.length;a+=4,s+=2){o(r,e,i,this.coordinates[s],this.coordinates[s+1],this.sampled);for(let t=0;t<4;t++)this.output[a+t]=this.sampled[t]}return this.output}clear(){this.coordinates=new Float32Array(0),this.output=new Uint8ClampedArray(0),this.geometry=""}},y=["ascii","blocks","braille","dots","lines","cross","diagonal","diamond","mixed","pixel","mosaic","lego","voxel","disco","dither","hex","led","cmyk"],k=["none","bayer2","bayer4","bayer8","bayer16","floyd-steinberg","atkinson","stucki","sierra","sierra-lite","burkes","jarvis","halftone","lines","noise","vertical-lines","diagonal-lines","radial","blue-noise"],S=["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"]},E={version:1,aspectRatio:"original",style:"ascii",cellSize:6,charset:" .:-=+*#%@",colorMode:"accent",ink:"#e8b900",crop:{x:.5,y:.5,zoom:1,rotation:0},warps:[],warpEdge:"clamp",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",blackPoint:0,whitePoint:1,gamma:1,shadows:0,highlights:0,curves:{rgb:[[0,0],[1,1]],red:[[0,0],[1,1]],green:[[0,0],[1,1]],blue:[[0,0],[1,1]]},denoise:0},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}},F=t=>t&&"object"==typeof t&&!Array.isArray(t)?t:{},T=(t,e,i,a)=>"number"==typeof t&&Number.isFinite(t)?Math.min(a,Math.max(i,t)):e,P=(t,e)=>"string"==typeof t&&/^#[\da-f]{6}$/i.test(t)?t:e,I=(t,e,i)=>e.includes(t)?t:i;function R(i={}){const a=F(i),s=E;if(void 0!==a.version&&1!==a.version)throw new Error("Unsupported studio settings version. Update asciify-engine.");const r=F(a.crop),o=F(a.backdrop),n=F(a.color),h=F(a.dither),l=F(a.mask),c=F(a.effects),m=F(a.motion),d=F(a.hover),u={...s.effects};for(const t of["bloom","characterBloom","grain","dust","scanlines","crt","prism","rgbSplit","sharpen","vignette","glitch","pixelate","halftone"])u[t]=T(c[t],0,0,1);u.blur=T(c.blur,0,0,24),u.blurType=I(c.blurType,["gaussian","directional","radial","progressive"],s.effects.blurType),u.angle=T(c.angle,0,-180,180),u.focus=T(c.focus,.5,0,1);let g=2048;return{version:1,aspectRatio:I(a.aspectRatio,["original","16:9","9:16","1:1","4:3","3:4","21:9"],"original"),style:I(a.style,y,s.style),cellSize:T(a.cellSize,s.cellSize,["pixel","mosaic","lego","voxel","disco","dots","hex","led","cmyk"].includes(String(a.style))?1:3,60),charset:"string"==typeof a.charset&&a.charset.length?Array.from(a.charset).slice(0,128).join(""):s.charset,colorMode:I(a.colorMode,["accent","source","gray"],s.colorMode),ink:P(a.ink,s.ink),warps:v(a.warps),warpEdge:I(a.warpEdge,["clamp","transparent"],"clamp"),crop:{x:T(r.x,.5,0,1),y:T(r.y,.5,0,1),zoom:T(r.zoom,1,1,5),rotation:90*Math.round(T(r.rotation,0,-360,360)/90)},backdrop:{mode:I(o.mode,["solid","transparent","source","blurred","gradient"],s.backdrop.mode),color:P(o.color,s.backdrop.color),color2:P(o.color2,s.backdrop.color2),blur:T(o.blur,12,0,32),opacity:T(o.opacity,1,0,1)},color:{brightness:T(n.brightness,0,-1,1),contrast:T(n.contrast,1,0,3),saturation:T(n.saturation,1,0,3),grayscale:T(n.grayscale,0,0,1),tint:P(n.tint,s.color.tint),amount:T(n.amount,0,0,1),blend:I(n.blend,S,s.color.blend),blackPoint:T(n.blackPoint,0,0,.99),whitePoint:Math.max(T(n.blackPoint,0,0,.99)+1/255,T(n.whitePoint,1,1/255,1)),gamma:T(n.gamma,1,.25,4),shadows:T(n.shadows,0,-1,1),highlights:T(n.highlights,0,-1,1),curves:f(n.curves),denoise:T(n.denoise,0,0,1)},dither:{algorithm:I(h.algorithm,k,"none"),palette:"string"==typeof h.palette&&Object.prototype.hasOwnProperty.call(A,h.palette)?h.palette:"mono",colors:Array.isArray(h.colors)&&h.colors.length>=2?h.colors.slice(0,32).map(t=>P(t,"#000000")):[...s.dither.colors],amount:T(h.amount,1,0,2),scale:Math.round(T(h.scale,2,1,12)),threshold:T(h.threshold,.5,0,1),motion:I(h.motion,["none","drift","shimmer"],"none"),speed:T(h.speed,1,.1,3),direction:T(h.direction,45,-180,180),colorSpace:I(h.colorSpace,["rgb","luminance"],"rgb")},mask:{enabled:!0===l.enabled,invert:!0===l.invert,shapes:Array.isArray(l.shapes)?l.shapes.slice(0,64).map(t=>{const e=F(t),i=I(e.kind,["rectangle","ellipse","brush"],"rectangle"),a="brush"===i&&Array.isArray(e.points)?e.points.slice(0,g).map(t=>Array.isArray(t)?[Math.round(1e4*T(t[0],0,0,1))/1e4,Math.round(1e4*T(t[1],0,0,1))/1e4]:[0,0]):[];return g-=a.length,{kind:i,x:T(e.x,0,0,1),y:T(e.y,0,0,1),width:T(e.width,.5,0,1),height:T(e.height,.5,0,1),size:T(e.size,.05,.005,.5),points:a}}):[]},lights:Array.isArray(a.lights)?a.lights.slice(0,4).map(t=>{const e=F(t);return{x:T(e.x,.5,0,1),y:T(e.y,.5,0,1),radius:T(e.radius,.3,.02,1),intensity:T(e.intensity,.5,0,1),color:P(e.color,"#ffffff")}}):[],effects:u,motion:{type:I(m.type,e.map(t=>t.mode),"none"),speed:T(m.speed,1,.1,3),amount:T(m.amount,1,0,2)},hover:{effect:I(d.effect,t.map(t=>t.value),"trail"),strength:T(d.strength,.55,0,1),radius:T(d.radius,.2,.1,1),edgeSafe:!0===d.edgeSafe}}}function C(t){return JSON.stringify(R(t))}function L(t){if(t.length>1e5)throw new Error("Settings exceed 100 KB.");return R(JSON.parse(t))}function N(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 X(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 e.color?.curves&&(i.color.curves={...t.color.curves,...e.color.curves}),R(i)}function D(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 O=[["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:R(a)}));function Y(t){const e=O.find(e=>e.id===t);if(!e)throw new Error("Unknown studio look.");return R(e.settings)}function q(t,e=Math.random){const i=O.filter(e=>e.settings.style!==t.style||e.settings.ink!==t.ink),a=Y(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=R(t).mask,a}function B(t){const e=function(t){const e=t.blackPoint??0,i=t.whitePoint??1,a=t.gamma??1,s=t.shadows??0,r=t.highlights??0;if(0===e&&1===i&&1===a&&0===s&&0===r)return null;const o=[0,.25+.2*s,.5,.75+.2*r,1],n=o.slice(1).map((t,e)=>4*(t-o[e])),h=[n[0],...n.slice(1).map((t,e)=>2*t*n[e]/(t+n[e])),n[3]],l=new Uint8Array(256);for(let t=0;t<256;t++){const s=Math.pow(Math.max(0,Math.min(1,(t/255-e)/(i-e))),1/a),r=Math.min(3,Math.floor(4*s)),n=4*s-r,c=n*n,f=c*n,m=(2*f-3*c+1)*o[r]+(f-2*c+n)*h[r]/4+(-2*f+3*c)*o[r+1]+(f-c)*h[r+1]/4;l[t]=Math.round(255*Math.max(0,Math.min(1,m)))}return l}(t),i=t.curves;if(!e&&(!i||Object.values(i).every(t=>t.every(([t,e])=>t===e))))return null;const a=m(i?.rgb),s=[i?.red,i?.green,i?.blue].map(m),r=new Uint8Array(768);for(let t=0;t<256;t++)for(let i=0;i<3;i++)r[256*i+t]=s[i][a[e?e[t]:t]];return r}var z=class{constructor(t=0){this.amount=0,this.weights=new Float32Array(256),this.source=new Uint8ClampedArray(0),this.configure(t)}configure(t=0){const e=Math.max(0,Math.min(1,Number.isFinite(t)?t:0));if(e===this.amount)return;if(this.amount=e,0===e)return void(this.source=new Uint8ClampedArray(0));const i=8+24*e;for(let t=0;t<256;t++)this.weights[t]=Math.exp(-t*t/(2*i*i))}apply(t,e,i){if(!this.amount)return;if(!Number.isInteger(e)||!Number.isInteger(i)||e<1||i<1||e*i*4!==t.length)throw new Error("Denoise dimensions do not match the source.");this.source.length!==t.length&&(this.source=new Uint8ClampedArray(t.length));const a=this.source;a.set(t);for(let s=0;s<i;s++)for(let r=0;r<e;r++){const o=4*(s*e+r);if(!a[o+3])continue;const n=a[o],h=a[o+1],l=a[o+2];let c=0,f=0,m=0,d=0;for(let t=-1;t<=1;t++){const o=s+t;if(!(o<0||o>=i))for(let i=-1;i<=1;i++){const s=r+i;if(s<0||s>=e)continue;const u=4*(o*e+s);if(!a[u+3])continue;const g=Math.max(Math.abs(a[u]-n),Math.abs(a[u+1]-h),Math.abs(a[u+2]-l)),p=this.weights[g]*(0===i?1:.6)*(0===t?1:.6)*a[u+3];c+=a[u]*p,f+=a[u+1]*p,m+=a[u+2]*p,d+=p}}t[o]=n+(c/d-n)*this.amount,t[o+1]=h+(f/d-h)*this.amount,t[o+2]=l+(m/d-l)*this.amount}}},W=new Set(["none","floyd-steinberg","atkinson","stucki","sierra","sierra-lite","burkes","jarvis"]);function G(t){return"dither"===t.style&&("shimmer"===t.dither.motion||"drift"===t.dither.motion&&t.dither.amount>0&&!W.has(t.dither.algorithm))}function j(t){return"none"!==t.motion.type&&(t.motion.amount??1)>0}var V="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",H="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",J=new Uint16Array(4096);for(let t=0;t<J.length;t++)J[t]=V.indexOf(H[2*t])<<6|V.indexOf(H[2*t+1]);var K=(t,e)=>{const i=43758.5453*Math.sin(127.1*t+311.7*e);return i-Math.floor(i)};var Q=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))])),Z={"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=>[parseInt(t.slice(1,3),16),parseInt(t.slice(3,5),16),parseInt(t.slice(5,7),16)];function $(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 tt=t=>["dissolve","silk","vortex","magnetic","scatter","etch","elastic","rake"].includes(t),et=class{constructor(t,e){this.edgeSafe=!1,this.previous=null,this.peak=0,this.scatterDirections=null,this.springX=null,this.springY=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]=$(a/(t-1),i/(e-1),t/e);this.clear()}get active(){return this.peak>2e-4}setMode(t){if("elastic"!==t||this.springX||(this.springX=new Float32Array(this.ink.length),this.springY=new Float32Array(this.ink.length)),"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,this.springX?.fill(0),this.springY?.fill(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&&"etch"!==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))),v=Math.max(0,Math.floor((c-2.5*f/n)*(r-1))),x=Math.min(r-1,Math.ceil((c+2.5*f/n)*(r-1)));for(let t=v;t<=x;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||"etch"===this.mode)this.ink[m]=Math.max(this.ink[m],h);else if("elastic"===this.mode)this.springX[m]=Math.max(-24,Math.min(24,this.springX[m]+d*h*g*8)),this.springY[m]=Math.max(-24,Math.min(24,this.springY[m]+u*h*g*8));else{const s=(-i*u+a*d)*h*2.8,r="rake"===this.mode?d*Math.sin(t*Math.PI/3)*h*2:"magnetic"===this.mode?-i*h*2.5:"scatter"===this.mode?(i+this.scatterDirections[2*m])*h*2:"silk"===this.mode?d*s:-a*h*2.5,o="rake"===this.mode?u*Math.sin(e*Math.PI/3)*h*2:"magnetic"===this.mode?-a*h*2.5:"scatter"===this.mode?(a+this.scatterDirections[2*m+1])*h*2:"silk"===this.mode?u*s:i*h*2.5;this.targetX[m]=Math.max(-1,Math.min(1,this.targetX[m]+r*g)),this.targetY[m]=Math.max(-1,Math.min(1,this.targetY[m]+o*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;const r=Math.sqrt(336),o=Math.exp(-8*e),n=Math.cos(r*e),h=Math.sin(r*e)/r;for(let t=0;t<this.ink.length;t++)if("etch"===this.mode)this.ink[t]*=i,s=Math.max(s,this.ink[t]);else 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{if("elastic"===this.mode){const e=this.offsetX[t],i=this.offsetY[t],a=this.springX[t],r=this.springY[t];this.offsetX[t]=Math.max(-1,Math.min(1,o*(e*n+(a+8*e)*h))),this.offsetY[t]=Math.max(-1,Math.min(1,o*(i*n+(r+8*i)*h))),this.springX[t]=o*(a*n-(8*a+400*e)*h),this.springY[t]=o*(r*n-(8*r+400*i)*h),s=Math.max(s,.05*Math.abs(this.springX[t]),.05*Math.abs(this.springY[t]))}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}if("etch"===this.mode)for(let t=0;t<this.height;t++)for(let e=0;e<this.width;e++){const i=t*this.width+e,a=3*(this.ink[t*this.width+Math.max(0,e-1)]-this.ink[t*this.width+Math.min(this.width-1,e+1)]+this.ink[Math.max(0,t-1)*this.width+e]-this.ink[Math.min(this.height-1,t+1)*this.width+e]);this.offsetX[i]=Math.max(-1,Math.min(1,a))*(this.edgeSafe?this.edge[i]:1);const s=Math.round(32767*this.offsetX[i]+32768);this.pixels[4*i]=s>>>8,this.pixels[4*i+1]=255&s}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;if("etch"===this.mode)return i[0]=i[1]=0,void(i[2]=3*f(this.offsetX));i[0]=f(this.offsetX)*(this.edgeSafe?$(t,e,this.width/this.height):1)*.08,i[1]=f(this.offsetY)*(this.edgeSafe?$(t,e,this.width/this.height):1)*.08,i[2]="dissolve"===this.mode?6*-f(this.ink):0}},it=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))}},at=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,v=g,x=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)*v)/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*x)),this.v[n]=Math.max(-80,Math.min(80,this.v[n]+(p*h+v*l)*s*x))}}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,v=m*d;this.a[n]=(this.u[u]*g+this.u[u+1]*p+this.u[u+i]*M+this.u[u+i+1]*v)*s,this.b[n]=(this.v[u]*g+this.v[u+1]*p+this.v[u+i]*M+this.v[u+i+1]*v)*s,this.c[n]=(this.ink[u]*g+this.ink[u+1]*p+this.ink[u+i]*M+this.ink[u+i+1]*v)*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 st(t,e){const i=Math.max(0,Math.min(1,e/.38));return t+(1-2*t)*(i*i*(3-2*i))}var rt=(t,e)=>"water"===t?30*e/.55:"silk"===t?28*e:"vortex"===t?38*e:["magnetic","scatter","elastic","rake"].includes(t)?32*e:0,ot=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/(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 at(this.columns,this.rows),this.contour=new it(this.columns,this.rows),this.afterimage=new et(this.columns,this.rows),this.clear()}get invertsDensity(){return"trail"===this.mode}get densityTrail(){return["trail","contour","dissolve","etch"].includes(this.mode)}get hasRefraction(){return this.active||this.lensStrength>15e-5}get active(){return tt(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,tt(t)&&this.afterimage.setMode(t),this.refraction.pixels="trail"===t?this.trail.pixels:"contour"===t?this.contour.pixels:tt(t)?this.afterimage.pixels:this.waterPixels,this.refraction.strength=rt(t,this.amount))}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=rt(t,this.amount),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)),tt(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(tt(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(tt(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 nt(t){return"videoWidth"in t?[t.videoWidth,t.videoHeight]:"naturalWidth"in t?[t.naturalWidth,t.naturalHeight]:[t.width,t.height]}var ht=(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}},lt=(t,e,i)=>{t.width===e&&t.height===i||(t.width=e,t.height=i)},ct={blocks:" ░▒▓█",braille:" ⠁⠃⠇⡇⣇⣧⣷⣿",lines:" -═╪╬",cross:" ·+xX#",diagonal:" /╱╳",diamond:" ·◇◈◆",mixed:" .:+/◇#@"};function ft(t,e={},i={}){const a=t.getContext("2d");if(!a)throw new Error("Canvas is already owned by another renderer.");const s=ht(),r=ht(!0),h=ht(),l=ht(),c=ht(),f=ht(),m=ht(),u=ht(),g=ht(),p=ht();let M,v=0,x=R(e),b=0,y="",k="",S="",E=new Uint8ClampedArray(0),F=new Uint8ClampedArray(0),T=0,P=0,I=0,C=null,L="",X=new ot,U=0;const O=[0,0,0],Y=[0,0,0,1],q=[0,0,0,0];let W=B(x.color);const V=new z(x.color.denoise),H=new w(x.warps,x.warpEdge),$=Math.max(64,Math.min(4096,Number.isFinite(i.maxDimension)?i.maxDimension:960)),tt=Math.max(256,Math.min(1048576,Number.isFinite(i.maxCells)?i.maxCells:12e3));let et=tt,it=Math.max(.1,Math.min(8,i.pixelRatio??1));return{canvas:t,render:function(e,i=0,w=960,R=540,B=i*x.motion.speed){if(Number.isFinite(i)||(i=0),!Number.isFinite(w)||!Number.isFinite(R)||w<1||R<1)throw new Error("Render dimensions must be positive finite numbers.");const[z,tt]=nt(e);if(!z||!tt)return;const at=Math.min(1,$/Math.max(w,R)),rt=Math.max(2,Math.round(w*at)),ht=Math.max(2,Math.round(R*at));lt(t,rt,ht),lt(s.canvas,rt,ht),lt(h.canvas,rt,ht),lt(l.canvas,rt,ht),lt(c.canvas,rt,ht),U!==rt/ht&&(U=rt/ht,X=new ot(U)),X.configure(x.hover.effect,x.hover.strength,x.hover.radius,x.hover.edgeSafe),X.step(Math.max(0,Math.min(.05,i-I))),I=i;const ft="currentTime"in e?e.getVideoPlaybackQuality?.().totalVideoFrames??e.currentTime:0,mt=[b,rt,ht,ft].join(":"),dt="dither"===x.style,ut=D(rt,ht,x,et,it),gt=ut.cell,pt=ut.rowHeight,Mt=ut.columns,vt=ut.rows;if(y!==mt||C!==e||T!==Mt||P!==vt){C=e,y=mt,k="",S="",T=Mt,P=vt,s.ctx.clearRect(0,0,rt,ht),s.ctx.save();const t=N(z,tt,rt,ht,x.crop);s.ctx.translate(t.x+t.width/2,t.y+t.height/2),s.ctx.rotate(x.crop.rotation*Math.PI/180),s.ctx.drawImage(e,-z*t.scale/2,-tt*t.scale/2,z*t.scale,tt*t.scale),s.ctx.restore(),lt(r.canvas,T,P),r.ctx.clearRect(0,0,T,P),r.ctx.drawImage(s.canvas,0,0,T,P),E=r.ctx.getImageData(0,0,T,P).data,V.apply(E,T,P);const i=x.color;for(let t=0;t<E.length;t+=4){const e=.299*E[t]+.587*E[t+1]+.114*E[t+2];for(let a=0;a<3;a++){const s=e+(E[t+a]-e)*i.saturation;E[t+a]=((s*(1-i.grayscale)+e*i.grayscale)/255-.5)*i.contrast*255+127.5+255*i.brightness}}W&&function(t,e){for(let i=0;i<t.length;i+=4)t[i+3]&&(t[i]=e[t[i]],t[i+1]=e[256+t[i+1]],t[i+2]=e[512+t[i+2]])}(E,W),E=H.apply(E,T,P,rt/ht)}const xt=j(x),bt=G(x),wt=!X.active&&!xt&&!bt,yt=[mt,T,P,gt,pt,it].join(":");if(!wt||S!==yt){h.ctx.clearRect(0,0,rt,ht),h.ctx.globalAlpha=1,h.ctx.globalCompositeOperation="source-over";const t=B,e=xt?x.motion.type:"none",a=["none","caustics","sheen","grain","trace"].includes(e);if(dt){const a=wt;if(!a||k!==mt){F.length!==E.length&&(F=new Uint8ClampedArray(E.length));const s=F;if(s.set(E),X.active||"none"!==e)for(let i=0;i<P;i++)for(let a=0;a<T;a++){const r=a/T,h=i/P;let l=a,c=i,f=0,m=1,u=1;X.active&&(X.sample(r,h,O),l-=O[0]*T,c-=O[1]*P,f=O[2]/3);const g=4*(i*T+a);o(e,r,h,t,Y,(.299*E[g]+.587*E[g+1]+.114*E[g+2])/255),n(Y,x.motion.amount??1),l-=Y[0]/960*T,c-=Y[1]/960*P,m=1+Y[2],u=Y[3];const p=4*(i*T+a);d(E,T,P,l,c,q);for(let t=0;t<3;t++){const e=q[t]/255;let i=(["trail","contour"].includes(x.hover.effect)?st(e,f):"etch"===x.hover.effect?Math.max(0,Math.min(1,e+.35*f)):"dissolve"===x.hover.effect?e*Math.max(0,1+f):e)*m;s[p+t]=255*i}s[p+3]=q[3]*u}!function(t,e,i,a,s=0){const{algorithm:r,amount:o,threshold:n}=a,h="blue-noise"===r,l=r.startsWith("bayer")?Number(r.slice(5)):0,c=Q.get(l),f=l-1,m="luminance"===a.colorSpace,d=("custom"===a.palette?a.colors:A[a.palette]??A.mono).map(_),u=Z[r],g=u?new Float32Array(3*e*3):null,p="drift"===a.motion?s*a.speed*3:0,M=(a.direction??45)*Math.PI/180,v=Math.round(Math.cos(M)*p),x=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"===a.motion?.15*Math.sin(s*a.speed*2):0;for(let a=0;a<i;a++)for(let s=0;s<e;s++){const M=4*(a*e+s),k=3*(a%3*e+s);if(0===t[M+3]){g&&(g[k]=g[k+1]=g[k+2]=0);continue}let S=0;const A=h?(J[64*(a-x&63)+(s-v&63)]+.5)/J.length:0;c?S=c[(a-x&f)*l+(s-v&f)]:"noise"===r?S=K(s-v,a-x)-.5:"lines"===r?S=b(a-x,4)/3-.5:"vertical-lines"===r?S=b(s-v,4)/3-.5:"diagonal-lines"===r?S=b(s-v+a-x,4)/3-.5:"radial"===r?S=b(Math.hypot(s-e/2,a-i/2)-p,6)/6-.5:"halftone"===r&&(S=Math.hypot(b(s-v,6)-2.5,b(a-x,6)-2.5)/3.54-.5);const E=128*(S*o+y+(.5-n)),F=t[M]+E+(g?.[k]??0),T=t[M+1]+E+(g?.[k+1]??0),P=t[M+2]+E+(g?.[k+2]??0);let I=0,R=0,C=0;if(d.length){let t=1/0,e=1/0,i=0,a=0;const s=.299*F+.587*T+.114*P;for(let r=0;r<d.length;r++){const o=d[r],n=m?(s-w[r])**2:(F-o[0])**2*.299+(T-o[1])**2*.587+(P-o[2])**2*.114;n<t?(h&&(e=t,a=i,i=r),t=n,[I,R,C]=o):h&&n<e&&(e=n,a=r)}if(h&&o>0&&Number.isFinite(e)){const t=d[a],e=t[0]-I,r=t[1]-R,n=t[2]-C,h=w[a]-w[i],l=m?h*h:e*e*.299+r*r*.587+n*n*.114,c=m?(s-w[i])*h:(F-I)*e*.299+(T-R)*r*.587+(P-C)*n*.114;A<(l?Math.max(0,Math.min(1,c/l*o)):0)&&([I,R,C]=t)}}else if(h){const t=Math.max(0,Math.min(1,.5+(A-.5)*o));I=51*Math.floor(F/51+1-t),R=51*Math.floor(T/51+1-t),C=51*Math.floor(P/51+1-t)}else I=51*Math.round(F/51),R=51*Math.round(T/51),C=51*Math.round(P/51);if(t[M]=I,t[M+1]=R,t[M+2]=C,g&&u){g[k]=g[k+1]=g[k+2]=0;for(const[t,r,n]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]+=(F-I)*n*o,g[l+1]+=(T-R)*n*o,g[l+2]+=(P-C)*n*o}}}}(s,T,P,x.dither,i),r.ctx.putImageData(new ImageData(s,T,P),0,0),k=a?mt:""}h.ctx.imageSmoothingEnabled=!1,h.ctx.drawImage(r.canvas,0,0,rt,ht)}else{const i=Array.from(ct[x.style]??x.charset),s=h.ctx,r=_(x.ink),l="ascii"===x.style||!!ct[x.style],c=Math.max(3,Math.round(.92*pt)),f=Math.ceil(gt+4),u=Math.ceil(pt+4),g=[i.join(""),c,f,u,x.ink,x.colorMode].join(":");if(l&&g!==L&&(L=g,lt(m.canvas,f*i.length,u),m.ctx.clearRect(0,0,m.canvas.width,u),m.ctx.font=`${c}px monospace`,m.ctx.textBaseline="middle",m.ctx.textAlign="center",m.ctx.fillStyle="accent"===x.colorMode?x.ink:"#ffffff",i.forEach((t,e)=>m.ctx.fillText(t,e*f+f/2,u/2))),"lego"===x.style&&v!==gt){v=gt,lt(p.canvas,Math.ceil(gt),Math.ceil(pt));const t=p.ctx;t.clearRect(0,0,p.canvas.width,p.canvas.height),t.fillStyle="rgba(255,255,255,.2)",t.strokeStyle="rgba(0,0,0,.25)",t.lineWidth=Math.max(.5,.6*it),t.beginPath(),t.arc(gt/2,pt/2,.28*gt,0,2*Math.PI),t.fill(),t.stroke()}s.font=`${c}px monospace`,s.textBaseline="middle",s.textAlign="center";for(let h=0;h<P;h++)for(let c=0;c<T;c++){let g=c,M=h,v=0;if(X.active&&(X.sample(c/T,h/P,O),g=Math.max(0,Math.min(T-1,c-O[0]*T)),M=Math.max(0,Math.min(P-1,h-O[1]*P)),v=O[2]/3),d(E,T,P,g,M,q),!q[3])continue;const b=q[0],w=q[1],y=q[2],k=(.299*b+.587*w+.114*y)/255;let S=k;["trail","contour"].includes(x.hover.effect)&&v?S=st(S,v):"etch"===x.hover.effect?S=Math.max(0,Math.min(1,S+.35*v)):"dissolve"===x.hover.effect&&(S*=Math.max(0,1+v));let A=c*gt,F=h*pt,I=1;o(e,c/T,h/P,t,Y,k),n(Y,x.motion.amount??1),A+=Y[0]*rt/960,F+=Y[1]*ht/960,S=Math.max(0,Math.min(1,S+Y[2])),I=Y[3]*q[3]/255,l||"accent"!==x.colorMode||"cmyk"===x.style||(I*=S),s.globalAlpha=I;const R=255*(S-k),C="source"===x.colorMode?`rgb(${b+R},${w+R},${y+R})`:"gray"===x.colorMode?`rgb(${Math.round(255*S)},${Math.round(255*S)},${Math.round(255*S)})`:x.ink;if(s.fillStyle=C,l){const t=Math.max(0,Math.min(i.length-1,Math.round(S*(i.length-1))));"accent"===x.colorMode?s.drawImage(m.canvas,t*f,0,f,u,a?Math.round(A+(gt-f)/2):A+(gt-f)/2,a?Math.round(F+(pt-u)/2):F+(pt-u)/2,f,u):s.fillText(i[t],A+gt/2,F+pt/2)}else if("dots"===x.style)s.beginPath(),s.arc(A+gt/2,F+pt/2,.48*gt*Math.sqrt(S),0,2*Math.PI),s.fill();else if("led"===x.style)s.beginPath(),s.arc(A+gt/2,F+pt/2,.39*gt,0,2*Math.PI),s.fill(),s.globalAlpha=.35*I,s.fillStyle="#ffffff",s.beginPath(),s.arc(A+.43*gt,F+.4*pt,.11*gt,0,2*Math.PI),s.fill();else if("hex"===x.style){const t=A+gt/2,e=F+pt/2;s.beginPath();for(let i=0;i<6;i++){const a=Math.PI/3*i,r=t+Math.cos(a)*gt*.49,o=e+Math.sin(a)*pt*.49;0===i?s.moveTo(r,o):s.lineTo(r,o)}s.closePath(),s.fill()}else if("cmyk"===x.style){s.fillStyle="#ffffff",s.fillRect(A,F,gt,pt);const t=(t,e)=>Math.max(0,Math.min(255,"source"===x.colorMode?t+R:"gray"===x.colorMode?255*S:e*S)),e=t(b,r[0]),i=t(w,r[1]),a=t(y,r[2]),o=1-Math.max(e,i,a)/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-a/255-o)/n,.5,.66],["#000000",o,.5,.5]];s.globalCompositeOperation="multiply";for(const[t,e,i,a]of h)s.fillStyle=t,s.beginPath(),s.arc(A+gt*i,F+pt*a,.48*gt*Math.sqrt(Math.max(0,e)),0,2*Math.PI),s.fill();s.globalCompositeOperation="source-over"}else if("voxel"===x.style){const t=A+gt/2,e=F+pt/2;s.beginPath(),s.moveTo(t,F),s.lineTo(A+gt,F+.25*pt),s.lineTo(t,e),s.lineTo(A,F+.25*pt),s.closePath(),s.fill(),s.globalAlpha=.72*I,s.beginPath(),s.moveTo(A,F+.25*pt),s.lineTo(t,e),s.lineTo(t,F+pt),s.lineTo(A,F+.75*pt),s.closePath(),s.fill(),s.globalAlpha=.42*I,s.beginPath(),s.moveTo(t,e),s.lineTo(A+gt,F+.25*pt),s.lineTo(A+gt,F+.75*pt),s.lineTo(t,F+pt),s.closePath(),s.fill()}else{const t="pixel"===x.style?0:Math.max(.5,.06*gt);s.fillRect(A,F,gt-t,pt-t),"lego"===x.style&&s.drawImage(p.canvas,A,F),"disco"===x.style&&(s.fillStyle=`rgba(255,255,255,${.1+.25*K(c,h)})`,s.fillRect(A,F,gt-t,.35*pt),s.fillStyle="rgba(0,0,0,.25)",s.fillRect(A,F+.65*pt,gt-t,.35*pt-t))}}}h.ctx.globalAlpha=1,(x.color.amount||x.lights.length)&&(lt(u.canvas,rt,ht),u.ctx.clearRect(0,0,rt,ht),u.ctx.drawImage(h.canvas,0,0)),x.color.amount&&(h.ctx.save(),h.ctx.globalCompositeOperation=x.color.blend,h.ctx.globalAlpha=x.color.amount,h.ctx.fillStyle=x.color.tint,h.ctx.fillRect(0,0,rt,ht),h.ctx.restore());for(const t of x.lights){const e=t.radius*Math.max(rt,ht),i=h.ctx.createRadialGradient(t.x*rt,t.y*ht,0,t.x*rt,t.y*ht,e);i.addColorStop(0,t.color),i.addColorStop(1,"transparent"),h.ctx.save(),h.ctx.globalCompositeOperation="screen",h.ctx.globalAlpha=t.intensity,h.ctx.fillStyle=i,h.ctx.fillRect(0,0,rt,ht),h.ctx.restore()}if((x.color.amount||x.lights.length)&&(h.ctx.globalCompositeOperation="destination-in",h.ctx.drawImage(u.canvas,0,0),h.ctx.globalCompositeOperation="source-over"),x.mask.enabled&&x.mask.shapes.length){const t=c.ctx;t.clearRect(0,0,rt,ht),t.fillStyle="#fff",t.strokeStyle="#fff",t.lineCap="round",t.lineJoin="round";for(const e of x.mask.shapes){if(t.beginPath(),"ellipse"===e.kind)t.ellipse((e.x+e.width/2)*rt,(e.y+e.height/2)*ht,e.width*rt/2,e.height*ht/2,0,0,2*Math.PI);else{if("rectangle"!==e.kind){if(t.lineWidth=(e.size??.05)*rt,1===e.points?.length){t.arc(e.points[0][0]*rt,e.points[0][1]*ht,t.lineWidth/2,0,2*Math.PI),t.fill();continue}e.points?.forEach(([e,i],a)=>a?t.lineTo(e*rt,i*ht):t.moveTo(e*rt,i*ht)),t.stroke();continue}t.rect(e.x*rt,e.y*ht,e.width*rt,e.height*ht)}t.fill()}h.ctx.globalCompositeOperation=x.mask.invert?"destination-out":"destination-in",h.ctx.drawImage(c.canvas,0,0),h.ctx.globalCompositeOperation="source-over"}const M=l.ctx;M.clearRect(0,0,rt,ht),M.globalAlpha=x.backdrop.opacity;const b=x.backdrop;if("solid"===b.mode&&(M.fillStyle=b.color,M.fillRect(0,0,rt,ht)),"gradient"===b.mode){const t=M.createLinearGradient(0,0,rt,ht);t.addColorStop(0,b.color),t.addColorStop(1,b.color2),M.fillStyle=t,M.fillRect(0,0,rt,ht)}if("source"===b.mode&&M.drawImage(s.canvas,0,0),"blurred"===b.mode){const t=Math.max(1,b.blur);lt(f.canvas,Math.max(2,Math.round(rt/t)),Math.max(2,Math.round(ht/t))),f.ctx.drawImage(s.canvas,0,0,f.canvas.width,f.canvas.height),M.imageSmoothingEnabled=!0,M.drawImage(f.canvas,0,0,rt,ht)}M.globalAlpha=1,x.effects.characterBloom>0&&(lt(g.canvas,Math.max(2,Math.round(rt/3)),Math.max(2,Math.round(ht/3))),g.ctx.clearRect(0,0,g.canvas.width,g.canvas.height),g.ctx.drawImage(h.canvas,0,0,g.canvas.width,g.canvas.height),M.save(),M.globalAlpha=.8*x.effects.characterBloom,M.globalCompositeOperation="screen",M.filter=`blur(${1+4*x.effects.characterBloom}px)`,M.drawImage(g.canvas,0,0,rt,ht),M.restore()),M.drawImage(h.canvas,0,0),S=wt?yt:""}const kt=Object.entries(x.effects).some(([t,e])=>!["angle","focus","blurType","characterBloom"].includes(t)&&"number"==typeof e&&e>0);a.clearRect(0,0,rt,ht),kt?(void 0===M&&(M=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()}}}()),M?a.drawImage(M.render(l.canvas,x.effects,i,it),0,0):a.drawImage(l.canvas,0,0),t.dataset.studioFinish=M?"gpu":"unavailable"):(a.drawImage(l.canvas,0,0),t.dataset.studioFinish="none")},setPixelRatio(t){it=Math.max(.1,Math.min(8,Number.isFinite(t)?t:1))},get pixelRatio(){return it},setBudget(t){et=Math.max(256,Math.min(tt,Number.isFinite(t)?Math.round(t):tt))},get maxCells(){return et},configure(t){x=R(t),W=B(x.color),V.configure(x.color.denoise),H.configure(x.warps,x.warpEdge),b++},pointer:(t,e,i)=>{X.configure(x.hover.effect,x.hover.strength,x.hover.radius,x.hover.edgeSafe),X.move(t,e,i)},leave(){X.leave()},get active(){return X.active},get settings(){return R(x)},get capabilities(){return{gpuFinish:null!==M}},invalidate(){y=""},destroy(){M?.destroy(),H.clear(),V.configure(0);for(const t of[s,r,h,l,c,f,m,u,g,p])t.canvas.width=t.canvas.height=1;C=null,E=new Uint8ClampedArray(0),F=new Uint8ClampedArray(0)}}}function mt(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 dt(t,e){if("string"==typeof t&&t.startsWith("blob:")){const i=await fetch(t,{signal:e}),a=await i.blob();return dt(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,v=null;const x=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,v=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&&v&&d.putImageData(v,0,0);const i=f[t];v=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 x(0),{source:m,width:h,height:l,duration:p,animated:!0,seek:async()=>{},frame:x,destroy(){m.width=u.width=1,f.length=0,v=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=mt(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=mt(o,"seeked",e);o.currentTime=i,await a},frame:()=>o,destroy:n}}catch(t){throw n(),t}}var ut=t=>{if(t?.aborted)throw new DOMException("Export cancelled","AbortError")};async function gt(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 l="png"===a||"jpeg"===a,c=i.time??0;if(!Number.isFinite(c)||c<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(!l&&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 f=document.createElement("canvas"),m=ft(f,R(e),{maxDimension:4096,maxCells:i.maxCells??12e3,pixelRatio:r/(i.referenceWidth??Math.min(r,960))});let d;try{if(ut(s),i.onProgress?.(0),l){if(m.render(await t.frame(c,s),c,r,o,i.motionTime),"unavailable"===f.dataset.studioFinish)throw new Error("Optical effects need WebGL. Disable these effects or export in a supported browser.");ut(s);const e=await new Promise((t,e)=>f.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:d}="function"==typeof a.GIFEncoder?a:a.default,u=l(),g=f.getContext("2d");for(let a=0;a<e;a++){ut(s);const l=a/n;if(m.invalidate(),m.render(await t.frame(l,s),l,r,o),"unavailable"===f.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),v=d(p,M);u.writeFrame(v,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:u,Mp4OutputFormat:g,WebMOutputFormat:p,BufferTarget:M,CanvasSource:v,canEncodeVideo:x}=await import("mediabunny"),b="mp4"===a?"avc":"vp9";if(!await x(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 u({format:"mp4"===a?new g({fastStart:"in-memory"}):new p,target:new M});f.width=r,f.height=o,d=()=>w.cancel();const y=new v(f,{codec:b,bitrate:Math.max(5e5,Math.round(r*o*n*.15))});w.addVideoTrack(y,{frameRate:n}),f.width=r,f.height=o,await w.start();for(let a=0;a<e;a++){ut(s);const l=a/n;if(m.invalidate(),m.render(await t.frame(l,s),l,r,o),"unavailable"===f.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(),ut(s),await w.finalize(),d=void 0,i.onProgress?.(1),new Blob([w.target.buffer],{type:"mp4"===a?"video/mp4":"video/webm"})}catch(t){throw await(d?.().catch(()=>{})),t}finally{m.destroy(),f.width=f.height=1}}async function pt(t,e,i={}){const a=await dt(e,i.signal);try{return Mt(t,a,i)}catch(t){throw a.destroy(),t}}function Mt(t,e,i={}){let a=R(i.settings),s=i.width??e.width,o=i.height??e.height;const n=ft(t,a,i),h=new r;let l=0,c=!1,f=!0,m=!1,d=!0,u=0,g=0,p=0,M=0,v=-1,x=!1!==i.adaptive,b=0,w=0,y=0;const k=matchMedia("(prefers-reduced-motion: reduce)"),S=e.source instanceof HTMLVideoElement?e.source:null,A=()=>{c||l||!d||document.hidden||(l=requestAnimationFrame(T))},E=()=>!m&&!k.matches&&d&&!document.hidden,F=()=>{S&&(S.loop=!0,E()?S.play().catch(()=>{m=!0,f=!0,A()}):S.pause()),p=0,f=!0,A()};function T(t){if(l=0,c||!d||document.hidden)return;const r=E();r&&(u+=p?Math.min(.1,(t-p)/1e3):0),p=t;const k=function(t){return j(t)||G(t)||t.effects.grain>0||t.effects.dust>0||t.effects.glitch>0}(a),F=r&&(e.animated||k),T=S?.getVideoPlaybackQuality?.().totalVideoFrames??-1,P=!S||T<0||T!==v;if(f||n.active||F&&(P||k)&&t-M>=1e3/Math.max(1,Math.min(60,i.fps??60))-.5){const r=performance.now();try{e.animated&&!S&&n.invalidate();const[t,l]=X(s,o,a);g=h.update(u,a.motion.type,a.motion.speed),n.render(e.frame(S?.currentTime??u),u,t,l,g);const c=performance.now()-r;i.onFrame?.(S?.currentTime??u,c),w=w?.9*w+.1*c:c,++b>20&&x&&(F||n.active)&&(y=w>13?y+1:0,y>=12&&n.maxCells>3e3&&(n.setBudget(Math.max(3e3,.8*n.maxCells)),i.onQualityChange?.(n.maxCells),y=0,w=0))}catch(t){m=!0,i.onError?.(t instanceof Error?t:new Error(String(t)))}f=!1,M=t,v=T}r&&(F||n.active)&&A()}const P=e=>{if(!E())return;const i=t.getBoundingClientRect();n.pointer((e.clientX-i.left)/i.width,(e.clientY-i.top)/i.height,e.timeStamp),A()},I=()=>{n.leave(),A()},C=new IntersectionObserver(([t])=>{d=t.isIntersecting,F()});C.observe(t),t.addEventListener("pointermove",P),t.addEventListener("pointerleave",I),t.addEventListener("pointercancel",I),document.addEventListener("visibilitychange",F),k.addEventListener("change",F);const L=()=>{c||(c=!0,cancelAnimationFrame(l),C.disconnect(),t.removeEventListener("pointermove",P),t.removeEventListener("pointerleave",I),t.removeEventListener("pointercancel",I),document.removeEventListener("visibilitychange",F),k.removeEventListener("change",F),i.signal?.removeEventListener("abort",L),n.destroy(),e.destroy())};if(i.signal?.addEventListener("abort",L,{once:!0}),i.signal?.aborted)throw L(),new DOMException("Cancelled","AbortError");return F(),{canvas:t,media:e,update(t){a=U(a,t),n.configure(a),f=!0,A()},resize(t,e,i){void 0!==i&&n.setPixelRatio(i),s=Math.max(2,t),o=Math.max(2,e),f=!0,A()},pause(t=!0){m=t,F()},async seek(t){u=Math.max(0,t),await e.seek(u),n.invalidate(),f=!0,A()},redraw(){n.invalidate(),f=!0,A()},get time(){return S?.currentTime??u},get settings(){return R(a)},get motionTime(){return g},get paused(){return m||k.matches},setAdaptive(t){x=t,b=y=w=0},setBudget(t){n.setBudget(t),i.onQualityChange?.(n.maxCells),f=!0,A()},get pixelRatio(){return n.pixelRatio},get maxCells(){return n.maxCells},get capabilities(){return n.capabilities},destroy:L}}var vt={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")},xt=Object.keys(vt);async function bt(t,e="Standard",i="#ffffff"){if(!Object.prototype.hasOwnProperty.call(vt,e))throw new Error("Unknown text font.");const{default:a}=await import("figlet/browser"),s=await vt[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 wt={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:" ⠁⠃⠇⡇⣇⣧⣷⣿"}},yt=wt;export{S as BLEND_MODES,wt as CHARACTER_SETS,E as DEFAULT_STUDIO_SETTINGS,k as DITHER_ALGORITHMS,p as MAX_STUDIO_WARPS,yt as STUDIO_CHARACTER_SETS,h as STUDIO_CURVE_CHANNELS,t as STUDIO_HOVERS,O as STUDIO_LOOKS,e as STUDIO_MOTIONS,A as STUDIO_PALETTES,y as STUDIO_STYLES,xt as STUDIO_TEXT_FONTS,g as STUDIO_WARPS,m as createStudioCurveLookup,ft as createStudioRenderer,bt as createStudioText,gt as exportStudio,dt as loadStudioMedia,pt as mountStudio,Mt as mountStudioMedia,c as normalizeStudioCurve,f as normalizeStudioCurves,R as normalizeStudioSettings,v as normalizeStudioWarps,L as parseStudioSettings,q as restyleStudio,C as serializeStudioSettings,N as studioCrop,X as studioDimensions,D as studioGrid,Y as studioLook,nt as studioSourceSize,U as updateStudioSettings};//# sourceMappingURL=studio.js.map
|
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
loadStudioMedia,
|
|
4
4
|
exportStudio,
|
|
5
5
|
normalizeStudioSettings,
|
|
6
|
-
updateStudioSettings,
|
|
7
6
|
serializeStudioSettings,
|
|
8
7
|
parseStudioSettings,
|
|
9
8
|
type StudioInput,
|
|
@@ -16,7 +15,7 @@ export async function createComposition(
|
|
|
16
15
|
source: File | string,
|
|
17
16
|
signal?: AbortSignal,
|
|
18
17
|
) {
|
|
19
|
-
|
|
18
|
+
const settings = normalizeStudioSettings({
|
|
20
19
|
style: 'ascii',
|
|
21
20
|
cellSize: 6,
|
|
22
21
|
ink: '#b3ed82',
|
|
@@ -27,7 +26,7 @@ export async function createComposition(
|
|
|
27
26
|
async function exportComposition(format: StudioExportFormat, cancellation?: AbortSignal) {
|
|
28
27
|
// Snapshot before loading another media instance. Export never seeks the
|
|
29
28
|
// live preview or picks up slider edits made while encoding.
|
|
30
|
-
const frozen =
|
|
29
|
+
const frozen = player.settings;
|
|
31
30
|
const time = player.time;
|
|
32
31
|
const motionTime = player.motionTime;
|
|
33
32
|
const referenceWidth = canvas.width / player.pixelRatio;
|
|
@@ -55,13 +54,11 @@ export async function createComposition(
|
|
|
55
54
|
return {
|
|
56
55
|
player,
|
|
57
56
|
update(patch: StudioInput) {
|
|
58
|
-
|
|
59
|
-
player.update(settings);
|
|
57
|
+
player.update(patch);
|
|
60
58
|
},
|
|
61
|
-
saveLook: () => serializeStudioSettings(settings),
|
|
59
|
+
saveLook: () => serializeStudioSettings(player.settings),
|
|
62
60
|
restoreLook(json: string) {
|
|
63
|
-
|
|
64
|
-
player.update(settings);
|
|
61
|
+
player.update(parseStudioSettings(json));
|
|
65
62
|
},
|
|
66
63
|
exportPng: (signal?: AbortSignal) => exportComposition('png', signal),
|
|
67
64
|
exportMp4: (signal?: AbortSignal) => exportComposition('mp4', signal),
|
package/package.json
CHANGED
|
@@ -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.
|
|
5
|
+
tested-engine: "4.7.0"
|
|
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.
|
|
11
|
+
Use the published `asciify-engine` npm package for browser canvas rendering. Tested against **4.7.0**. 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
|
|
@@ -149,3 +149,7 @@ Legacy `CHARSETS`, `ART_STYLE_PRESETS`, `CHARSET_SEQUENCES`, `LIVING_STYLE_PRESE
|
|
|
149
149
|
Studio adds `hex`, `led`, and `cmyk` render styles, directional/radial dither patterns, additional palettes, luminance mapping, RGB split, sharpening, and adjustable still-image motion strength. Read [studio-workspace.md](references/studio-workspace.md#fine-detail-and-controls-42) for exact options, density budgets and preview/export consistency. These are local Canvas/WebGL effects, not AI generation or a claim of parity with every competing catalog.
|
|
150
150
|
|
|
151
151
|
From 4.5, `dither.algorithm: 'blue-noise'` uses a precomputed dispersed threshold tile, separate from white `noise`. Studio `color` also accepts `blackPoint`, `whitePoint`, `gamma`, `shadows` and `highlights` to shape detail before conversion. All are neutral by default. Read [tonal detail and blue noise](references/studio-workspace.md#tonal-detail-and-blue-noise-45) before adding these controls; they belong to `/studio`, not core `AsciiOptions`.
|
|
152
|
+
|
|
153
|
+
From 4.6, Studio adds `color.curves` (RGB, red, green, blue) and optional edge-preserving `color.denoise`. `player.settings` returns a detached snapshot for saving or exporting after updates. Read [channel curves and source noise](references/studio-workspace.md#channel-curves-and-source-noise-46) for point limits, update semantics and the processing budget. Do not turn noise reduction on by default or apply it again to the already-rendered characters.
|
|
154
|
+
|
|
155
|
+
From 4.7, Studio supports up to eight ordered source warps: Twirl, Pinch, Spherize, Ripple, Zigzag, Shear, Smudge, Polar and Fragment. Build controls from `STUDIO_WARPS`, retain array order, and read [source shaping](references/source-shaping.md) for bounds, alpha/edge policy and cached-map performance. These shape the source; use `motion.type` to bring a still image to life and `hover.effect` for pointer interaction. Keep them independent, with an empty warp stack by default.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Ordered source shaping (4.7+)
|
|
2
|
+
|
|
3
|
+
Use `/studio` when a source needs local distortion before ASCII or dither conversion. These are static source transforms, independent of pointer hover and autonomous still-image motion. No AI service, external image upload or new animation clock is involved. Empty stacks are the default.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { mountStudio, STUDIO_WARPS, MAX_STUDIO_WARPS } from 'asciify-engine/studio';
|
|
7
|
+
|
|
8
|
+
const player = await mountStudio(canvas, '/photo.webp', {
|
|
9
|
+
settings: {
|
|
10
|
+
style: 'ascii', cellSize: 6,
|
|
11
|
+
warps: [
|
|
12
|
+
{ type: 'sphere', amount: .3, x: .5, y: .5, radius: .65 },
|
|
13
|
+
{ type: 'shear', amount: .12, angle: 25 },
|
|
14
|
+
],
|
|
15
|
+
warpEdge: 'clamp',
|
|
16
|
+
motion: { type: 'parallax', speed: .6, amount: .5 },
|
|
17
|
+
hover: { effect: 'elastic', radius: .4, strength: .5 },
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
// STUDIO_WARPS supplies value, label, description, direction and frequency.
|
|
21
|
+
// Keep add/reorder/disable controls bounded by MAX_STUDIO_WARPS (8).
|
|
22
|
+
player.update({ warps: [{ type: 'ripple', amount: .2, frequency: 6 }] });
|
|
23
|
+
const snapshot = player.settings; // persist / export this same state
|
|
24
|
+
player.update({ warps: [] }); // remove shaping without changing motion
|
|
25
|
+
// Call player.destroy() when its owning component unmounts.
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Catalog and controls
|
|
29
|
+
|
|
30
|
+
| Type | Mechanism | Extra controls |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| `twirl` | Continuous regional rotation, fading to a fixed boundary | — |
|
|
33
|
+
| `pinch` | Contract the center; negative strength expands | — |
|
|
34
|
+
| `sphere` | Rounded lens magnification; negative strength recedes | — |
|
|
35
|
+
| `ripple` | Smooth concentric displacement bands | `frequency` |
|
|
36
|
+
| `zigzag` | Angular twist bands with a triangular profile | `frequency` |
|
|
37
|
+
| `shear` | Regional slant along a chosen direction | `angle` |
|
|
38
|
+
| `smudge` | Pull a soft region along a chosen direction | `angle` |
|
|
39
|
+
| `polar` | Polar coordinate unwrap; negative strength wraps | `angle` |
|
|
40
|
+
| `fragment` | Stable hashed tile offsets; intentionally discontinuous | `angle`, `frequency` |
|
|
41
|
+
|
|
42
|
+
Every stage accepts `enabled` (default true), `amount` (−1…1, default .35), normalized center `x`/`y` (0…1, default .5) and `radius` (.05…2, default .55). Radius is a fraction of the **canvas's shorter side**, not a width percentage. This keeps circular regions circular in portrait and landscape output. `angle` is degrees −180…180, default 0. `frequency` is 1…24, default 5. Only expose angle/frequency when the catalog says that transform uses it. Zero strength and disabled stages preserve the source. Negative strength reverses the transform's direction but is not an exact inverse for every mechanism.
|
|
43
|
+
|
|
44
|
+
Stacks apply in array order. The engine composes inverse coordinates in reverse order, then resamples the source once, avoiding repeated raster blur. `player.update({warps:[...]})` replaces the entire array; it does not append or merge stages. Preserve the other entries when changing one. `normalizeStudioWarps` is available to control builders and returns independent normalized objects. At most eight valid stages are retained; unknown types are omitted, never aliased to another effect. Settings version remains 1. Older package versions ignore these fields, so upgrade npm and refresh the installed skill before using them.
|
|
45
|
+
|
|
46
|
+
`warpEdge:'clamp'` extends the nearest source edge when a coordinate leaves the source. `'transparent'` samples transparent virtual pixels, interpolating alpha correctly. Transparent areas reveal the selected backdrop; also select `backdrop.mode:'transparent'` for a cutout. Polar has a coordinate seam; Fragment has intentional tile boundaries. Do not describe either as an imperceptible photographic correction.
|
|
47
|
+
|
|
48
|
+
## Composition, export and performance
|
|
49
|
+
|
|
50
|
+
Processing order: crop/sample → source noise/grade/levels/channel curves → source warps → hover and still-image motion → selected render style/dither → masks, lights, backdrop and finish. Shaping applies to the converted source, not the independent source/blurred backdrop. All 18 styles share it. Save `player.settings` for JSON or independent export; do not recreate a different stack in the exporter. Animated exports include autonomous motion but do not record live pointer events. A warp stack by itself does not animate a static picture.
|
|
51
|
+
|
|
52
|
+
Static sources reuse the final shaped pixels during hover/motion. Video frames reuse the coordinate map and require one premultiplied-alpha bilinear sample per output sample; map construction occurs only when source-grid dimensions/aspect or warp controls change. Empty stacks add no pixel pass. A changed warp at a very dense source grid can be expensive; keep interactive preview `maxCells` bounded and let the user explicitly choose finer export density. Do not animate warp parameters every frame as a substitute for the existing motion system.
|
|
53
|
+
|
|
54
|
+
Measured CPU-only on an Apple M4 Pro / Node 25.9.0: an eight-stage map at 320×180 samples took 1.049 ms p95 per cached video-frame resample; editing it and rebuilding took 9.857 ms p95. At 960×540 the corresponding times were 8.909 and 72.833 ms. These numbers exclude decode, quantization, Canvas, GPU and display; they do not establish browser FPS or mobile performance. The default identity path preserves prior output; transformed pixels intentionally change. Moving dither now interpolates alpha with RGB to avoid stepped transparent edges or hidden-color halos.
|
|
@@ -210,3 +210,36 @@ const player = await mountStudio(canvas, '/portrait.jpg', {
|
|
|
210
210
|
// Use the same player.settings for live embedding and independent export.
|
|
211
211
|
// On unmount: player.destroy().
|
|
212
212
|
```
|
|
213
|
+
|
|
214
|
+
## Channel curves and source noise (4.6)
|
|
215
|
+
|
|
216
|
+
`color.curves` accepts `rgb`, `red`, `green` and `blue` arrays of `[input, output]` points in 0–1. Each curve holds up to 16 points including input endpoints 0 and 1. Missing endpoints use the identity; coordinates are clamped, input positions quantize to 1/255, repeated inputs retain the last valid point and points are sorted. Output points may reverse direction for an intentional inverted/solarized treatment. Shape-preserving cubic interpolation avoids overshoot between points. `createStudioCurveLookup(points)` returns the same 256 samples used by the renderer for a custom graph; `normalizeStudioCurve` and `STUDIO_CURVE_CHANNELS` are public helpers.
|
|
217
|
+
|
|
218
|
+
Incremental `player.update({color:{curves:{red: [...]}}})` preserves the other channels. Each supplied channel array replaces that channel. Reset a channel with `[[0,0],[1,1]]`; reset all with `normalizeStudioCurves({})`. Keep input ordering and show precise values in a curve editor; do not call an arbitrary point graph a fixed shadow/highlight control.
|
|
219
|
+
|
|
220
|
+
`color.denoise` is 0–1, default 0. It performs a bounded 3×3 edge-preserving filter on sampled source pixels, preserving alpha and excluding hidden transparent colors. Use it for small source noise; strong settings can remove intentional texture and do not recover clipped or missing detail. It is not generative restoration or temporal video denoising.
|
|
221
|
+
|
|
222
|
+
Processing order: crop/sample → reduce noise → brightness/contrast/saturation/grayscale → levels/gamma/shadow/highlight tone → RGB curve → individual channel curves → source warps (4.7+) → hover/motion → style/dither → composition/finish. Use `colorMode:'source'` to retain the resulting colors; accent and gray modes use the adjusted source tones for their own ink mapping. The backdrop remains its existing source/background pipeline.
|
|
223
|
+
|
|
224
|
+
Curves compile to three 256-entry tables only when configured. Noise reduction uses one bounded source copy; static images reuse their processed pixels during motion/hover. Video must process changed source frames, so dense one-pixel sampling adds CPU cost. Start with the default 12,000-cell budget or measure a larger budget on the target device. Neutral settings skip both processing paths; root/core/hover entry points do not load them.
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
player.update({
|
|
228
|
+
color: {
|
|
229
|
+
denoise: .25,
|
|
230
|
+
curves: {
|
|
231
|
+
rgb: [[0,0], [.25,.2], [.75,.8], [1,1]],
|
|
232
|
+
blue: [[0,.04], [1,.95]],
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
const saved = serializeStudioSettings(player.settings);
|
|
237
|
+
// For export, snapshot player.settings once before async media loading.
|
|
238
|
+
// The independent export renderer applies the same source controls.
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
`player.settings` is an owned snapshot, not a live mutable reference. Use `player.update()` to edit. Reuse it for JSON, independent PNG/video export and restoring a live component. Check installed version before using this getter (4.6+).
|
|
242
|
+
|
|
243
|
+
## Ordered source shaping (4.7)
|
|
244
|
+
|
|
245
|
+
`warps` stores up to eight optional transforms applied in array order before all styles. `warpEdge` selects extended or transparent edges. Read [source shaping](source-shaping.md) for the nine mechanisms, controls, normalization, editor construction and dense-video cost. This is separate from art-style selection, pointer response and autonomous motion. The default stack is empty.
|