asciify-engine 4.12.0 → 4.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -663,4 +663,5 @@ Also in 4.12:
663
663
 
664
664
  - **Even quality across styles.** Character styles stretch each image's tones to its own range and pick glyphs by measured ink, so dark or flat images keep their subject in every character set and look. Set `color.levels: 'none'` for the previous linear mapping. Adaptive resolution only steps down while frames are genuinely over budget, and returns to full detail when motion settles.
665
665
  - **Faster fields.** Image sources decode off the main thread and are scaled to what the cells can show; scene maths runs once per cell; shaders compile without blocking. `ready` resolves for fields mounted off screen, and a shader link failure is reported through `onError`.
666
- - **21 ready-made components in the agent skill** ([components.md](skills/asciify-engine/references/components.md)): interface pieces, backgrounds, text, transitions, pages and AI states, each with the prompt and the reference code. See them running at [asciify.org/components](https://asciify.org/components).
666
+ - **4.12.1:** fields size themselves from layout, so a parent's transform (a dialog scaling open) no longer stretches them.
667
+ - **29 ready-made components in the agent skill** ([components.md](skills/asciify-engine/references/components.md)): controls, feedback, AI states, transitions, data, media, text and pages, each with the prompt and the reference code. See them running at [asciify.org/components](https://asciify.org/components).
package/dist/field.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var e=e=>e?"\nconst float VSCALE=6.;\nvec4 decodeFluid(vec4 t){return vec4((t.xy*2.-1.)*VSCALE,t.z,t.w);}\nvec4 encodeFluid(vec4 v){return vec4(clamp(v.xy/VSCALE*.5+.5,0.,1.),clamp(v.z,0.,1.),1.);}":"\nvec4 decodeFluid(vec4 t){return t;}\nvec4 encodeFluid(vec4 v){return vec4(v.xyz,1.);}",t="#version 300 es\nprecision highp float;\nprecision highp sampler2D;\nuniform vec2 uTexel;\nout vec4 outColor;\nvec2 here(){return gl_FragCoord.xy*uTexel;}\n",n=t+"\nuniform sampler2D uVelocity;\nuniform vec2 uFrom, uTo, uForce;\nuniform float uAspect, uRadius;\nfloat segment(vec2 p,vec2 a,vec2 b){vec2 pa=p-a,ba=b-a;float h=clamp(dot(pa,ba)/max(dot(ba,ba),1e-8),0.,1.);return length(pa-ba*h);}\nvoid main(){\n vec2 uv=here(),scale=vec2(uAspect,1.);\n float d=segment(uv*scale,uFrom*scale,uTo*scale);\n outColor=vec4(texture(uVelocity,uv).xy+uForce*exp(-d*d/(uRadius*uRadius)),0.,1.);\n}",r=t+"\nuniform sampler2D uVelocity;\nvoid main(){\n vec2 uv=here();\n float l=texture(uVelocity,uv-vec2(uTexel.x,0.)).y,r=texture(uVelocity,uv+vec2(uTexel.x,0.)).y;\n float t=texture(uVelocity,uv+vec2(0.,uTexel.y)).x,b=texture(uVelocity,uv-vec2(0.,uTexel.y)).x;\n outColor=vec4(.5*(r-l-t+b),0.,0.,1.);\n}",i=t+"\nuniform sampler2D uVelocity, uCurl;\nuniform float uStrength, uDt;\nvoid main(){\n vec2 uv=here();\n float l=texture(uCurl,uv-vec2(uTexel.x,0.)).x,r=texture(uCurl,uv+vec2(uTexel.x,0.)).x;\n float t=texture(uCurl,uv+vec2(0.,uTexel.y)).x,b=texture(uCurl,uv-vec2(0.,uTexel.y)).x,c=texture(uCurl,uv).x;\n vec2 force=.5*vec2(abs(t)-abs(b),abs(r)-abs(l));\n force=force/(length(force)+1e-4)*uStrength*c;\n force.y=-force.y;\n outColor=vec4(clamp(texture(uVelocity,uv).xy+force*uDt,-1000.,1000.),0.,1.);\n}",o=t+"\nuniform sampler2D uVelocity;\nvoid main(){\n vec2 uv=here(),c=texture(uVelocity,uv).xy;\n float l=texture(uVelocity,uv-vec2(uTexel.x,0.)).x,r=texture(uVelocity,uv+vec2(uTexel.x,0.)).x;\n float t=texture(uVelocity,uv+vec2(0.,uTexel.y)).y,b=texture(uVelocity,uv-vec2(0.,uTexel.y)).y;\n if(uv.x-uTexel.x<0.)l=-c.x;\n if(uv.x+uTexel.x>1.)r=-c.x;\n if(uv.y+uTexel.y>1.)t=-c.y;\n if(uv.y-uTexel.y<0.)b=-c.y;\n outColor=vec4(.5*(r-l+t-b),0.,0.,1.);\n}",a=t+"\nuniform sampler2D uSource;\nuniform float uAmount;\nvoid main(){outColor=vec4(texture(uSource,here()).x*uAmount,0.,0.,1.);}",l=t+"\nuniform sampler2D uPressure, uDivergence;\nvoid main(){\n vec2 uv=here();\n float l=texture(uPressure,uv-vec2(uTexel.x,0.)).x,r=texture(uPressure,uv+vec2(uTexel.x,0.)).x;\n float t=texture(uPressure,uv+vec2(0.,uTexel.y)).x,b=texture(uPressure,uv-vec2(0.,uTexel.y)).x;\n outColor=vec4((l+r+b+t-texture(uDivergence,uv).x)*.25,0.,0.,1.);\n}",u=t+"\nuniform sampler2D uPressure, uVelocity;\nvoid main(){\n vec2 uv=here();\n float l=texture(uPressure,uv-vec2(uTexel.x,0.)).x,r=texture(uPressure,uv+vec2(uTexel.x,0.)).x;\n float t=texture(uPressure,uv+vec2(0.,uTexel.y)).x,b=texture(uPressure,uv-vec2(0.,uTexel.y)).x;\n outColor=vec4(texture(uVelocity,uv).xy-vec2(r-l,t-b),0.,1.);\n}",s=t+"\nuniform sampler2D uVelocity;\nuniform float uDt, uDissipation;\nvoid main(){\n vec2 uv=here();\n vec2 back=uv-uDt*texture(uVelocity,uv).xy*uTexel;\n outColor=vec4(texture(uVelocity,back).xy/(1.+uDissipation*uDt),0.,1.);\n}";var c=class{constructor(e,t){this.previous=null,this.peak=0,this.width=e,this.height=t;const n=e*t;this.pixels=new Uint8Array(4*n),this.signal=new Float32Array(n),this.offsetX=new Float32Array(n),this.offsetY=new Float32Array(n),this.driftX=new Float32Array(n),this.driftY=new Float32Array(n),this.u=new Float32Array(n),this.v=new Float32Array(n),this.ink=new Float32Array(n),this.a=new Float32Array(n),this.b=new Float32Array(n),this.c=new Float32Array(n),this.pressure=new Float32Array(n),this.pressureNext=new Float32Array(n),this.divergence=new Float32Array(n),this.curl=new Float32Array(n)}get active(){return this.peak>2e-4}clear(){for(const e 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])e.fill(0);this.previous=null,this.peak=0}leave(){this.previous=null}move(e,t,n,r=performance.now()){if(!Number.isFinite(e+t+n+r))return;e=Math.max(0,Math.min(1,e)),t=Math.max(0,Math.min(1,t)),n=Math.max(.1,Math.min(1,n));const i=this.previous;if(this.previous={x:e,y:t,time:r},!i)return;const o=(e-i.x)*(this.width-1),a=(t-i.y)*(this.height-1),l=Math.hypot(o,a);if(l<.001)return;const u=Math.min(this.width,this.height),s=r>i.time?Math.max(1/240,(r-i.time)/1e3):1/60,c=Math.min(12,l/u/s),f=1-Math.exp(-c/2.2),h=u*(.022+.075*n)*(.65+.95*f),d=Math.min(128,Math.max(1,Math.ceil(l/(.4*h)))),m=l/h*(.32+.22*f)/d,v=o/l,g=a/l,p=-g,x=v,E=l*(2+18*f)/d;for(let n=0;n<d;n++){const r=(n+.5)/d,o=(i.x+(e-i.x)*r)*(this.width-1),a=(i.y+(t-i.y)*r)*(this.height-1),l=Math.max(1,Math.floor(o-3*h)),u=Math.min(this.width-2,Math.ceil(o+3*h)),s=Math.max(1,Math.floor(a-3*h)),c=Math.min(this.height-2,Math.ceil(a+3*h));for(let e=s;e<=c;e++)for(let t=l;t<=u;t++){const n=((t-o)*v+(e-a)*g)/h,r=((t-o)*p+(e-a)*x)/h,i=Math.exp(-n*n*.65-r*r*1.7),l=e*this.width+t;this.ink[l]=1-(1-this.ink[l])*Math.exp(-i*m);const u=1-r*r*.55,s=n*r*.7;this.u[l]=Math.max(-80,Math.min(80,this.u[l]+(v*u+p*s)*i*E)),this.v[l]=Math.max(-80,Math.min(80,this.v[l]+(g*u+x*s)*i*E))}}this.peak=1}read(e,t,n){const r=0|(t=Math.max(.5,Math.min(this.width-1.5,t))),i=0|(n=Math.max(.5,Math.min(this.height-1.5,n))),o=t-r,a=n-i,l=i*this.width+r;return(e[l]*(1-o)+e[l+1]*o)*(1-a)+(e[l+this.width]*(1-o)+e[l+this.width+1]*o)*a}sample(e,t){return this.read(this.signal,e*(this.width-1),t*(this.height-1))}displacement(e,t,n){n[0]=this.read(this.offsetX,e*(this.width-1),t*(this.height-1)),n[1]=this.read(this.offsetY,e*(this.width-1),t*(this.height-1))}step(e){if(!this.active||!Number.isFinite(e))return;let t=Math.max(0,Math.min(.05,e));const n=this.width,r=this.height;for(;t>1e-7;){const e=Math.min(1/60,t);t-=e;const i=Math.exp(-1.9*e),o=Math.exp(-3.2*e);for(let t=1;t<r-1;t++)for(let a=1;a<n-1;a++){const l=t*n+a,u=Math.max(.5,Math.min(n-1.5,a-this.u[l]*e)),s=Math.max(.5,Math.min(r-1.5,t-this.v[l]*e)),c=0|u,f=0|s,h=u-c,d=s-f,m=f*n+c,v=(1-h)*(1-d),g=h*(1-d),p=(1-h)*d,x=h*d;this.a[l]=(this.u[m]*v+this.u[m+1]*g+this.u[m+n]*p+this.u[m+n+1]*x)*i,this.b[l]=(this.v[m]*v+this.v[m+1]*g+this.v[m+n]*p+this.v[m+n+1]*x)*i,this.c[l]=(this.ink[m]*v+this.ink[m+1]*g+this.ink[m+n]*p+this.ink[m+n+1]*x)*o}[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 e=1;e<r-1;e++)for(let t=1;t<n-1;t++){const r=e*n+t;this.curl[r]=.5*(this.v[r+1]-this.v[r-1]-this.u[r+n]+this.u[r-n])}for(let t=1;t<r-1;t++)for(let r=1;r<n-1;r++){const i=t*n+r,o=Math.abs(this.curl[i+1])-Math.abs(this.curl[i-1]),a=Math.abs(this.curl[i+n])-Math.abs(this.curl[i-n]),l=Math.hypot(o,a)+1e-4,u=this.curl[i]*e*10,s=Math.hypot(this.u[i],this.v[i]);this.u[i]+=a/l*u,this.v[i]-=o/l*u;const c=Math.min(1,s/(Math.hypot(this.u[i],this.v[i])+1e-6));this.u[i]*=c,this.v[i]*=c,this.divergence[i]=-.5*(this.u[i+1]-this.u[i-1]+this.v[i+n]-this.v[i-n])}this.pressure.fill(0);for(let e=0;e<4;e++){for(let e=1;e<r-1;e++)for(let t=1;t<n-1;t++){const r=e*n+t;this.pressureNext[r]=.25*(this.divergence[r]+this.pressure[r-1]+this.pressure[r+1]+this.pressure[r-n]+this.pressure[r+n])}[this.pressure,this.pressureNext]=[this.pressureNext,this.pressure]}let a=0;for(let t=1;t<r-1;t++)for(let r=1;r<n-1;r++){const i=t*n+r;this.u[i]=Math.max(-80,Math.min(80,this.u[i]-.5*(this.pressure[i+1]-this.pressure[i-1]))),this.v[i]=Math.max(-80,Math.min(80,this.v[i]-.5*(this.pressure[i+n]-this.pressure[i-n]))),this.driftX[i]+=(3.6*this.u[i]-58*this.offsetX[i]-12*this.driftX[i])*e,this.driftY[i]+=(3.6*this.v[i]-58*this.offsetY[i]-12*this.driftY[i])*e,this.offsetX[i]=Math.max(-2,Math.min(2,this.offsetX[i]+this.driftX[i]*e)),this.offsetY[i]=Math.max(-2,Math.min(2,this.offsetY[i]+this.driftY[i]*e)),this.signal[i]=1-Math.exp(.06*-Math.hypot(this.u[i],this.v[i])-.16*this.ink[i]),a=Math.max(a,this.signal[i])}this.peak=a}if(this.active)for(let e=0;e<this.ink.length;e++){const t=Math.round(65535*Math.min(1,this.signal[e]));this.pixels[4*e]=t>>>8,this.pixels[4*e+1]=255&t,this.pixels[4*e+2]=Math.round(128+63.5*this.offsetX[e]),this.pixels[4*e+3]=Math.round(128+63.5*this.offsetY[e])}else this.clear()}},f={radius:.13,strength:.5,trail:.45,follow:12},h={strength:.55,radius:.45},d={radius:.075,force:1,dissipation:1.6,refraction:.03,glow:.55,resolution:128,curl:24},m={charset:" .,:-=+*#%@",cellSize:12,cellAspect:1,font:'ui-monospace, "SF Mono", Menlo, Consolas, monospace',fontWeight:500,glyphScale:.8,mark:"glyph",dotScale:.9,color:"ink",ink:"#f3ba32",highlight:"auto",background:"transparent",brightness:0,contrast:1,gamma:1,invert:!1,levels:"auto",saturation:1.1,shade:.5,backdrop:0,fit:"cover",focus:{x:.5,y:.5},vignette:0,drift:0,scene:"flow",scale:1,clear:!1,speed:1,hover:"fluid",fluid:d,lantern:f,trail:h,maxPixelRatio:2,respectReducedMotion:!0},v=(e,t,n,r)=>"number"==typeof e&&Number.isFinite(e)?Math.min(n,Math.max(t,e)):r,g=(e,t)=>"string"==typeof e&&/^#([\da-f]{3}|[\da-f]{6})$/i.test(e.trim())?e.trim().toLowerCase():t;function p(e){const t=Intl.Segmenter;return(t?Array.from(new t(void 0,{granularity:"grapheme"}).segment(e),e=>e.segment):Array.from(e)).filter(e=>"\n"!==e&&"\r"!==e&&"\t"!==e).slice(0,256)}function x(e={},t=m){const n="string"==typeof e.charset&&p(e.charset).length>=2?e.charset:t.charset,r=t.fluid||d,i=void 0===e.fluid?t.fluid:e.fluid,o=!1!==i&&(()=>{const e=!0!==i&&i?{...r,...i}:r;return{radius:v(e.radius,.005,.5,d.radius),force:v(e.force,0,8,d.force),dissipation:v(e.dissipation,.05,20,d.dissipation),refraction:v(e.refraction,0,.5,d.refraction),glow:v(e.glow,0,2,d.glow),resolution:Math.round(v(e.resolution,16,512,d.resolution)),curl:v(e.curl,0,80,d.curl)}})(),a="none"===e.scene||"flow"===e.scene||"orb"===e.scene||"drift"===e.scene?e.scene:t.scene,l=!!e.clear&&"object"==typeof e.clear&&("element"in e.clear||"nodeType"in e.clear),u=void 0===e.clear||l?t.clear:e.clear,s=!!u&&{x:v(u.x,-.5,1.5,.25),y:v(u.y,-.5,1.5,.3),width:v(u.width,0,2,.5),height:v(u.height,0,2,.4),feather:v(u.feather,.005,1,.12),shape:"rect"===u.shape?"rect":"ellipse"};return{charset:n,cellSize:v(e.cellSize,3,96,t.cellSize),cellAspect:v(e.cellAspect,.3,2,t.cellAspect),font:"string"==typeof e.font&&e.font.trim()?e.font:t.font,fontWeight:Math.round(v(e.fontWeight,100,900,t.fontWeight)),glyphScale:v(e.glyphScale,.3,1.6,t.glyphScale),mark:"dot"===e.mark||"glyph"===e.mark?e.mark:t.mark,dotScale:v(e.dotScale,.1,1.4,t.dotScale),color:"source"===e.color||"ink"===e.color?e.color:t.color,ink:g(e.ink,t.ink),highlight:"auto"===e.highlight?"auto":g(e.highlight,t.highlight),background:"transparent"===e.background?"transparent":g(e.background,t.background),brightness:v(e.brightness,-1,1,t.brightness),contrast:v(e.contrast,0,4,t.contrast),gamma:v(e.gamma,.2,5,t.gamma),invert:"boolean"==typeof e.invert?e.invert:t.invert,levels:"none"===e.levels||"auto"===e.levels?e.levels:t.levels,saturation:v(e.saturation,0,3,t.saturation),shade:v(e.shade,0,1,t.shade),backdrop:v(e.backdrop,0,1,t.backdrop),fit:"contain"===e.fit||"cover"===e.fit?e.fit:t.fit,focus:{x:v(e.focus?.x,0,1,t.focus.x),y:v(e.focus?.y,0,1,t.focus.y)},vignette:v(e.vignette,0,1,t.vignette),drift:v(e.drift,0,2,t.drift),scene:a,scale:v(e.scale,.2,6,t.scale),clear:s,speed:v(e.speed,0,5,t.speed),hover:"fluid"===e.hover||"lantern"===e.hover||"trail"===e.hover||"none"===e.hover?e.hover:t.hover,fluid:o,lantern:{radius:v(e.lantern?.radius,.02,.6,t.lantern.radius),strength:v(e.lantern?.strength,0,1.5,t.lantern.strength),trail:v(e.lantern?.trail,0,3,t.lantern.trail),follow:v(e.lantern?.follow,1,60,t.lantern.follow)},trail:{strength:v(e.trail?.strength,0,1,t.trail.strength),radius:v(e.trail?.radius,.1,1,t.trail.radius)},maxPixelRatio:v(e.maxPixelRatio,.5,4,t.maxPixelRatio),respectReducedMotion:"boolean"==typeof e.respectReducedMotion?e.respectReducedMotion:t.respectReducedMotion}}function E(e,t,n,r,i,o={x:.5,y:.5}){if(!(e>0&&t>0&&n>0&&r>0))return{scale:[1,1],offset:[0,0]};const a=e/t,l=n/r,u=l>a,s="cover"===i?u?[a/l,1]:[1,l/a]:u?[1,l/a]:[a/l,1];return{scale:s,offset:"cover"===i?[(1-s[0])*o.x,(1-s[1])*o.y]:[.5*(1-s[0]),.5*(1-s[1])]}}function T(e,t,n,r){const i=Math.max(2,Math.round(e*n)),o=Math.max(2,Math.round(i*t)),a=Math.min(16,Math.max(1,r)),l=Math.ceil(r/a);return{width:o,height:i,columns:a,rows:l,atlasWidth:a*o,atlasHeight:l*i}}function y(e){const t=e.replace("#",""),n=3===t.length?t.split("").map(e=>e+e).join(""):t,r=Number.parseInt(n,16);return Number.isFinite(r)&&6===n.length?[(r>>16&255)/255,(r>>8&255)/255,(255&r)/255]:[0,0,0]}function b(e){const t=new Uint32Array(256);let n=0;for(let r=0;r+3<e.length;r+=4)e[r+3]<8||(t[Math.round(.2126*e[r]+.7152*e[r+1]+.0722*e[r+2])]++,n++);if(!n)return[0,1];const r=e=>{let r=0;for(let i=0;i<256;i++)if(r+=t[i],r>=n*e)return i/255;return 1},i=r(.02),o=r(.98);return o-i<.08?[0,1]:[i,o]}var w=e=>"fluid"!==e.hover||e.fluid?e.hover:"none";function M(e,t,n=24,r=.12,i="ellipse"){return e.width>0&&e.height>0&&{x:(t.left-n-e.left)/e.width,y:(t.top-n-e.top)/e.height,width:(t.width+2*n)/e.width,height:(t.height+2*n)/e.height,feather:r,shape:i}}var R,F=/\.(mp4|webm|mov|m4v|ogv)(\?|#|$)/i,A=/swiftshader|llvmpipe|softpipe|software|basic render/i;function L(){if(R)return R;if("undefined"==typeof document||void 0===globalThis.WebGL2RenderingContext)return R="none";try{const e=document.createElement("canvas").getContext("webgl2");if(!e)return R="none";const t=e.getExtension("WEBGL_debug_renderer_info"),n=String(e.getParameter(t?t.UNMASKED_RENDERER_WEBGL:e.RENDERER)??"");return e.getExtension("WEBGL_lose_context")?.loseContext(),R=A.test(n)?"software":"gpu"}catch{return R="none"}}exports.DEFAULT_FIELD_FLUID=d,exports.DEFAULT_FIELD_LANTERN=f,exports.DEFAULT_FIELD_SETTINGS=m,exports.DEFAULT_FIELD_TRAIL=h,exports.FIELD_HOVERS=[{value:"fluid",label:"Fluid",description:"An ink-in-water wake that bends the image and swirls the characters."},{value:"lantern",label:"Lantern",description:"A soft light follows the pointer and lifts the characters it passes, without distortion."},{value:"trail",label:"Trail",description:"The Asciify Trail: in the wake, sparse characters fill in and dense ones open up, then settle. No distortion."},{value:"none",label:"Off",description:"The field stays still under the pointer."}],exports.activeFieldHover=w,exports.fieldCellLayout=T,exports.fieldClearFromRects=M,exports.fieldGlyphs=p,exports.fieldLevels=b,exports.fieldSourceRect=E,exports.glyphFieldTier=L,exports.isGlyphFieldSupported=function(e={}){const t=L();return"gpu"===t||"software"===t&&!!e.allowSoftware},exports.mountGlyphField=function(t,f={}){const h=e=>"undefined"!=typeof HTMLElement&&e instanceof HTMLElement,d=e=>{const t=e.clear;return h(t)?{element:{element:t},rest:{...e,clear:void 0}}:t&&"object"==typeof t&&h(t.element)?{element:t,rest:{...e,clear:void 0}}:{element:null,rest:e}};let m=d(f).element,v=x(d(f).rest);const g=f.onError,R=t.getContext("webgl2",{alpha:!0,antialias:!1,depth:!1,stencil:!1,premultipliedAlpha:!0,powerPreference:"high-performance"});if(!R)throw new Error("WebGL2 is not available for the glyph field.");const A=R;t.setAttribute("role","img"),t.setAttribute("aria-label",f.label??"ASCII artwork"),t.dataset.renderer="glyph-field";let L=!1;const _=A.getExtension("KHR_parallel_shader_compile");let S,D,k=null,C=null,U=null,P=null,B=[],I=[],X=null,N=!1,V=null,G=null;const H=[];let z=0,O=0,W=1,Y=0,q=T(v.cellSize,v.cellAspect,1,2),$=2,j=0,K=0,J=0,Q=null,Z=null,ee=0,te=0,ne=0,re=null,ie=0,oe=!0,ae=0,le=[0,1],ue=-1/0,se=0,ce=0,fe=0,he=!0,de=!1,me=!1,ve=!1,ge=!1,pe=!0,xe=null,Ee=null,Te=1/60,ye=0,be=0,we=[];const Me=new Float32Array(16),Re=()=>{if(!m)return;const{element:e,padding:n=32,feather:r=.16,shape:i="rect"}=m,o=M(t.getBoundingClientRect(),e.getBoundingClientRect(),n,r,i),a=v.clear;JSON.stringify(a)!==JSON.stringify(o)&&(v={...v,clear:o},he=!0)},Fe={target:null,at:null,strength:0,trail:[]},Ae=new Float32Array(36);let Le=null,_e=null,Se=!1;const De=()=>{if(!z||!O)return null;const e=z/O,t=Math.round(e>=1?128:128*e),n=Math.round(e>=1?128/e:128);return Le&&Le.width===t&&Le.height===n||(Le=new c(t,n),_e||(_e=ze(A.LINEAR)),A.bindTexture(A.TEXTURE_2D,_e),A.texImage2D(A.TEXTURE_2D,0,A.RGBA8,t,n,0,A.RGBA,A.UNSIGNED_BYTE,Le.pixels),Se=!0),Le};let ke=0;const Ce="function"==typeof matchMedia?matchMedia("(prefers-reduced-motion: reduce)"):null,Ue=()=>v.respectReducedMotion&&!!Ce?.matches;let Pe;const Be=new Promise(e=>{Pe=e});let Ie=!0;const Xe=e=>g?.(e instanceof Error?e:new Error(String(e))),Ne=(e,t)=>{const n=A.createShader(e);return H.push(n),A.shaderSource(n,t),A.compileShader(n),n},Ve=e=>{G??(G=Ne(A.VERTEX_SHADER,"#version 300 es\nvoid main(){vec2 p=vec2((gl_VertexID<<1)&2,gl_VertexID&2);gl_Position=vec4(p*2.-1.,0.,1.);}"));const t=A.createProgram(),n=Ne(A.FRAGMENT_SHADER,e);return A.attachShader(t,G),A.attachShader(t,n),A.linkProgram(t),{program:t,locations:null,fragment:n}},Ge=(e,t=!1)=>{if(e.locations)return!0;const{program:n,fragment:r}=e;if(!t&&_&&!A.getProgramParameter(n,_.COMPLETION_STATUS_KHR))return!1;if(!A.getProgramParameter(n,A.LINK_STATUS)){if(A.isContextLost())return!1;throw new Error(A.getShaderInfoLog(r)||A.getProgramInfoLog(n)||"Glyph field program failed to link.")}const i={};for(let e=A.getProgramParameter(n,A.ACTIVE_UNIFORMS)-1;e>=0;e--){const t=A.getActiveUniform(n,e).name.replace(/\[0\]$/,"");i[t]=A.getUniformLocation(n,t)}return e.locations=i,!0},He=(e=!1)=>!!k&&!!C&&Ge(k,e)&&Ge(C,e),ze=e=>{const t=A.createTexture();return A.bindTexture(A.TEXTURE_2D,t),A.texParameteri(A.TEXTURE_2D,A.TEXTURE_MIN_FILTER,e),A.texParameteri(A.TEXTURE_2D,A.TEXTURE_MAG_FILTER,e===A.LINEAR_MIPMAP_LINEAR?A.LINEAR:e),A.texParameteri(A.TEXTURE_2D,A.TEXTURE_WRAP_S,A.CLAMP_TO_EDGE),A.texParameteri(A.TEXTURE_2D,A.TEXTURE_WRAP_T,A.CLAMP_TO_EDGE),t},Oe=()=>{P&&(A.deleteTexture(P.ink),A.deleteTexture(P.under),A.deleteFramebuffer(P.buffer),P=null)},We=()=>!!v.fluid&&"fluid"===w(v),Ye=()=>{X?.release(),N=!1,B.forEach(e=>A.deleteTexture(e)),I.forEach(e=>A.deleteFramebuffer(e)),B=[],I=[]};function qe(){U&&A.deleteProgram(U.program),U=Ve((t=>`#version 300 es\nprecision highp float;\nprecision highp sampler2D;\nuniform sampler2D uPrevious;\nuniform vec2 uSize;\nuniform float uAspect, uDt, uDecay, uEnergyDecay, uRadius, uSplat;\nuniform vec2 uFrom, uTo, uForce;\nout vec4 outColor;\n${e(t)}\nfloat segment(vec2 p,vec2 a,vec2 b){vec2 pa=p-a,ba=b-a;float h=clamp(dot(pa,ba)/max(dot(ba,ba),1e-8),0.,1.);return length(pa-ba*h);}\nvoid main(){\n vec2 uv=gl_FragCoord.xy/uSize;\n vec4 here=decodeFluid(texture(uPrevious,uv));\n vec4 state=decodeFluid(texture(uPrevious,uv-uDt*here.xy*vec2(1./uAspect,1.)));\n state.xy*=exp(-uDt*uDecay);\n state.z*=exp(-uDt*uEnergyDecay);\n if(uSplat>.5){\n vec2 scale=vec2(uAspect,1.);\n float d=segment(uv*scale,uFrom*scale,uTo*scale);\n float g=exp(-d*d/(uRadius*uRadius));\n state.xy+=uForce*g;\n state.z=min(1.,state.z+g*min(1.,length(uForce)*.5+.08));\n }\n float speed=length(state.xy);\n if(speed>5.)state.xy*=5./speed;\n outColor=encodeFluid(state);\n}`)(L)),X?.dispose(),X=L?null:function(e,t,c){const f={splat:t(n),curl:t(r),vorticity:t(i),divergence:t(o),fade:t(a),pressure:t(l),gradient:t(u),advect:t(s)},h=Object.values(f);let d=0,m=0,v=[],g=[],p=null,x=null;const E=t=>{const n=e.createTexture();e.bindTexture(e.TEXTURE_2D,n),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,t),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,t),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),e.texImage2D(e.TEXTURE_2D,0,e.RGBA16F,d,m,0,e.RGBA,e.HALF_FLOAT,null);const r=e.createFramebuffer();return e.bindFramebuffer(e.FRAMEBUFFER,r),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,n,0),e.checkFramebufferStatus(e.FRAMEBUFFER)!==e.FRAMEBUFFER_COMPLETE?(e.deleteFramebuffer(r),e.deleteTexture(n),null):(e.viewport(0,0,d,m),e.clearColor(0,0,0,1),e.clear(e.COLOR_BUFFER_BIT),{texture:n,buffer:r})},T=t=>{t&&(e.deleteTexture(t.texture),e.deleteFramebuffer(t.buffer))},y=()=>{[...v,...g,p,x].forEach(T),v=[],g=[],p=x=null},b=(t,n)=>{const r=t.locations;return e.useProgram(t.program),e.bindFramebuffer(e.FRAMEBUFFER,n.buffer),e.uniform2f(r.uTexel,1/d,1/m),r},w=(t,n,r,i)=>{e.activeTexture(e.TEXTURE0+r),e.bindTexture(e.TEXTURE_2D,i),e.uniform1i(t[n],r)},M=()=>e.drawArrays(e.TRIANGLES,0,3),R=e=>{const t=e[0];e[0]=e[1],e[1]=t};return{resize(t,n){y(),d=t,m=n;const r=[E(e.LINEAR),E(e.LINEAR),E(e.NEAREST),E(e.NEAREST),E(e.NEAREST),E(e.NEAREST)];return e.bindFramebuffer(e.FRAMEBUFFER,null),r.some(e=>!e)?(r.forEach(T),!1):(v=[r[0],r[1]],g=[r[2],r[3]],p=r[4],x=r[5],!0)},ready:()=>h.every(c),step(t,n){if(!v.length||!p||!x)return;let r;e.viewport(0,0,d,m),t&&(r=b(f.splat,v[1]),w(r,"uVelocity",0,v[0].texture),e.uniform2f(r.uFrom,t.from[0],t.from[1]),e.uniform2f(r.uTo,t.to[0],t.to[1]),e.uniform2f(r.uForce,t.force[0],t.force[1]),e.uniform1f(r.uAspect,n.aspect),e.uniform1f(r.uRadius,n.radius),M(),R(v)),n.curl>0&&(r=b(f.curl,x),w(r,"uVelocity",0,v[0].texture),M(),r=b(f.vorticity,v[1]),w(r,"uVelocity",0,v[0].texture),w(r,"uCurl",1,x.texture),e.uniform1f(r.uStrength,n.curl),e.uniform1f(r.uDt,n.dt),M(),R(v)),r=b(f.divergence,p),w(r,"uVelocity",0,v[0].texture),M(),r=b(f.fade,g[1]),w(r,"uSource",0,g[0].texture),e.uniform1f(r.uAmount,.8),M(),R(g);for(let e=0;e<20;e++)r=b(f.pressure,g[1]),w(r,"uPressure",0,g[0].texture),w(r,"uDivergence",1,p.texture),M(),R(g);r=b(f.gradient,v[1]),w(r,"uPressure",0,g[0].texture),w(r,"uVelocity",1,v[0].texture),M(),R(v),r=b(f.advect,v[1]),w(r,"uVelocity",0,v[0].texture),e.uniform1f(r.uDt,n.dt),e.uniform1f(r.uDissipation,n.dissipation),M(),R(v),e.bindFramebuffer(e.FRAMEBUFFER,null)},get velocity(){return v[0]?.texture??null},get size(){return[d,m]},release:y,dispose(){y(),h.forEach(({program:t})=>e.deleteProgram(t))}}}(A,Ve,e=>Ge(e))}const $e=()=>{Ye();const e=v.fluid;if(!(e&&We()&&z&&O))return;U||qe();const t=e.resolution,n=z/O;if(j=n>=1?t:Math.max(8,Math.round(t*n)),K=n>=1?Math.max(8,Math.round(t/n)):t,N=!!X&&X.resize(j,K),N)return void(J=0);const r=()=>{Ye();for(let e=0;e<2;e++){const e=ze(A.LINEAR);A.texImage2D(A.TEXTURE_2D,0,L?A.RGBA8:A.RGBA16F,j,K,0,A.RGBA,L?A.UNSIGNED_BYTE:A.HALF_FLOAT,null);const t=A.createFramebuffer();A.bindFramebuffer(A.FRAMEBUFFER,t),A.framebufferTexture2D(A.FRAMEBUFFER,A.COLOR_ATTACHMENT0,A.TEXTURE_2D,e,0),B.push(e),I.push(t)}return A.checkFramebufferStatus(A.FRAMEBUFFER)===A.FRAMEBUFFER_COMPLETE};if(!r()){if(L)throw new Error("The glyph field could not allocate its fluid buffers.");L=!0,je(),qe(),r()}for(const e of I)A.bindFramebuffer(A.FRAMEBUFFER,e),A.viewport(0,0,j,K),A.clearColor(L?.5:0,L?.5:0,0,1),A.clear(A.COLOR_BUFFER_BIT);A.bindFramebuffer(A.FRAMEBUFFER,null),J=0};function je(){k&&A.deleteProgram(k.program),C&&A.deleteProgram(C.program),Oe(),k=Ve((t=>`#version 300 es\nprecision highp float;\nprecision highp sampler2D;\nuniform sampler2D uSource, uFluid;\nuniform vec2 uResolution, uCell;\nuniform float uCount;\nuniform vec4 uSourceMap;\nuniform int uScene;\nuniform vec3 uInk, uHighlight;\nuniform vec4 uBackground;\nuniform float uBrightness, uContrast, uGamma, uInvert, uVignette, uSourceColor;\nuniform float uRefraction, uGlow, uTime, uDrift, uFluidOn, uLod, uSaturation, uBackdrop, uShade, uHighlightAuto, uFluidMode;\nuniform vec2 uFluidTexel;\nuniform int uHoverMode;\nuniform vec3 uLight;\nuniform vec3 uTrail[12];\nuniform float uLightRadius, uLightGain, uClock, uFlicker, uTrailAmount;\nuniform sampler2D uTrailMap;\nuniform vec2 uLevels;\nuniform int uMark;\nuniform float uScale, uClearFeather, uClearShape;\nuniform vec4 uClear;\nuniform vec4 uPulse[4];\nlayout(location=0) out vec4 outInk;\nlayout(location=1) out vec4 outUnder;\n${e(t)}\nfloat hash(vec2 p){p=fract(p*vec2(123.34,456.21));p+=dot(p,p+45.32);return fract(p.x*p.y);}\nfloat noise(vec2 p){vec2 i=floor(p),f=fract(p),u=f*f*(3.-2.*f);\n return mix(mix(hash(i),hash(i+vec2(1,0)),u.x),mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),u.x),u.y);}\nfloat fbm(vec2 p){float v=0.,a=.5;for(int i=0;i<5;i++){v+=a*noise(p);p=p*2.03+vec2(1.7,9.2);a*=.5;}return v;}\nvec3 generated(vec2 uv,float aspect){\n vec2 p=(uv-.5)*vec2(aspect,1.)*uScale;\n if(uScene==1){\n float t=uTime*.08;\n vec2 q=vec2(fbm(p*1.6+t),fbm(p*1.6-t+3.1));\n return vec3(smoothstep(.28,.86,fbm(p*1.8+q*1.6+vec2(t*.6,-t*.4))));\n }\n if(uScene==2){\n float r=.36,d=length(p),a=uTime*.25;\n vec3 l=normalize(vec3(cos(a)*.8,.55,sin(a)*.6+.6));\n if(d<r){\n vec3 n=normalize(vec3(p.x,-p.y,sqrt(r*r-d*d)));\n float diffuse=max(dot(n,l),0.),rim=pow(1.-n.z,2.5);\n float lon=atan(n.x,n.z)+uTime*.35,lat=asin(clamp(n.y,-1.,1.));\n float bands=.5+.25*sin(lat*14.)+.25*sin(lon*6.);\n return vec3(clamp(diffuse*.85+rim*.5+bands*.12,0.,1.));\n }\n return vec3(exp(-pow((d-r)*9.,2.))*.35);\n }\n if(uScene==4){\n // Drift: a few broad lobes that wander slowly. Built for halftone, where large soft shapes read best.\n float t=uTime*.045;\n vec2 q=p*1.15;\n vec2 w=vec2(fbm(q*.7+vec2(t,-t*.7)),fbm(q*.7+vec2(4.3-t*.8,t+1.7)));\n float n=fbm(q+w*1.3+vec2(t*.4,t*.25));\n return vec3(smoothstep(.36,.74,n));\n }\n return vec3(0.);\n}\nvoid main(){\n vec2 cell=floor(gl_FragCoord.xy);\n vec2 uv=(cell+.5)*uCell/uResolution;\n float aspect=uResolution.x/uResolution.y;\n vec4 fluid=vec4(0.);\n // lift: signed tone change. The solver's flow brightens one side of each eddy and opens the other into gaps.\n float lift=0.,wake=0.,bend=1.,swirl=0.,speed=0.;\n if(uFluidOn>.5){\n vec4 raw=texture(uFluid,vec2(uv.x,1.-uv.y));\n if(uFluidMode>.5){\n vec2 v=raw.xy*uFluidTexel*vec2(aspect,1.);\n fluid=vec4(v,0.,1.);\n swirl=clamp(dot(v,vec2(.4,.65))*uGlow*.32,-.65,.65);\n speed=clamp(length(v)*uGlow*.055,0.,.38);\n bend=.4;\n } else {\n fluid=decodeFluid(raw);\n wake=clamp(fluid.z*uGlow,0.,1.);\n lift=wake*.55;\n }\n }\n vec2 drift=uDrift*.006*vec2(sin(uv.y*5.3+uTime*.6),cos(uv.x*4.1-uTime*.5));\n vec2 sampleAt=uv-vec2(fluid.x/aspect,-fluid.y)*uRefraction*bend+drift;\n vec3 rgb,glow=vec3(0.);\n if(uScene==0){\n vec2 s=sampleAt*uSourceMap.xy+uSourceMap.zw;\n bool inside=!(s.x<0.||s.y<0.||s.x>1.||s.y>1.);\n rgb=inside?textureLod(uSource,s,uLod).rgb:vec3(0.);\n if(uBackdrop>0.){\n vec2 b=(uv-vec2(fluid.x/aspect,-fluid.y)*uRefraction*bend*.5)*uSourceMap.xy+uSourceMap.zw;\n glow=(b.x<0.||b.y<0.||b.x>1.||b.y>1.)?vec3(0.):textureLod(uSource,b,uLod+3.).rgb*uBackdrop;\n }\n } else rgb=generated(sampleAt,aspect);\n float tone=dot(rgb,vec3(.2126,.7152,.0722));\n tone=clamp((tone-uLevels.x)/max(uLevels.y-uLevels.x,.01),0.,1.);\n tone=clamp((tone-.5)*uContrast+.5+uBrightness,0.,1.);\n tone=pow(tone,1./uGamma);\n if(uInvert>.5)tone=1.-tone;\n float flick=0.,trailFold=0.;\n if(uHoverMode==2){\n // Lantern: a soft light and its fading tail lift the characters they pass. Nothing moves the image.\n vec2 ap=vec2(aspect,1.),d=(uv-uLight.xy)*ap;\n float r2=uLightRadius*uLightRadius,glowL=uLight.z*exp(-dot(d,d)/r2);\n for(int i=0;i<12;i++){vec2 e=(uv-uTrail[i].xy)*ap;glowL+=uTrail[i].z*exp(-dot(e,e)/(r2*.55));}\n glowL=min(glowL,1.);\n float grain=fract(sin(dot(cell,vec2(12.9898,78.233)))*43758.5453);\n lift=glowL*uLightGain*(.7+.6*grain);\n wake=glowL*.4;\n // A few letters at the light's rim step up the ramp and back, as if being retyped.\n flick=uFlicker*step(.9,fract(grain*13.7+floor(uClock*7.+grain*5.)*.37))*glowL*(1.-glowL)*4.;\n }\n if(uHoverMode==3){\n // Trail: the studio's ink field, as a 16-bit density. Sparse marks fill in, dense marks open up.\n vec4 ink=texture(uTrailMap,uv);\n float density=(ink.r*65280.+ink.g*255.)/65535.*uTrailAmount;\n float t=clamp(density/.38,0.,1.);\n trailFold=t*t*(3.-2.*t);\n }\n if(uFluidMode>.5&&uHoverMode==1){\n // Dark ground: any motion lights the characters. Lit ground: the flow lights one side of each eddy and opens the other.\n // A little cell-to-cell grain keeps the lit area a shimmer of letters rather than a solid block.\n float grain=fract(sin(dot(cell,vec2(12.9898,78.233)))*43758.5453);\n lift=mix(abs(swirl)*.5+speed,swirl+speed*.35,smoothstep(.12,.55,tone))*(.65+.7*grain);\n wake=clamp(lift*1.6,0.,1.);\n }\n tone=clamp(tone+lift,0.,1.);\n tone+=(1.-2.*tone)*trailFold;\n // Pulses: rings sent by the interface (field.pulse()), e.g. on a save or a notification.\n for(int i=0;i<4;i++){\n if(uPulse[i].w<=0.)continue;\n float dist=length((uv-uPulse[i].xy)*vec2(aspect,1.)),front=uPulse[i].z*.95;\n float ring=exp(-pow((dist-front)/.1,2.))*uPulse[i].w*exp(-uPulse[i].z*.85);\n tone=clamp(tone+ring*.85,0.,1.);\n }\n tone*=1.-uVignette*smoothstep(.35,1.05,length((uv-.5)*vec2(min(aspect,1.8),1.)*2.));\n if(uClear.z>0.){\n // Clear zone: the field fades out around content so text above it stays readable.\n vec2 half_=uClear.zw*.5*vec2(aspect,1.),q=(uv-uClear.xy-uClear.zw*.5)*vec2(aspect,1.);\n float dz;\n if(uClearShape>.5){vec2 e=abs(q)-half_;dz=length(max(e,0.))+min(max(e.x,e.y),0.);}\n else dz=(length(q/max(half_,vec2(1e-4)))-1.)*min(half_.x,half_.y);\n tone*=smoothstep(0.,uClearFeather,dz);\n }\n float index=min(uCount-1.,floor(tone*(uCount-1.)+.5)+(flick>.5&&tone>.02?1.:0.));\n vec3 hue=rgb/max(max(rgb.r,rgb.g),max(rgb.b,.03));\n hue=mix(vec3(dot(hue,vec3(.2126,.7152,.0722))),hue,uSaturation);\n vec3 ink=mix(uInk*mix(1.,.3+.7*tone,uShade),clamp(hue*(.38+.62*tone),0.,1.),uSourceColor);\n vec3 lifted=uHighlightAuto>.5?ink/max(max(ink.r,ink.g),max(ink.b,.06)):uHighlight;\n ink=mix(ink,lifted,wake*.7);\n float underAlpha=max(uBackground.a,min(1.,max(glow.r,max(glow.g,glow.b))));\n vec3 under=min(uBackground.rgb*uBackground.a+glow,vec3(underAlpha));\n // Halftone keeps the tone for the dot size; glyphs keep their atlas index, exact in 8 bits.\n outInk=vec4(ink,uMark==1?(tone>.003?tone:0.):index/255.);\n outUnder=vec4(under,underAlpha);\n}`)(L)),C=Ve("#version 300 es\nprecision highp float;\nprecision highp sampler2D;\nuniform sampler2D uAtlas, uCells, uUnder;\nuniform vec2 uResolution, uCell;\nuniform float uColumns, uDotScale;\nuniform int uMark;\nout vec4 outColor;\nvoid main(){\n vec2 frag=vec2(gl_FragCoord.x,uResolution.y-gl_FragCoord.y);\n vec2 cell=floor(frag/uCell);\n vec4 ink=texelFetch(uCells,ivec2(cell),0),under=texelFetch(uUnder,ivec2(cell),0);\n vec2 local=frag-cell*uCell;\n float coverage;\n if(uMark==1){\n // Halftone: dot area follows tone, so midtones read true; edges are antialiased to one device pixel.\n float r=sqrt(ink.a)*min(uCell.x,uCell.y)*.5*uDotScale;\n coverage=ink.a>.003?clamp(r-length(local-uCell*.5)+.5,0.,1.):0.;\n } else {\n float index=floor(ink.a*255.+.5);\n ivec2 tile=ivec2(int(mod(index,uColumns)),int(floor(index/uColumns)));\n coverage=texelFetch(uAtlas,tile*ivec2(uCell)+ivec2(local),0).a;\n }\n outColor=vec4(ink.rgb*coverage+under.rgb*(1.-coverage),coverage+under.a*(1.-coverage));\n}"),_||He(!0)}const Ke=()=>`${v.fontWeight} ${Math.max(1,q.height*v.glyphScale)}px ${v.font}`,Je=()=>{const e=p(v.charset);$=e.length,q=T(v.cellSize,v.cellAspect,W,$),Y=W;const t=document.createElement("canvas");t.width=q.atlasWidth,t.height=q.atlasHeight;const n=t.getContext("2d");n.fillStyle="#fff",n.textAlign="center",n.textBaseline="alphabetic",n.font=Ke();const r=n.measureText("M"),i=r.actualBoundingBoxAscent||q.height*v.glyphScale*.7,o=r.actualBoundingBoxDescent||0,a=(q.height+i-o)/2;e.forEach((e,t)=>{const r=t%q.columns*q.width+q.width/2,i=Math.floor(t/q.columns)*q.height+a,o=n.measureText(e).width;n.save(),n.beginPath(),n.rect(r-q.width/2,i-a,q.width,q.height),n.clip(),n.translate(r,i),o>.96*q.width&&n.scale(.96*q.width/o,1),n.fillText(e,0,0),n.restore()}),A.bindTexture(A.TEXTURE_2D,S),A.pixelStorei(A.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),A.texImage2D(A.TEXTURE_2D,0,A.RGBA8,A.RGBA,A.UNSIGNED_BYTE,t),t.width=t.height=1,he=!0},Qe="undefined"==typeof document?void 0:document.fonts,Ze=()=>{if(!Qe?.load)return;const e=Ke();Qe.load(e,p(v.charset).join("")).then(()=>{de||me||Ke()!==e||(Je(),Et())},()=>{})},et=e=>e.trim().replace(/^["']|["']$/g,"").toLowerCase(),tt=e=>{const t=e.fontfaces??[],n=v.font.split(",").map(et);t.some(e=>n.includes(et(e.family)))&&Ze()},nt=()=>{if(le=[0,1],Q&&"none"!==v.levels)try{const e=document.createElement("canvas");e.width=e.height=64;const t=e.getContext("2d",{willReadFrequently:!0});Q===re&&(t.imageSmoothingQuality="medium"),t.drawImage(Q,0,0,64,64),le=b(t.getImageData(0,0,64,64).data)}catch{le=[0,1]}},rt=()=>{if(!Q)return;const e=performance.now();Q instanceof HTMLVideoElement&&!(e-ue>1500)||(nt(),ue=e),A.bindTexture(A.TEXTURE_2D,D),A.pixelStorei(A.UNPACK_FLIP_Y_WEBGL,!1),A.pixelStorei(A.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1);try{A.texImage2D(A.TEXTURE_2D,0,A.RGBA8,A.RGBA,A.UNSIGNED_BYTE,Q),A.generateMipmap(A.TEXTURE_2D)}catch(e){Xe(e)}},it=()=>{L=!A.getExtension("EXT_color_buffer_float")&&!A.getExtension("EXT_color_buffer_half_float"),je(),V=A.createVertexArray(),S=ze(A.NEAREST),D=ze(A.LINEAR_MIPMAP_LINEAR),A.bindTexture(A.TEXTURE_2D,D),A.texImage2D(A.TEXTURE_2D,0,A.RGBA8,1,1,0,A.RGBA,A.UNSIGNED_BYTE,new Uint8Array([0,0,0,255])),A.generateMipmap(A.TEXTURE_2D),z=O=0,Y=0,ot(),Ze(),Q&&rt()};function ot(){const e=t.getBoundingClientRect(),n=Math.min(globalThis.devicePixelRatio||1,v.maxPixelRatio),r=Math.max(1,Math.round(e.width*n)),i=Math.max(1,Math.round(e.height*n));if(r===z&&i===O&&n===W)return!1;const o=!z||Math.abs(r/i-z/O)>.01;return W=n,z=r,O=i,t.width=z,t.height=O,Y!==W&&Je(),(o||We()&&!B.length&&!N)&&$e(),he=!0,!0}let at=null,lt=[0,0,0],ut=[1,1,1],st=[0,0,0,0],ct=E(1,1,1,1,"cover"),ft=null,ht=0,dt=0,mt=0,vt=0;const gt=()=>{at!==v&&(at=v,lt=y(v.ink),ut=y("auto"===v.highlight?"#ffffff":v.highlight),st="transparent"===v.background?[0,0,0,0]:[...y(v.background),1]),ft===v&&ht===z&&dt===O&&mt===ee&&vt===te||(ft=v,ht=z,dt=O,mt=ee,vt=te,ct=E(z,O,ee||1,te||1,v.fit,v.focus));const e=v.fluid,t=Q&&ie?q.height/O*ie*ct.scale[1]:1,n=(()=>{const e=Math.ceil(z/q.width),t=Math.ceil(O/q.height);if(P&&P.columns===e&&P.rows===t)return P;Oe();const n=()=>{const n=ze(A.NEAREST);return A.texStorage2D(A.TEXTURE_2D,1,L?A.RGBA8:A.RGBA16F,e,t),n},r=n(),i=n(),o=A.createFramebuffer();return A.bindFramebuffer(A.FRAMEBUFFER,o),A.framebufferTexture2D(A.FRAMEBUFFER,A.COLOR_ATTACHMENT0,A.TEXTURE_2D,r,0),A.framebufferTexture2D(A.FRAMEBUFFER,A.COLOR_ATTACHMENT1,A.TEXTURE_2D,i,0),A.drawBuffers([A.COLOR_ATTACHMENT0,A.COLOR_ATTACHMENT1]),P={ink:r,under:i,buffer:o,columns:e,rows:t}})(),r=k.locations,i=C.locations,o=w(v),a=!Ue();A.bindFramebuffer(A.FRAMEBUFFER,n.buffer),A.viewport(0,0,n.columns,n.rows),A.useProgram(k.program),A.activeTexture(A.TEXTURE0),A.bindTexture(A.TEXTURE_2D,D),A.uniform1i(r.uSource,0),A.activeTexture(A.TEXTURE2),A.bindTexture(A.TEXTURE_2D,(N?X?.velocity:B[J])??D),A.uniform1i(r.uFluid,2),A.activeTexture(A.TEXTURE3),A.bindTexture(A.TEXTURE_2D,_e??D),A.uniform1i(r.uTrailMap,3),A.uniform1f(r.uFluidMode,N?1:0),A.uniform1i(r.uHoverMode,"fluid"===o?1:"lantern"===o?2:"trail"===o&&Se?3:0),A.uniform1f(r.uTrailAmount,v.trail.strength),A.uniform3f(r.uLight,Fe.at?.[0]??-1,Fe.at?.[1]??-1,"lantern"===o?Fe.strength:0),A.uniform3fv(r.uTrail,Ae),A.uniform1f(r.uLightRadius,v.lantern.radius),A.uniform1f(r.uLightGain,v.lantern.strength),A.uniform1f(r.uClock,performance.now()/1e3),A.uniform1f(r.uFlicker,a?1:0),A.uniform2f(r.uFluidTexel,1/Math.max(1,j),1/Math.max(1,K)),A.uniform2f(r.uResolution,z,O),A.uniform2f(r.uCell,q.width,q.height),A.uniform1f(r.uCount,$),A.uniform4f(r.uSourceMap,ct.scale[0],ct.scale[1],ct.offset[0],ct.offset[1]),A.uniform1i(r.uScene,Q?0:"flow"===v.scene?1:"orb"===v.scene?2:"drift"===v.scene?4:3),A.uniform1i(r.uMark,"dot"===v.mark?1:0),A.uniform1f(r.uScale,v.scale);const l=v.clear;A.uniform4f(r.uClear,l?l.x:0,l?l.y:0,l?l.width:0,l?l.height:0),A.uniform1f(r.uClearFeather,l?l.feather:.1),A.uniform1f(r.uClearShape,l&&"rect"===l.shape?1:0);const u=performance.now();Me.fill(0);for(let e=0;e<we.length;e++){const t=we[e],n=4*e;Me[n]=t.x,Me[n+1]=t.y,Me[n+2]=(u-t.born)/1e3,Me[n+3]=t.strength}A.uniform4fv(r.uPulse,Me),A.uniform3fv(r.uInk,lt),A.uniform3fv(r.uHighlight,ut),A.uniform1f(r.uHighlightAuto,"auto"===v.highlight?1:0),A.uniform4fv(r.uBackground,st),A.uniform1f(r.uBrightness,v.brightness),A.uniform1f(r.uContrast,v.contrast),A.uniform1f(r.uGamma,v.gamma),A.uniform1f(r.uInvert,v.invert?1:0),A.uniform1f(r.uVignette,v.vignette),A.uniform1f(r.uSourceColor,"source"===v.color?1:0),A.uniform1f(r.uRefraction,e?e.refraction*(a?1:.25):0),A.uniform1f(r.uGlow,e?e.glow:0),A.uniform1f(r.uTime,fe),A.uniform1f(r.uDrift,a?v.drift:0),A.uniform1f(r.uFluidOn,e&&"fluid"===o&&(B.length||N)?1:0),A.uniform1f(r.uLod,Math.max(0,Math.log2(Math.max(1,t))-.5)),A.uniform1f(r.uSaturation,v.saturation),A.uniform1f(r.uShade,v.shade),A.uniform1f(r.uBackdrop,Q?v.backdrop:0),A.uniform2f(r.uLevels,Q&&"auto"===v.levels?le[0]:0,Q&&"auto"===v.levels?le[1]:1),A.bindVertexArray(V),A.drawArrays(A.TRIANGLES,0,3),A.bindFramebuffer(A.FRAMEBUFFER,null),A.viewport(0,0,z,O),A.useProgram(C.program),A.activeTexture(A.TEXTURE1),A.bindTexture(A.TEXTURE_2D,S),A.uniform1i(i.uAtlas,1),A.activeTexture(A.TEXTURE4),A.bindTexture(A.TEXTURE_2D,n.ink),A.uniform1i(i.uCells,4),A.activeTexture(A.TEXTURE5),A.bindTexture(A.TEXTURE_2D,n.under),A.uniform1i(i.uUnder,5),A.uniform2f(i.uResolution,z,O),A.uniform2f(i.uCell,q.width,q.height),A.uniform1f(i.uColumns,q.columns),A.uniform1f(i.uDotScale,v.dotScale),A.uniform1i(i.uMark,"dot"===v.mark?1:0),A.drawArrays(A.TRIANGLES,0,3)},pt=()=>!de&&!me&&!ve&&(pe||Ie)&&!("undefined"!=typeof document&&document.hidden),xt=e=>{if(se=0,!pt())return;const t=(e-ce)/1e3,n=Math.min(1/30,Math.max(1/480,t));ce=e,Te=.9*Te+.1*Math.min(t,.25),ye=Te>1/24?ye+1:0;try{ye>45&&be<2&&(ye=0,be++,1===be&&W>1?(v={...v,maxPixelRatio:1},ot()):be=2);const t=He(),r=!(ge||be>=2&&!(Q instanceof HTMLVideoElement)||(!(Q instanceof HTMLVideoElement)||Q.paused||Q.ended)&&(Ue()||!(!Q&&"none"!==v.scene||v.drift>0)));r&&(fe+=n*v.speed),Q instanceof HTMLVideoElement&&Q.readyState>=2&&oe&&(oe=!("requestVideoFrameCallback"in Q),rt(),he=!0);const i=w(v);let o="fluid"===i&&(null!==Ee||e<ke);if(o?((e,t)=>{const n=v.fluid;if(!n||!I.length&&!N)return;if(N?!X?.ready():!U||!Ge(U))return;const r=z/O,i=Ee;if(Ee=null,N&&X){const o=5200*n.force*(Ue()?.35:1);return i&&(ke=t+1e3*Math.min(9,6/Math.max(.3,n.dissipation))),void X.step(i?{from:i.from,to:i.to,force:[(i.to[0]-i.from[0])*o,(i.to[1]-i.from[1])*o]}:null,{dt:Math.min(e,1/60),radius:1.1*n.radius,curl:n.curl,dissipation:n.dissipation,aspect:r})}let o=0,a=0;if(i){const e=(Ue()?1:3)*n.force;o=(i.to[0]-i.from[0])*r*e*4,a=(i.to[1]-i.from[1])*e*4,ke=t+7/Math.min(n.dissipation,1.1*n.dissipation)*1e3}const l=U.locations;A.bindFramebuffer(A.FRAMEBUFFER,I[1-J]),A.viewport(0,0,j,K),A.useProgram(U.program),A.activeTexture(A.TEXTURE0),A.bindTexture(A.TEXTURE_2D,B[J]),A.uniform1i(l.uPrevious,0),A.uniform2f(l.uSize,j,K),A.uniform1f(l.uAspect,r),A.uniform1f(l.uDt,e),A.uniform1f(l.uDecay,n.dissipation),A.uniform1f(l.uEnergyDecay,1.1*n.dissipation),A.uniform1f(l.uRadius,n.radius),A.uniform1f(l.uSplat,i?1:0),A.uniform2f(l.uFrom,i?.from[0]??0,i?.from[1]??0),A.uniform2f(l.uTo,i?.to[0]??0,i?.to[1]??0),A.uniform2f(l.uForce,o,a),A.drawArrays(A.TRIANGLES,0,3),J=1-J})(n,e):Ee=null,"lantern"===i&&(o=((e,t)=>{const n=v.lantern,r=Fe.target;if(r){const i=Fe.at;if(i)if(Ue())i[0]=r[0],i[1]=r[1];else{const t=1-Math.exp(-n.follow*e);i[0]+=(r[0]-i[0])*t,i[1]+=(r[1]-i[1])*t}else Fe.at=[r[0],r[1]];const o=Fe.at,a=Fe.trail[Fe.trail.length-1];n.trail>0&&(!a||t-a.born>28&&Math.hypot(o[0]-a.x,o[1]-a.y)>.004)&&(Fe.trail.push({x:o[0],y:o[1],born:t}),Fe.trail.length>12&&Fe.trail.shift())}Fe.strength+=((r?1:0)-Fe.strength)*(1-Math.exp(-e*(r?9:5)));const i=Fe.trail;for(let e=i.length-1;e>=0;e--)n.trail>0&&t-i[e].born<4e3*n.trail||i.splice(e,1);Ae.fill(0);for(let e=0;e<i.length;e++){const r=i[e];Ae[3*e]=r.x,Ae[3*e+1]=r.y,Ae[3*e+2]=.55*Math.exp(-(t-r.born)/1e3/Math.max(.01,n.trail))}return Fe.strength>.003||i.length>0})(n,e)||o),"trail"===i){const e=!!Le?.active;o=(e=>{const t=De();return!(!t||!t.active||(t.step(e),A.bindTexture(A.TEXTURE_2D,_e),A.texSubImage2D(A.TEXTURE_2D,0,0,0,t.width,t.height,A.RGBA,A.UNSIGNED_BYTE,t.pixels),Se=!0,0))})(n)||o,e&&!Le?.active&&(he=!0)}if(we.length){for(let t=we.length-1;t>=0;t--)e-we[t].born>=2400&&we.splice(t,1);o=!0,we.length||(he=!0)}t&&(r||o||he)&&(gt(),he=!1,!Ie||ne&&!Q||(Ie=!1,Pe())),(r||o||Ee||!t)&&(se=requestAnimationFrame(xt))}catch(e){ve=!0,Xe(e)}};function Et(){!se&&pt()&&(ce=performance.now(),se=requestAnimationFrame(xt))}const Tt=(e,n)=>{const r=t.getBoundingClientRect();if(!r.width||!r.height)return null;const i=(e-r.left)/r.width,o=(n-r.top)/r.height;return i<-.05||i>1.05||o<-.05||o>1.05?null:[i,1-o]},yt=e=>{const t=w(v);if("none"===t||ge)return xe=null,void(Fe.target=null);if("lantern"===t)return Fe.target=e?[e[0],1-e[1]]:null,xe=null,void Et();if("trail"===t){const t=De();if(!t)return;return e?t.move(e[0],1-e[1],v.trail.radius,performance.now()):t.leave(),xe=null,void Et()}e?(xe&&(Ee=Ee?{from:Ee.from,to:e}:{from:xe,to:e}),xe=e,Et()):xe=null},bt=e=>yt(Tt(e.clientX,e.clientY)),wt=()=>{xe=null,Le?.leave(),Fe.target&&(Fe.target=null,Et())},Mt=f.interactionTarget??window;Mt.addEventListener("pointermove",bt,{passive:!0}),Mt.addEventListener("pointerdown",bt,{passive:!0}),document.addEventListener("pointerleave",wt),Mt!==window&&Mt.addEventListener("pointerleave",wt);const Rt="function"==typeof ResizeObserver?new ResizeObserver(()=>{const e=ot();Re(),(e||he)&&Et()}):null;Rt?.observe(t),m&&Rt?.observe(m.element);const Ft="function"==typeof IntersectionObserver?new IntersectionObserver(([e])=>{pe=e.isIntersecting,pe&&(he=!0,Et())},{rootMargin:"120px"}):null;Ft?.observe(t);const At=()=>{document.hidden||(he=!0,Et())};document.addEventListener("visibilitychange",At);const Lt=()=>{he=!0,Et()};Ce?.addEventListener?.("change",Lt),Qe?.addEventListener?.("loadingdone",tt);const _t=e=>{e.preventDefault(),me=!0,se&&cancelAnimationFrame(se),se=0},St=()=>{me=!1,H.length=0,G=null,k=C=U=null,P=null,B=[],I=[],X=null,N=!1,_e=null,Le=null,Se=!1;try{it(),he=!0,Et()}catch(e){Xe(e)}};t.addEventListener("webglcontextlost",_t),t.addEventListener("webglcontextrestored",St);const Dt=()=>{Z&&(Z.pause(),Z.removeAttribute("src"),Z.load(),Z=null),ae&&Q instanceof HTMLVideoElement&&"cancelVideoFrameCallback"in Q&&Q.cancelVideoFrameCallback(ae),ae=0},kt=()=>{re?.close(),re=null},Ct=async e=>{const t=await createImageBitmap(e,{premultiplyAlpha:"none"}),n=[t.width,t.height],r=function(e,t,n,r,i,o,a=8){if(!(n>=16&&r>=16&&e>0&&t>0&&i>0))return 1;const l=a/i/("cover"===o?Math.min(e/n,t/r):Math.max(e/n,t/r));return l>.5?1:2**-Math.floor(Math.log2(1/l))}(t.width,t.height,z,O,q.height,v.fit);if(1===r)return{element:t,owned:!0,natural:n};try{return{element:await createImageBitmap(t,{resizeWidth:Math.max(1,Math.round(t.width*r)),resizeHeight:Math.max(1,Math.round(t.height*r)),resizeQuality:"medium",premultiplyAlpha:"none"}),owned:!0,natural:n}}finally{t.close()}},Ut=e=>new Promise((t,n)=>{const r=new Image;r.crossOrigin="anonymous",r.decoding="async",r.onload=()=>t({element:r,owned:!1}),r.onerror=()=>n(new Error("The glyph field could not load this image.")),r.src=e}),Pt=async e=>{const t=++ne;if(!e)return Dt(),kt(),Q=null,ee=te=ie=0,he=!0,void Et();try{const n=await(async e=>{if("string"!=typeof e)return e instanceof HTMLImageElement&&!e.complete?e.decode().then(()=>({element:e,owned:!1})):e instanceof HTMLVideoElement&&e.readyState<2?new Promise((t,n)=>{e.addEventListener("loadeddata",()=>t({element:e,owned:!1}),{once:!0}),e.addEventListener("error",()=>n(new Error("The glyph field could not load this video.")),{once:!0})}):{element:e,owned:!1};if(F.test(e))return new Promise((t,n)=>{const r=document.createElement("video");r.crossOrigin="anonymous",r.muted=!0,r.loop=!0,r.playsInline=!0,r.preload="auto",r.addEventListener("loadeddata",()=>{r.play().catch(()=>{}),t({element:r,owned:!0})},{once:!0}),r.addEventListener("error",()=>n(new Error("The glyph field could not load this video.")),{once:!0}),r.src=e});if("function"!=typeof createImageBitmap||"function"!=typeof fetch)return Ut(e);let t;try{t=await(await fetch(e).then(e=>{if(!e.ok)throw new Error("The glyph field could not load this image.");return e})).blob()}catch(t){if(t instanceof Error&&t.message.startsWith("The glyph field"))throw t;return Ut(e)}if(!/svg/i.test(t.type))try{return await Ct(t)}catch{}const n=URL.createObjectURL(t);try{return await Ut(n)}finally{URL.revokeObjectURL(n)}})(e),{element:r,owned:i}=n;if(de||t!==ne)return void(i&&r instanceof HTMLVideoElement?(r.pause(),r.removeAttribute("src"),r.load()):i&&r.close());Dt(),kt(),Z=i&&r instanceof HTMLVideoElement?r:null,!i||r instanceof HTMLVideoElement||(re=r),Q=r,ie=r instanceof HTMLVideoElement?r.videoHeight:r instanceof HTMLImageElement?r.naturalHeight:r.height,ee=n.natural?.[0]??(r instanceof HTMLVideoElement?r.videoWidth:r instanceof HTMLImageElement?r.naturalWidth:r.width),te=n.natural?.[1]??ie,oe=!0,r instanceof HTMLVideoElement&&(e=>{const t=e;if(!t.requestVideoFrameCallback)return;const n=()=>{de||Q!==e||(oe=!0,Et(),ae=t.requestVideoFrameCallback(n))};ae=t.requestVideoFrameCallback(n)})(r),me||rt(),he=!0,Et()}catch(e){Ie&&t===ne&&(Ie=!1,Pe()),Xe(e)}};it(),Re();const Bt=f.source??null;return Bt?(ne=1,Pt(Bt)):Et(),{canvas:t,ready:Be,get settings(){return v},update(e){const n=v,{element:r,rest:i}=d(e);"clear"in e&&(m&&m.element!==r?.element&&Rt?.unobserve(m.element),m=r,r&&Rt?.observe(r.element)),v=x(r?{...i,clear:v.clear}:i,v),Re(),e.label&&t.setAttribute("aria-label",e.label),v.maxPixelRatio!==n.maxPixelRatio&&ot(),v.charset===n.charset&&v.cellSize===n.cellSize&&v.cellAspect===n.cellAspect&&v.font===n.font&&v.fontWeight===n.fontWeight&&v.glyphScale===n.glyphScale||(Je(),Ze()),v.levels!==n.levels&&nt();((!!n.fluid&&"fluid"===w(n))!==We()||v.fluid&&n.fluid&&v.fluid.resolution!==n.fluid.resolution)&&$e(),"source"in e&&Pt(e.source??null),he=!0,Et()},setSource:Pt,pointer(e,n=0){if(null===e)return void yt(null);const r=t.getBoundingClientRect();yt(Tt(r.left+e,r.top+n))},redraw(){if(!(de||me||ve)&&z)try{He(!0)&&gt()}catch(e){ve=!0,Xe(e)}},pulse(e={}){if(de||Ue())return;const n=t.getBoundingClientRect();if(!n.width||!n.height)return;let r=e.x??n.width/2,i=e.y??n.height/2;if(e.element){const t=e.element.getBoundingClientRect();r=t.left+t.width/2-n.left,i=t.top+t.height/2-n.top}const o=Math.max(0,Math.min(2,e.strength??1));we=[...we,{x:r/n.width,y:i/n.height,born:performance.now(),strength:o}].slice(-4),Et()},play(){ge=!1,Q instanceof HTMLVideoElement&&Q.play().catch(()=>{}),he=!0,Et()},pause(){ge=!0,Q instanceof HTMLVideoElement&&Q.pause()},resize(){ot()&&Et()},destroy(){if(!de&&(de=!0,se&&cancelAnimationFrame(se),Mt.removeEventListener("pointermove",bt),Mt.removeEventListener("pointerdown",bt),document.removeEventListener("pointerleave",wt),Mt.removeEventListener("pointerleave",wt),document.removeEventListener("visibilitychange",At),Ce?.removeEventListener?.("change",Lt),Qe?.removeEventListener?.("loadingdone",tt),t.removeEventListener("webglcontextlost",_t),t.removeEventListener("webglcontextrestored",St),Rt?.disconnect(),Ft?.disconnect(),Dt(),kt(),Q=null,!me)){Ye(),Oe(),A.deleteTexture(S),A.deleteTexture(D);for(const e of[k,C,U])e&&A.deleteProgram(e.program);X?.dispose(),_e&&A.deleteTexture(_e),H.forEach(e=>A.deleteShader(e)),V&&A.deleteVertexArray(V)}}}},exports.normalizeFieldSettings=x;//# sourceMappingURL=field.cjs.map
1
+ "use strict";var e=e=>e?"\nconst float VSCALE=6.;\nvec4 decodeFluid(vec4 t){return vec4((t.xy*2.-1.)*VSCALE,t.z,t.w);}\nvec4 encodeFluid(vec4 v){return vec4(clamp(v.xy/VSCALE*.5+.5,0.,1.),clamp(v.z,0.,1.),1.);}":"\nvec4 decodeFluid(vec4 t){return t;}\nvec4 encodeFluid(vec4 v){return vec4(v.xyz,1.);}",t="#version 300 es\nprecision highp float;\nprecision highp sampler2D;\nuniform vec2 uTexel;\nout vec4 outColor;\nvec2 here(){return gl_FragCoord.xy*uTexel;}\n",n=t+"\nuniform sampler2D uVelocity;\nuniform vec2 uFrom, uTo, uForce;\nuniform float uAspect, uRadius;\nfloat segment(vec2 p,vec2 a,vec2 b){vec2 pa=p-a,ba=b-a;float h=clamp(dot(pa,ba)/max(dot(ba,ba),1e-8),0.,1.);return length(pa-ba*h);}\nvoid main(){\n vec2 uv=here(),scale=vec2(uAspect,1.);\n float d=segment(uv*scale,uFrom*scale,uTo*scale);\n outColor=vec4(texture(uVelocity,uv).xy+uForce*exp(-d*d/(uRadius*uRadius)),0.,1.);\n}",r=t+"\nuniform sampler2D uVelocity;\nvoid main(){\n vec2 uv=here();\n float l=texture(uVelocity,uv-vec2(uTexel.x,0.)).y,r=texture(uVelocity,uv+vec2(uTexel.x,0.)).y;\n float t=texture(uVelocity,uv+vec2(0.,uTexel.y)).x,b=texture(uVelocity,uv-vec2(0.,uTexel.y)).x;\n outColor=vec4(.5*(r-l-t+b),0.,0.,1.);\n}",i=t+"\nuniform sampler2D uVelocity, uCurl;\nuniform float uStrength, uDt;\nvoid main(){\n vec2 uv=here();\n float l=texture(uCurl,uv-vec2(uTexel.x,0.)).x,r=texture(uCurl,uv+vec2(uTexel.x,0.)).x;\n float t=texture(uCurl,uv+vec2(0.,uTexel.y)).x,b=texture(uCurl,uv-vec2(0.,uTexel.y)).x,c=texture(uCurl,uv).x;\n vec2 force=.5*vec2(abs(t)-abs(b),abs(r)-abs(l));\n force=force/(length(force)+1e-4)*uStrength*c;\n force.y=-force.y;\n outColor=vec4(clamp(texture(uVelocity,uv).xy+force*uDt,-1000.,1000.),0.,1.);\n}",o=t+"\nuniform sampler2D uVelocity;\nvoid main(){\n vec2 uv=here(),c=texture(uVelocity,uv).xy;\n float l=texture(uVelocity,uv-vec2(uTexel.x,0.)).x,r=texture(uVelocity,uv+vec2(uTexel.x,0.)).x;\n float t=texture(uVelocity,uv+vec2(0.,uTexel.y)).y,b=texture(uVelocity,uv-vec2(0.,uTexel.y)).y;\n if(uv.x-uTexel.x<0.)l=-c.x;\n if(uv.x+uTexel.x>1.)r=-c.x;\n if(uv.y+uTexel.y>1.)t=-c.y;\n if(uv.y-uTexel.y<0.)b=-c.y;\n outColor=vec4(.5*(r-l+t-b),0.,0.,1.);\n}",a=t+"\nuniform sampler2D uSource;\nuniform float uAmount;\nvoid main(){outColor=vec4(texture(uSource,here()).x*uAmount,0.,0.,1.);}",l=t+"\nuniform sampler2D uPressure, uDivergence;\nvoid main(){\n vec2 uv=here();\n float l=texture(uPressure,uv-vec2(uTexel.x,0.)).x,r=texture(uPressure,uv+vec2(uTexel.x,0.)).x;\n float t=texture(uPressure,uv+vec2(0.,uTexel.y)).x,b=texture(uPressure,uv-vec2(0.,uTexel.y)).x;\n outColor=vec4((l+r+b+t-texture(uDivergence,uv).x)*.25,0.,0.,1.);\n}",u=t+"\nuniform sampler2D uPressure, uVelocity;\nvoid main(){\n vec2 uv=here();\n float l=texture(uPressure,uv-vec2(uTexel.x,0.)).x,r=texture(uPressure,uv+vec2(uTexel.x,0.)).x;\n float t=texture(uPressure,uv+vec2(0.,uTexel.y)).x,b=texture(uPressure,uv-vec2(0.,uTexel.y)).x;\n outColor=vec4(texture(uVelocity,uv).xy-vec2(r-l,t-b),0.,1.);\n}",s=t+"\nuniform sampler2D uVelocity;\nuniform float uDt, uDissipation;\nvoid main(){\n vec2 uv=here();\n vec2 back=uv-uDt*texture(uVelocity,uv).xy*uTexel;\n outColor=vec4(texture(uVelocity,back).xy/(1.+uDissipation*uDt),0.,1.);\n}";var c=class{constructor(e,t){this.previous=null,this.peak=0,this.width=e,this.height=t;const n=e*t;this.pixels=new Uint8Array(4*n),this.signal=new Float32Array(n),this.offsetX=new Float32Array(n),this.offsetY=new Float32Array(n),this.driftX=new Float32Array(n),this.driftY=new Float32Array(n),this.u=new Float32Array(n),this.v=new Float32Array(n),this.ink=new Float32Array(n),this.a=new Float32Array(n),this.b=new Float32Array(n),this.c=new Float32Array(n),this.pressure=new Float32Array(n),this.pressureNext=new Float32Array(n),this.divergence=new Float32Array(n),this.curl=new Float32Array(n)}get active(){return this.peak>2e-4}clear(){for(const e 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])e.fill(0);this.previous=null,this.peak=0}leave(){this.previous=null}move(e,t,n,r=performance.now()){if(!Number.isFinite(e+t+n+r))return;e=Math.max(0,Math.min(1,e)),t=Math.max(0,Math.min(1,t)),n=Math.max(.1,Math.min(1,n));const i=this.previous;if(this.previous={x:e,y:t,time:r},!i)return;const o=(e-i.x)*(this.width-1),a=(t-i.y)*(this.height-1),l=Math.hypot(o,a);if(l<.001)return;const u=Math.min(this.width,this.height),s=r>i.time?Math.max(1/240,(r-i.time)/1e3):1/60,c=Math.min(12,l/u/s),f=1-Math.exp(-c/2.2),h=u*(.022+.075*n)*(.65+.95*f),d=Math.min(128,Math.max(1,Math.ceil(l/(.4*h)))),m=l/h*(.32+.22*f)/d,v=o/l,g=a/l,p=-g,x=v,E=l*(2+18*f)/d;for(let n=0;n<d;n++){const r=(n+.5)/d,o=(i.x+(e-i.x)*r)*(this.width-1),a=(i.y+(t-i.y)*r)*(this.height-1),l=Math.max(1,Math.floor(o-3*h)),u=Math.min(this.width-2,Math.ceil(o+3*h)),s=Math.max(1,Math.floor(a-3*h)),c=Math.min(this.height-2,Math.ceil(a+3*h));for(let e=s;e<=c;e++)for(let t=l;t<=u;t++){const n=((t-o)*v+(e-a)*g)/h,r=((t-o)*p+(e-a)*x)/h,i=Math.exp(-n*n*.65-r*r*1.7),l=e*this.width+t;this.ink[l]=1-(1-this.ink[l])*Math.exp(-i*m);const u=1-r*r*.55,s=n*r*.7;this.u[l]=Math.max(-80,Math.min(80,this.u[l]+(v*u+p*s)*i*E)),this.v[l]=Math.max(-80,Math.min(80,this.v[l]+(g*u+x*s)*i*E))}}this.peak=1}read(e,t,n){const r=0|(t=Math.max(.5,Math.min(this.width-1.5,t))),i=0|(n=Math.max(.5,Math.min(this.height-1.5,n))),o=t-r,a=n-i,l=i*this.width+r;return(e[l]*(1-o)+e[l+1]*o)*(1-a)+(e[l+this.width]*(1-o)+e[l+this.width+1]*o)*a}sample(e,t){return this.read(this.signal,e*(this.width-1),t*(this.height-1))}displacement(e,t,n){n[0]=this.read(this.offsetX,e*(this.width-1),t*(this.height-1)),n[1]=this.read(this.offsetY,e*(this.width-1),t*(this.height-1))}step(e){if(!this.active||!Number.isFinite(e))return;let t=Math.max(0,Math.min(.05,e));const n=this.width,r=this.height;for(;t>1e-7;){const e=Math.min(1/60,t);t-=e;const i=Math.exp(-1.9*e),o=Math.exp(-3.2*e);for(let t=1;t<r-1;t++)for(let a=1;a<n-1;a++){const l=t*n+a,u=Math.max(.5,Math.min(n-1.5,a-this.u[l]*e)),s=Math.max(.5,Math.min(r-1.5,t-this.v[l]*e)),c=0|u,f=0|s,h=u-c,d=s-f,m=f*n+c,v=(1-h)*(1-d),g=h*(1-d),p=(1-h)*d,x=h*d;this.a[l]=(this.u[m]*v+this.u[m+1]*g+this.u[m+n]*p+this.u[m+n+1]*x)*i,this.b[l]=(this.v[m]*v+this.v[m+1]*g+this.v[m+n]*p+this.v[m+n+1]*x)*i,this.c[l]=(this.ink[m]*v+this.ink[m+1]*g+this.ink[m+n]*p+this.ink[m+n+1]*x)*o}[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 e=1;e<r-1;e++)for(let t=1;t<n-1;t++){const r=e*n+t;this.curl[r]=.5*(this.v[r+1]-this.v[r-1]-this.u[r+n]+this.u[r-n])}for(let t=1;t<r-1;t++)for(let r=1;r<n-1;r++){const i=t*n+r,o=Math.abs(this.curl[i+1])-Math.abs(this.curl[i-1]),a=Math.abs(this.curl[i+n])-Math.abs(this.curl[i-n]),l=Math.hypot(o,a)+1e-4,u=this.curl[i]*e*10,s=Math.hypot(this.u[i],this.v[i]);this.u[i]+=a/l*u,this.v[i]-=o/l*u;const c=Math.min(1,s/(Math.hypot(this.u[i],this.v[i])+1e-6));this.u[i]*=c,this.v[i]*=c,this.divergence[i]=-.5*(this.u[i+1]-this.u[i-1]+this.v[i+n]-this.v[i-n])}this.pressure.fill(0);for(let e=0;e<4;e++){for(let e=1;e<r-1;e++)for(let t=1;t<n-1;t++){const r=e*n+t;this.pressureNext[r]=.25*(this.divergence[r]+this.pressure[r-1]+this.pressure[r+1]+this.pressure[r-n]+this.pressure[r+n])}[this.pressure,this.pressureNext]=[this.pressureNext,this.pressure]}let a=0;for(let t=1;t<r-1;t++)for(let r=1;r<n-1;r++){const i=t*n+r;this.u[i]=Math.max(-80,Math.min(80,this.u[i]-.5*(this.pressure[i+1]-this.pressure[i-1]))),this.v[i]=Math.max(-80,Math.min(80,this.v[i]-.5*(this.pressure[i+n]-this.pressure[i-n]))),this.driftX[i]+=(3.6*this.u[i]-58*this.offsetX[i]-12*this.driftX[i])*e,this.driftY[i]+=(3.6*this.v[i]-58*this.offsetY[i]-12*this.driftY[i])*e,this.offsetX[i]=Math.max(-2,Math.min(2,this.offsetX[i]+this.driftX[i]*e)),this.offsetY[i]=Math.max(-2,Math.min(2,this.offsetY[i]+this.driftY[i]*e)),this.signal[i]=1-Math.exp(.06*-Math.hypot(this.u[i],this.v[i])-.16*this.ink[i]),a=Math.max(a,this.signal[i])}this.peak=a}if(this.active)for(let e=0;e<this.ink.length;e++){const t=Math.round(65535*Math.min(1,this.signal[e]));this.pixels[4*e]=t>>>8,this.pixels[4*e+1]=255&t,this.pixels[4*e+2]=Math.round(128+63.5*this.offsetX[e]),this.pixels[4*e+3]=Math.round(128+63.5*this.offsetY[e])}else this.clear()}},f={radius:.13,strength:.5,trail:.45,follow:12},h={strength:.55,radius:.45},d={radius:.075,force:1,dissipation:1.6,refraction:.03,glow:.55,resolution:128,curl:24},m={charset:" .,:-=+*#%@",cellSize:12,cellAspect:1,font:'ui-monospace, "SF Mono", Menlo, Consolas, monospace',fontWeight:500,glyphScale:.8,mark:"glyph",dotScale:.9,color:"ink",ink:"#f3ba32",highlight:"auto",background:"transparent",brightness:0,contrast:1,gamma:1,invert:!1,levels:"auto",saturation:1.1,shade:.5,backdrop:0,fit:"cover",focus:{x:.5,y:.5},vignette:0,drift:0,scene:"flow",scale:1,clear:!1,speed:1,hover:"fluid",fluid:d,lantern:f,trail:h,maxPixelRatio:2,respectReducedMotion:!0},v=(e,t,n,r)=>"number"==typeof e&&Number.isFinite(e)?Math.min(n,Math.max(t,e)):r,g=(e,t)=>"string"==typeof e&&/^#([\da-f]{3}|[\da-f]{6})$/i.test(e.trim())?e.trim().toLowerCase():t;function p(e){const t=Intl.Segmenter;return(t?Array.from(new t(void 0,{granularity:"grapheme"}).segment(e),e=>e.segment):Array.from(e)).filter(e=>"\n"!==e&&"\r"!==e&&"\t"!==e).slice(0,256)}function x(e={},t=m){const n="string"==typeof e.charset&&p(e.charset).length>=2?e.charset:t.charset,r=t.fluid||d,i=void 0===e.fluid?t.fluid:e.fluid,o=!1!==i&&(()=>{const e=!0!==i&&i?{...r,...i}:r;return{radius:v(e.radius,.005,.5,d.radius),force:v(e.force,0,8,d.force),dissipation:v(e.dissipation,.05,20,d.dissipation),refraction:v(e.refraction,0,.5,d.refraction),glow:v(e.glow,0,2,d.glow),resolution:Math.round(v(e.resolution,16,512,d.resolution)),curl:v(e.curl,0,80,d.curl)}})(),a="none"===e.scene||"flow"===e.scene||"orb"===e.scene||"drift"===e.scene?e.scene:t.scene,l=!!e.clear&&"object"==typeof e.clear&&("element"in e.clear||"nodeType"in e.clear),u=void 0===e.clear||l?t.clear:e.clear,s=!!u&&{x:v(u.x,-.5,1.5,.25),y:v(u.y,-.5,1.5,.3),width:v(u.width,0,2,.5),height:v(u.height,0,2,.4),feather:v(u.feather,.005,1,.12),shape:"rect"===u.shape?"rect":"ellipse"};return{charset:n,cellSize:v(e.cellSize,3,96,t.cellSize),cellAspect:v(e.cellAspect,.3,2,t.cellAspect),font:"string"==typeof e.font&&e.font.trim()?e.font:t.font,fontWeight:Math.round(v(e.fontWeight,100,900,t.fontWeight)),glyphScale:v(e.glyphScale,.3,1.6,t.glyphScale),mark:"dot"===e.mark||"glyph"===e.mark?e.mark:t.mark,dotScale:v(e.dotScale,.1,1.4,t.dotScale),color:"source"===e.color||"ink"===e.color?e.color:t.color,ink:g(e.ink,t.ink),highlight:"auto"===e.highlight?"auto":g(e.highlight,t.highlight),background:"transparent"===e.background?"transparent":g(e.background,t.background),brightness:v(e.brightness,-1,1,t.brightness),contrast:v(e.contrast,0,4,t.contrast),gamma:v(e.gamma,.2,5,t.gamma),invert:"boolean"==typeof e.invert?e.invert:t.invert,levels:"none"===e.levels||"auto"===e.levels?e.levels:t.levels,saturation:v(e.saturation,0,3,t.saturation),shade:v(e.shade,0,1,t.shade),backdrop:v(e.backdrop,0,1,t.backdrop),fit:"contain"===e.fit||"cover"===e.fit?e.fit:t.fit,focus:{x:v(e.focus?.x,0,1,t.focus.x),y:v(e.focus?.y,0,1,t.focus.y)},vignette:v(e.vignette,0,1,t.vignette),drift:v(e.drift,0,2,t.drift),scene:a,scale:v(e.scale,.2,6,t.scale),clear:s,speed:v(e.speed,0,5,t.speed),hover:"fluid"===e.hover||"lantern"===e.hover||"trail"===e.hover||"none"===e.hover?e.hover:t.hover,fluid:o,lantern:{radius:v(e.lantern?.radius,.02,.6,t.lantern.radius),strength:v(e.lantern?.strength,0,1.5,t.lantern.strength),trail:v(e.lantern?.trail,0,3,t.lantern.trail),follow:v(e.lantern?.follow,1,60,t.lantern.follow)},trail:{strength:v(e.trail?.strength,0,1,t.trail.strength),radius:v(e.trail?.radius,.1,1,t.trail.radius)},maxPixelRatio:v(e.maxPixelRatio,.5,4,t.maxPixelRatio),respectReducedMotion:"boolean"==typeof e.respectReducedMotion?e.respectReducedMotion:t.respectReducedMotion}}function E(e,t,n,r,i,o={x:.5,y:.5}){if(!(e>0&&t>0&&n>0&&r>0))return{scale:[1,1],offset:[0,0]};const a=e/t,l=n/r,u=l>a,s="cover"===i?u?[a/l,1]:[1,l/a]:u?[1,l/a]:[a/l,1];return{scale:s,offset:"cover"===i?[(1-s[0])*o.x,(1-s[1])*o.y]:[.5*(1-s[0]),.5*(1-s[1])]}}function T(e,t,n,r){const i=Math.max(2,Math.round(e*n)),o=Math.max(2,Math.round(i*t)),a=Math.min(16,Math.max(1,r)),l=Math.ceil(r/a);return{width:o,height:i,columns:a,rows:l,atlasWidth:a*o,atlasHeight:l*i}}function y(e){const t=e.replace("#",""),n=3===t.length?t.split("").map(e=>e+e).join(""):t,r=Number.parseInt(n,16);return Number.isFinite(r)&&6===n.length?[(r>>16&255)/255,(r>>8&255)/255,(255&r)/255]:[0,0,0]}function b(e){const t=new Uint32Array(256);let n=0;for(let r=0;r+3<e.length;r+=4)e[r+3]<8||(t[Math.round(.2126*e[r]+.7152*e[r+1]+.0722*e[r+2])]++,n++);if(!n)return[0,1];const r=e=>{let r=0;for(let i=0;i<256;i++)if(r+=t[i],r>=n*e)return i/255;return 1},i=r(.02),o=r(.98);return o-i<.08?[0,1]:[i,o]}var w=e=>"fluid"!==e.hover||e.fluid?e.hover:"none";function M(e,t,n=24,r=.12,i="ellipse"){return e.width>0&&e.height>0&&{x:(t.left-n-e.left)/e.width,y:(t.top-n-e.top)/e.height,width:(t.width+2*n)/e.width,height:(t.height+2*n)/e.height,feather:r,shape:i}}var R,F=/\.(mp4|webm|mov|m4v|ogv)(\?|#|$)/i,A=/swiftshader|llvmpipe|softpipe|software|basic render/i;function L(){if(R)return R;if("undefined"==typeof document||void 0===globalThis.WebGL2RenderingContext)return R="none";try{const e=document.createElement("canvas").getContext("webgl2");if(!e)return R="none";const t=e.getExtension("WEBGL_debug_renderer_info"),n=String(e.getParameter(t?t.UNMASKED_RENDERER_WEBGL:e.RENDERER)??"");return e.getExtension("WEBGL_lose_context")?.loseContext(),R=A.test(n)?"software":"gpu"}catch{return R="none"}}exports.DEFAULT_FIELD_FLUID=d,exports.DEFAULT_FIELD_LANTERN=f,exports.DEFAULT_FIELD_SETTINGS=m,exports.DEFAULT_FIELD_TRAIL=h,exports.FIELD_HOVERS=[{value:"fluid",label:"Fluid",description:"An ink-in-water wake that bends the image and swirls the characters."},{value:"lantern",label:"Lantern",description:"A soft light follows the pointer and lifts the characters it passes, without distortion."},{value:"trail",label:"Trail",description:"The Asciify Trail: in the wake, sparse characters fill in and dense ones open up, then settle. No distortion."},{value:"none",label:"Off",description:"The field stays still under the pointer."}],exports.activeFieldHover=w,exports.fieldCellLayout=T,exports.fieldClearFromRects=M,exports.fieldGlyphs=p,exports.fieldLevels=b,exports.fieldSourceRect=E,exports.glyphFieldTier=L,exports.isGlyphFieldSupported=function(e={}){const t=L();return"gpu"===t||"software"===t&&!!e.allowSoftware},exports.mountGlyphField=function(t,f={}){const h=e=>"undefined"!=typeof HTMLElement&&e instanceof HTMLElement,d=e=>{const t=e.clear;return h(t)?{element:{element:t},rest:{...e,clear:void 0}}:t&&"object"==typeof t&&h(t.element)?{element:t,rest:{...e,clear:void 0}}:{element:null,rest:e}};let m=d(f).element,v=x(d(f).rest);const g=f.onError,R=t.getContext("webgl2",{alpha:!0,antialias:!1,depth:!1,stencil:!1,premultipliedAlpha:!0,powerPreference:"high-performance"});if(!R)throw new Error("WebGL2 is not available for the glyph field.");const A=R;t.setAttribute("role","img"),t.setAttribute("aria-label",f.label??"ASCII artwork"),t.dataset.renderer="glyph-field";let L=!1;const _=A.getExtension("KHR_parallel_shader_compile");let S,D,k=null,C=null,U=null,P=null,B=[],I=[],X=null,N=!1,V=null,G=null;const H=[];let z=0,O=0,W=1,Y=0,q=T(v.cellSize,v.cellAspect,1,2),$=2,j=0,K=0,J=0,Q=null,Z=null,ee=0,te=0,ne=0,re=null,ie=0,oe=!0,ae=0,le=[0,1],ue=-1/0,se=0,ce=0,fe=0,he=!0,de=!1,me=!1,ve=!1,ge=!1,pe=!0,xe=null,Ee=null,Te=1/60,ye=0,be=0,we=[];const Me=new Float32Array(16),Re=()=>{if(!m)return;const{element:e,padding:n=32,feather:r=.16,shape:i="rect"}=m,o=M(t.getBoundingClientRect(),e.getBoundingClientRect(),n,r,i),a=v.clear;JSON.stringify(a)!==JSON.stringify(o)&&(v={...v,clear:o},he=!0)},Fe={target:null,at:null,strength:0,trail:[]},Ae=new Float32Array(36);let Le=null,_e=null,Se=!1;const De=()=>{if(!z||!O)return null;const e=z/O,t=Math.round(e>=1?128:128*e),n=Math.round(e>=1?128/e:128);return Le&&Le.width===t&&Le.height===n||(Le=new c(t,n),_e||(_e=ze(A.LINEAR)),A.bindTexture(A.TEXTURE_2D,_e),A.texImage2D(A.TEXTURE_2D,0,A.RGBA8,t,n,0,A.RGBA,A.UNSIGNED_BYTE,Le.pixels),Se=!0),Le};let ke=0;const Ce="function"==typeof matchMedia?matchMedia("(prefers-reduced-motion: reduce)"):null,Ue=()=>v.respectReducedMotion&&!!Ce?.matches;let Pe;const Be=new Promise(e=>{Pe=e});let Ie=!0;const Xe=e=>g?.(e instanceof Error?e:new Error(String(e))),Ne=(e,t)=>{const n=A.createShader(e);return H.push(n),A.shaderSource(n,t),A.compileShader(n),n},Ve=e=>{G??(G=Ne(A.VERTEX_SHADER,"#version 300 es\nvoid main(){vec2 p=vec2((gl_VertexID<<1)&2,gl_VertexID&2);gl_Position=vec4(p*2.-1.,0.,1.);}"));const t=A.createProgram(),n=Ne(A.FRAGMENT_SHADER,e);return A.attachShader(t,G),A.attachShader(t,n),A.linkProgram(t),{program:t,locations:null,fragment:n}},Ge=(e,t=!1)=>{if(e.locations)return!0;const{program:n,fragment:r}=e;if(!t&&_&&!A.getProgramParameter(n,_.COMPLETION_STATUS_KHR))return!1;if(!A.getProgramParameter(n,A.LINK_STATUS)){if(A.isContextLost())return!1;throw new Error(A.getShaderInfoLog(r)||A.getProgramInfoLog(n)||"Glyph field program failed to link.")}const i={};for(let e=A.getProgramParameter(n,A.ACTIVE_UNIFORMS)-1;e>=0;e--){const t=A.getActiveUniform(n,e).name.replace(/\[0\]$/,"");i[t]=A.getUniformLocation(n,t)}return e.locations=i,!0},He=(e=!1)=>!!k&&!!C&&Ge(k,e)&&Ge(C,e),ze=e=>{const t=A.createTexture();return A.bindTexture(A.TEXTURE_2D,t),A.texParameteri(A.TEXTURE_2D,A.TEXTURE_MIN_FILTER,e),A.texParameteri(A.TEXTURE_2D,A.TEXTURE_MAG_FILTER,e===A.LINEAR_MIPMAP_LINEAR?A.LINEAR:e),A.texParameteri(A.TEXTURE_2D,A.TEXTURE_WRAP_S,A.CLAMP_TO_EDGE),A.texParameteri(A.TEXTURE_2D,A.TEXTURE_WRAP_T,A.CLAMP_TO_EDGE),t},Oe=()=>{P&&(A.deleteTexture(P.ink),A.deleteTexture(P.under),A.deleteFramebuffer(P.buffer),P=null)},We=()=>!!v.fluid&&"fluid"===w(v),Ye=()=>{X?.release(),N=!1,B.forEach(e=>A.deleteTexture(e)),I.forEach(e=>A.deleteFramebuffer(e)),B=[],I=[]};function qe(){U&&A.deleteProgram(U.program),U=Ve((t=>`#version 300 es\nprecision highp float;\nprecision highp sampler2D;\nuniform sampler2D uPrevious;\nuniform vec2 uSize;\nuniform float uAspect, uDt, uDecay, uEnergyDecay, uRadius, uSplat;\nuniform vec2 uFrom, uTo, uForce;\nout vec4 outColor;\n${e(t)}\nfloat segment(vec2 p,vec2 a,vec2 b){vec2 pa=p-a,ba=b-a;float h=clamp(dot(pa,ba)/max(dot(ba,ba),1e-8),0.,1.);return length(pa-ba*h);}\nvoid main(){\n vec2 uv=gl_FragCoord.xy/uSize;\n vec4 here=decodeFluid(texture(uPrevious,uv));\n vec4 state=decodeFluid(texture(uPrevious,uv-uDt*here.xy*vec2(1./uAspect,1.)));\n state.xy*=exp(-uDt*uDecay);\n state.z*=exp(-uDt*uEnergyDecay);\n if(uSplat>.5){\n vec2 scale=vec2(uAspect,1.);\n float d=segment(uv*scale,uFrom*scale,uTo*scale);\n float g=exp(-d*d/(uRadius*uRadius));\n state.xy+=uForce*g;\n state.z=min(1.,state.z+g*min(1.,length(uForce)*.5+.08));\n }\n float speed=length(state.xy);\n if(speed>5.)state.xy*=5./speed;\n outColor=encodeFluid(state);\n}`)(L)),X?.dispose(),X=L?null:function(e,t,c){const f={splat:t(n),curl:t(r),vorticity:t(i),divergence:t(o),fade:t(a),pressure:t(l),gradient:t(u),advect:t(s)},h=Object.values(f);let d=0,m=0,v=[],g=[],p=null,x=null;const E=t=>{const n=e.createTexture();e.bindTexture(e.TEXTURE_2D,n),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,t),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,t),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),e.texImage2D(e.TEXTURE_2D,0,e.RGBA16F,d,m,0,e.RGBA,e.HALF_FLOAT,null);const r=e.createFramebuffer();return e.bindFramebuffer(e.FRAMEBUFFER,r),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,n,0),e.checkFramebufferStatus(e.FRAMEBUFFER)!==e.FRAMEBUFFER_COMPLETE?(e.deleteFramebuffer(r),e.deleteTexture(n),null):(e.viewport(0,0,d,m),e.clearColor(0,0,0,1),e.clear(e.COLOR_BUFFER_BIT),{texture:n,buffer:r})},T=t=>{t&&(e.deleteTexture(t.texture),e.deleteFramebuffer(t.buffer))},y=()=>{[...v,...g,p,x].forEach(T),v=[],g=[],p=x=null},b=(t,n)=>{const r=t.locations;return e.useProgram(t.program),e.bindFramebuffer(e.FRAMEBUFFER,n.buffer),e.uniform2f(r.uTexel,1/d,1/m),r},w=(t,n,r,i)=>{e.activeTexture(e.TEXTURE0+r),e.bindTexture(e.TEXTURE_2D,i),e.uniform1i(t[n],r)},M=()=>e.drawArrays(e.TRIANGLES,0,3),R=e=>{const t=e[0];e[0]=e[1],e[1]=t};return{resize(t,n){y(),d=t,m=n;const r=[E(e.LINEAR),E(e.LINEAR),E(e.NEAREST),E(e.NEAREST),E(e.NEAREST),E(e.NEAREST)];return e.bindFramebuffer(e.FRAMEBUFFER,null),r.some(e=>!e)?(r.forEach(T),!1):(v=[r[0],r[1]],g=[r[2],r[3]],p=r[4],x=r[5],!0)},ready:()=>h.every(c),step(t,n){if(!v.length||!p||!x)return;let r;e.viewport(0,0,d,m),t&&(r=b(f.splat,v[1]),w(r,"uVelocity",0,v[0].texture),e.uniform2f(r.uFrom,t.from[0],t.from[1]),e.uniform2f(r.uTo,t.to[0],t.to[1]),e.uniform2f(r.uForce,t.force[0],t.force[1]),e.uniform1f(r.uAspect,n.aspect),e.uniform1f(r.uRadius,n.radius),M(),R(v)),n.curl>0&&(r=b(f.curl,x),w(r,"uVelocity",0,v[0].texture),M(),r=b(f.vorticity,v[1]),w(r,"uVelocity",0,v[0].texture),w(r,"uCurl",1,x.texture),e.uniform1f(r.uStrength,n.curl),e.uniform1f(r.uDt,n.dt),M(),R(v)),r=b(f.divergence,p),w(r,"uVelocity",0,v[0].texture),M(),r=b(f.fade,g[1]),w(r,"uSource",0,g[0].texture),e.uniform1f(r.uAmount,.8),M(),R(g);for(let e=0;e<20;e++)r=b(f.pressure,g[1]),w(r,"uPressure",0,g[0].texture),w(r,"uDivergence",1,p.texture),M(),R(g);r=b(f.gradient,v[1]),w(r,"uPressure",0,g[0].texture),w(r,"uVelocity",1,v[0].texture),M(),R(v),r=b(f.advect,v[1]),w(r,"uVelocity",0,v[0].texture),e.uniform1f(r.uDt,n.dt),e.uniform1f(r.uDissipation,n.dissipation),M(),R(v),e.bindFramebuffer(e.FRAMEBUFFER,null)},get velocity(){return v[0]?.texture??null},get size(){return[d,m]},release:y,dispose(){y(),h.forEach(({program:t})=>e.deleteProgram(t))}}}(A,Ve,e=>Ge(e))}const $e=()=>{Ye();const e=v.fluid;if(!(e&&We()&&z&&O))return;U||qe();const t=e.resolution,n=z/O;if(j=n>=1?t:Math.max(8,Math.round(t*n)),K=n>=1?Math.max(8,Math.round(t/n)):t,N=!!X&&X.resize(j,K),N)return void(J=0);const r=()=>{Ye();for(let e=0;e<2;e++){const e=ze(A.LINEAR);A.texImage2D(A.TEXTURE_2D,0,L?A.RGBA8:A.RGBA16F,j,K,0,A.RGBA,L?A.UNSIGNED_BYTE:A.HALF_FLOAT,null);const t=A.createFramebuffer();A.bindFramebuffer(A.FRAMEBUFFER,t),A.framebufferTexture2D(A.FRAMEBUFFER,A.COLOR_ATTACHMENT0,A.TEXTURE_2D,e,0),B.push(e),I.push(t)}return A.checkFramebufferStatus(A.FRAMEBUFFER)===A.FRAMEBUFFER_COMPLETE};if(!r()){if(L)throw new Error("The glyph field could not allocate its fluid buffers.");L=!0,je(),qe(),r()}for(const e of I)A.bindFramebuffer(A.FRAMEBUFFER,e),A.viewport(0,0,j,K),A.clearColor(L?.5:0,L?.5:0,0,1),A.clear(A.COLOR_BUFFER_BIT);A.bindFramebuffer(A.FRAMEBUFFER,null),J=0};function je(){k&&A.deleteProgram(k.program),C&&A.deleteProgram(C.program),Oe(),k=Ve((t=>`#version 300 es\nprecision highp float;\nprecision highp sampler2D;\nuniform sampler2D uSource, uFluid;\nuniform vec2 uResolution, uCell;\nuniform float uCount;\nuniform vec4 uSourceMap;\nuniform int uScene;\nuniform vec3 uInk, uHighlight;\nuniform vec4 uBackground;\nuniform float uBrightness, uContrast, uGamma, uInvert, uVignette, uSourceColor;\nuniform float uRefraction, uGlow, uTime, uDrift, uFluidOn, uLod, uSaturation, uBackdrop, uShade, uHighlightAuto, uFluidMode;\nuniform vec2 uFluidTexel;\nuniform int uHoverMode;\nuniform vec3 uLight;\nuniform vec3 uTrail[12];\nuniform float uLightRadius, uLightGain, uClock, uFlicker, uTrailAmount;\nuniform sampler2D uTrailMap;\nuniform vec2 uLevels;\nuniform int uMark;\nuniform float uScale, uClearFeather, uClearShape;\nuniform vec4 uClear;\nuniform vec4 uPulse[4];\nlayout(location=0) out vec4 outInk;\nlayout(location=1) out vec4 outUnder;\n${e(t)}\nfloat hash(vec2 p){p=fract(p*vec2(123.34,456.21));p+=dot(p,p+45.32);return fract(p.x*p.y);}\nfloat noise(vec2 p){vec2 i=floor(p),f=fract(p),u=f*f*(3.-2.*f);\n return mix(mix(hash(i),hash(i+vec2(1,0)),u.x),mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),u.x),u.y);}\nfloat fbm(vec2 p){float v=0.,a=.5;for(int i=0;i<5;i++){v+=a*noise(p);p=p*2.03+vec2(1.7,9.2);a*=.5;}return v;}\nvec3 generated(vec2 uv,float aspect){\n vec2 p=(uv-.5)*vec2(aspect,1.)*uScale;\n if(uScene==1){\n float t=uTime*.08;\n vec2 q=vec2(fbm(p*1.6+t),fbm(p*1.6-t+3.1));\n return vec3(smoothstep(.28,.86,fbm(p*1.8+q*1.6+vec2(t*.6,-t*.4))));\n }\n if(uScene==2){\n float r=.36,d=length(p),a=uTime*.25;\n vec3 l=normalize(vec3(cos(a)*.8,.55,sin(a)*.6+.6));\n if(d<r){\n vec3 n=normalize(vec3(p.x,-p.y,sqrt(r*r-d*d)));\n float diffuse=max(dot(n,l),0.),rim=pow(1.-n.z,2.5);\n float lon=atan(n.x,n.z)+uTime*.35,lat=asin(clamp(n.y,-1.,1.));\n float bands=.5+.25*sin(lat*14.)+.25*sin(lon*6.);\n return vec3(clamp(diffuse*.85+rim*.5+bands*.12,0.,1.));\n }\n return vec3(exp(-pow((d-r)*9.,2.))*.35);\n }\n if(uScene==4){\n // Drift: a few broad lobes that wander slowly. Built for halftone, where large soft shapes read best.\n float t=uTime*.045;\n vec2 q=p*1.15;\n vec2 w=vec2(fbm(q*.7+vec2(t,-t*.7)),fbm(q*.7+vec2(4.3-t*.8,t+1.7)));\n float n=fbm(q+w*1.3+vec2(t*.4,t*.25));\n return vec3(smoothstep(.36,.74,n));\n }\n return vec3(0.);\n}\nvoid main(){\n vec2 cell=floor(gl_FragCoord.xy);\n vec2 uv=(cell+.5)*uCell/uResolution;\n float aspect=uResolution.x/uResolution.y;\n vec4 fluid=vec4(0.);\n // lift: signed tone change. The solver's flow brightens one side of each eddy and opens the other into gaps.\n float lift=0.,wake=0.,bend=1.,swirl=0.,speed=0.;\n if(uFluidOn>.5){\n vec4 raw=texture(uFluid,vec2(uv.x,1.-uv.y));\n if(uFluidMode>.5){\n vec2 v=raw.xy*uFluidTexel*vec2(aspect,1.);\n fluid=vec4(v,0.,1.);\n swirl=clamp(dot(v,vec2(.4,.65))*uGlow*.32,-.65,.65);\n speed=clamp(length(v)*uGlow*.055,0.,.38);\n bend=.4;\n } else {\n fluid=decodeFluid(raw);\n wake=clamp(fluid.z*uGlow,0.,1.);\n lift=wake*.55;\n }\n }\n vec2 drift=uDrift*.006*vec2(sin(uv.y*5.3+uTime*.6),cos(uv.x*4.1-uTime*.5));\n vec2 sampleAt=uv-vec2(fluid.x/aspect,-fluid.y)*uRefraction*bend+drift;\n vec3 rgb,glow=vec3(0.);\n if(uScene==0){\n vec2 s=sampleAt*uSourceMap.xy+uSourceMap.zw;\n bool inside=!(s.x<0.||s.y<0.||s.x>1.||s.y>1.);\n rgb=inside?textureLod(uSource,s,uLod).rgb:vec3(0.);\n if(uBackdrop>0.){\n vec2 b=(uv-vec2(fluid.x/aspect,-fluid.y)*uRefraction*bend*.5)*uSourceMap.xy+uSourceMap.zw;\n glow=(b.x<0.||b.y<0.||b.x>1.||b.y>1.)?vec3(0.):textureLod(uSource,b,uLod+3.).rgb*uBackdrop;\n }\n } else rgb=generated(sampleAt,aspect);\n float tone=dot(rgb,vec3(.2126,.7152,.0722));\n tone=clamp((tone-uLevels.x)/max(uLevels.y-uLevels.x,.01),0.,1.);\n tone=clamp((tone-.5)*uContrast+.5+uBrightness,0.,1.);\n tone=pow(tone,1./uGamma);\n if(uInvert>.5)tone=1.-tone;\n float flick=0.,trailFold=0.;\n if(uHoverMode==2){\n // Lantern: a soft light and its fading tail lift the characters they pass. Nothing moves the image.\n vec2 ap=vec2(aspect,1.),d=(uv-uLight.xy)*ap;\n float r2=uLightRadius*uLightRadius,glowL=uLight.z*exp(-dot(d,d)/r2);\n for(int i=0;i<12;i++){vec2 e=(uv-uTrail[i].xy)*ap;glowL+=uTrail[i].z*exp(-dot(e,e)/(r2*.55));}\n glowL=min(glowL,1.);\n float grain=fract(sin(dot(cell,vec2(12.9898,78.233)))*43758.5453);\n lift=glowL*uLightGain*(.7+.6*grain);\n wake=glowL*.4;\n // A few letters at the light's rim step up the ramp and back, as if being retyped.\n flick=uFlicker*step(.9,fract(grain*13.7+floor(uClock*7.+grain*5.)*.37))*glowL*(1.-glowL)*4.;\n }\n if(uHoverMode==3){\n // Trail: the studio's ink field, as a 16-bit density. Sparse marks fill in, dense marks open up.\n vec4 ink=texture(uTrailMap,uv);\n float density=(ink.r*65280.+ink.g*255.)/65535.*uTrailAmount;\n float t=clamp(density/.38,0.,1.);\n trailFold=t*t*(3.-2.*t);\n }\n if(uFluidMode>.5&&uHoverMode==1){\n // Dark ground: any motion lights the characters. Lit ground: the flow lights one side of each eddy and opens the other.\n // A little cell-to-cell grain keeps the lit area a shimmer of letters rather than a solid block.\n float grain=fract(sin(dot(cell,vec2(12.9898,78.233)))*43758.5453);\n lift=mix(abs(swirl)*.5+speed,swirl+speed*.35,smoothstep(.12,.55,tone))*(.65+.7*grain);\n wake=clamp(lift*1.6,0.,1.);\n }\n tone=clamp(tone+lift,0.,1.);\n tone+=(1.-2.*tone)*trailFold;\n // Pulses: rings sent by the interface (field.pulse()), e.g. on a save or a notification.\n for(int i=0;i<4;i++){\n if(uPulse[i].w<=0.)continue;\n float dist=length((uv-uPulse[i].xy)*vec2(aspect,1.)),front=uPulse[i].z*.95;\n float ring=exp(-pow((dist-front)/.1,2.))*uPulse[i].w*exp(-uPulse[i].z*.85);\n tone=clamp(tone+ring*.85,0.,1.);\n }\n tone*=1.-uVignette*smoothstep(.35,1.05,length((uv-.5)*vec2(min(aspect,1.8),1.)*2.));\n if(uClear.z>0.){\n // Clear zone: the field fades out around content so text above it stays readable.\n vec2 half_=uClear.zw*.5*vec2(aspect,1.),q=(uv-uClear.xy-uClear.zw*.5)*vec2(aspect,1.);\n float dz;\n if(uClearShape>.5){vec2 e=abs(q)-half_;dz=length(max(e,0.))+min(max(e.x,e.y),0.);}\n else dz=(length(q/max(half_,vec2(1e-4)))-1.)*min(half_.x,half_.y);\n tone*=smoothstep(0.,uClearFeather,dz);\n }\n float index=min(uCount-1.,floor(tone*(uCount-1.)+.5)+(flick>.5&&tone>.02?1.:0.));\n vec3 hue=rgb/max(max(rgb.r,rgb.g),max(rgb.b,.03));\n hue=mix(vec3(dot(hue,vec3(.2126,.7152,.0722))),hue,uSaturation);\n vec3 ink=mix(uInk*mix(1.,.3+.7*tone,uShade),clamp(hue*(.38+.62*tone),0.,1.),uSourceColor);\n vec3 lifted=uHighlightAuto>.5?ink/max(max(ink.r,ink.g),max(ink.b,.06)):uHighlight;\n ink=mix(ink,lifted,wake*.7);\n float underAlpha=max(uBackground.a,min(1.,max(glow.r,max(glow.g,glow.b))));\n vec3 under=min(uBackground.rgb*uBackground.a+glow,vec3(underAlpha));\n // Halftone keeps the tone for the dot size; glyphs keep their atlas index, exact in 8 bits.\n outInk=vec4(ink,uMark==1?(tone>.003?tone:0.):index/255.);\n outUnder=vec4(under,underAlpha);\n}`)(L)),C=Ve("#version 300 es\nprecision highp float;\nprecision highp sampler2D;\nuniform sampler2D uAtlas, uCells, uUnder;\nuniform vec2 uResolution, uCell;\nuniform float uColumns, uDotScale;\nuniform int uMark;\nout vec4 outColor;\nvoid main(){\n vec2 frag=vec2(gl_FragCoord.x,uResolution.y-gl_FragCoord.y);\n vec2 cell=floor(frag/uCell);\n vec4 ink=texelFetch(uCells,ivec2(cell),0),under=texelFetch(uUnder,ivec2(cell),0);\n vec2 local=frag-cell*uCell;\n float coverage;\n if(uMark==1){\n // Halftone: dot area follows tone, so midtones read true; edges are antialiased to one device pixel.\n float r=sqrt(ink.a)*min(uCell.x,uCell.y)*.5*uDotScale;\n coverage=ink.a>.003?clamp(r-length(local-uCell*.5)+.5,0.,1.):0.;\n } else {\n float index=floor(ink.a*255.+.5);\n ivec2 tile=ivec2(int(mod(index,uColumns)),int(floor(index/uColumns)));\n coverage=texelFetch(uAtlas,tile*ivec2(uCell)+ivec2(local),0).a;\n }\n outColor=vec4(ink.rgb*coverage+under.rgb*(1.-coverage),coverage+under.a*(1.-coverage));\n}"),_||He(!0)}const Ke=()=>`${v.fontWeight} ${Math.max(1,q.height*v.glyphScale)}px ${v.font}`,Je=()=>{const e=p(v.charset);$=e.length,q=T(v.cellSize,v.cellAspect,W,$),Y=W;const t=document.createElement("canvas");t.width=q.atlasWidth,t.height=q.atlasHeight;const n=t.getContext("2d");n.fillStyle="#fff",n.textAlign="center",n.textBaseline="alphabetic",n.font=Ke();const r=n.measureText("M"),i=r.actualBoundingBoxAscent||q.height*v.glyphScale*.7,o=r.actualBoundingBoxDescent||0,a=(q.height+i-o)/2;e.forEach((e,t)=>{const r=t%q.columns*q.width+q.width/2,i=Math.floor(t/q.columns)*q.height+a,o=n.measureText(e).width;n.save(),n.beginPath(),n.rect(r-q.width/2,i-a,q.width,q.height),n.clip(),n.translate(r,i),o>.96*q.width&&n.scale(.96*q.width/o,1),n.fillText(e,0,0),n.restore()}),A.bindTexture(A.TEXTURE_2D,S),A.pixelStorei(A.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),A.texImage2D(A.TEXTURE_2D,0,A.RGBA8,A.RGBA,A.UNSIGNED_BYTE,t),t.width=t.height=1,he=!0},Qe="undefined"==typeof document?void 0:document.fonts,Ze=()=>{if(!Qe?.load)return;const e=Ke();Qe.load(e,p(v.charset).join("")).then(()=>{de||me||Ke()!==e||(Je(),Et())},()=>{})},et=e=>e.trim().replace(/^["']|["']$/g,"").toLowerCase(),tt=e=>{const t=e.fontfaces??[],n=v.font.split(",").map(et);t.some(e=>n.includes(et(e.family)))&&Ze()},nt=()=>{if(le=[0,1],Q&&"none"!==v.levels)try{const e=document.createElement("canvas");e.width=e.height=64;const t=e.getContext("2d",{willReadFrequently:!0});Q===re&&(t.imageSmoothingQuality="medium"),t.drawImage(Q,0,0,64,64),le=b(t.getImageData(0,0,64,64).data)}catch{le=[0,1]}},rt=()=>{if(!Q)return;const e=performance.now();Q instanceof HTMLVideoElement&&!(e-ue>1500)||(nt(),ue=e),A.bindTexture(A.TEXTURE_2D,D),A.pixelStorei(A.UNPACK_FLIP_Y_WEBGL,!1),A.pixelStorei(A.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1);try{A.texImage2D(A.TEXTURE_2D,0,A.RGBA8,A.RGBA,A.UNSIGNED_BYTE,Q),A.generateMipmap(A.TEXTURE_2D)}catch(e){Xe(e)}},it=()=>{L=!A.getExtension("EXT_color_buffer_float")&&!A.getExtension("EXT_color_buffer_half_float"),je(),V=A.createVertexArray(),S=ze(A.NEAREST),D=ze(A.LINEAR_MIPMAP_LINEAR),A.bindTexture(A.TEXTURE_2D,D),A.texImage2D(A.TEXTURE_2D,0,A.RGBA8,1,1,0,A.RGBA,A.UNSIGNED_BYTE,new Uint8Array([0,0,0,255])),A.generateMipmap(A.TEXTURE_2D),z=O=0,Y=0,ot(),Ze(),Q&&rt()};function ot(){const e=t.clientWidth||t.clientHeight?{width:t.clientWidth,height:t.clientHeight}:t.getBoundingClientRect(),n=Math.min(globalThis.devicePixelRatio||1,v.maxPixelRatio),r=Math.max(1,Math.round(e.width*n)),i=Math.max(1,Math.round(e.height*n));if(r===z&&i===O&&n===W)return!1;const o=!z||Math.abs(r/i-z/O)>.01;return W=n,z=r,O=i,t.width=z,t.height=O,Y!==W&&Je(),(o||We()&&!B.length&&!N)&&$e(),he=!0,!0}let at=null,lt=[0,0,0],ut=[1,1,1],st=[0,0,0,0],ct=E(1,1,1,1,"cover"),ft=null,ht=0,dt=0,mt=0,vt=0;const gt=()=>{at!==v&&(at=v,lt=y(v.ink),ut=y("auto"===v.highlight?"#ffffff":v.highlight),st="transparent"===v.background?[0,0,0,0]:[...y(v.background),1]),ft===v&&ht===z&&dt===O&&mt===ee&&vt===te||(ft=v,ht=z,dt=O,mt=ee,vt=te,ct=E(z,O,ee||1,te||1,v.fit,v.focus));const e=v.fluid,t=Q&&ie?q.height/O*ie*ct.scale[1]:1,n=(()=>{const e=Math.ceil(z/q.width),t=Math.ceil(O/q.height);if(P&&P.columns===e&&P.rows===t)return P;Oe();const n=()=>{const n=ze(A.NEAREST);return A.texStorage2D(A.TEXTURE_2D,1,L?A.RGBA8:A.RGBA16F,e,t),n},r=n(),i=n(),o=A.createFramebuffer();return A.bindFramebuffer(A.FRAMEBUFFER,o),A.framebufferTexture2D(A.FRAMEBUFFER,A.COLOR_ATTACHMENT0,A.TEXTURE_2D,r,0),A.framebufferTexture2D(A.FRAMEBUFFER,A.COLOR_ATTACHMENT1,A.TEXTURE_2D,i,0),A.drawBuffers([A.COLOR_ATTACHMENT0,A.COLOR_ATTACHMENT1]),P={ink:r,under:i,buffer:o,columns:e,rows:t}})(),r=k.locations,i=C.locations,o=w(v),a=!Ue();A.bindFramebuffer(A.FRAMEBUFFER,n.buffer),A.viewport(0,0,n.columns,n.rows),A.useProgram(k.program),A.activeTexture(A.TEXTURE0),A.bindTexture(A.TEXTURE_2D,D),A.uniform1i(r.uSource,0),A.activeTexture(A.TEXTURE2),A.bindTexture(A.TEXTURE_2D,(N?X?.velocity:B[J])??D),A.uniform1i(r.uFluid,2),A.activeTexture(A.TEXTURE3),A.bindTexture(A.TEXTURE_2D,_e??D),A.uniform1i(r.uTrailMap,3),A.uniform1f(r.uFluidMode,N?1:0),A.uniform1i(r.uHoverMode,"fluid"===o?1:"lantern"===o?2:"trail"===o&&Se?3:0),A.uniform1f(r.uTrailAmount,v.trail.strength),A.uniform3f(r.uLight,Fe.at?.[0]??-1,Fe.at?.[1]??-1,"lantern"===o?Fe.strength:0),A.uniform3fv(r.uTrail,Ae),A.uniform1f(r.uLightRadius,v.lantern.radius),A.uniform1f(r.uLightGain,v.lantern.strength),A.uniform1f(r.uClock,performance.now()/1e3),A.uniform1f(r.uFlicker,a?1:0),A.uniform2f(r.uFluidTexel,1/Math.max(1,j),1/Math.max(1,K)),A.uniform2f(r.uResolution,z,O),A.uniform2f(r.uCell,q.width,q.height),A.uniform1f(r.uCount,$),A.uniform4f(r.uSourceMap,ct.scale[0],ct.scale[1],ct.offset[0],ct.offset[1]),A.uniform1i(r.uScene,Q?0:"flow"===v.scene?1:"orb"===v.scene?2:"drift"===v.scene?4:3),A.uniform1i(r.uMark,"dot"===v.mark?1:0),A.uniform1f(r.uScale,v.scale);const l=v.clear;A.uniform4f(r.uClear,l?l.x:0,l?l.y:0,l?l.width:0,l?l.height:0),A.uniform1f(r.uClearFeather,l?l.feather:.1),A.uniform1f(r.uClearShape,l&&"rect"===l.shape?1:0);const u=performance.now();Me.fill(0);for(let e=0;e<we.length;e++){const t=we[e],n=4*e;Me[n]=t.x,Me[n+1]=t.y,Me[n+2]=(u-t.born)/1e3,Me[n+3]=t.strength}A.uniform4fv(r.uPulse,Me),A.uniform3fv(r.uInk,lt),A.uniform3fv(r.uHighlight,ut),A.uniform1f(r.uHighlightAuto,"auto"===v.highlight?1:0),A.uniform4fv(r.uBackground,st),A.uniform1f(r.uBrightness,v.brightness),A.uniform1f(r.uContrast,v.contrast),A.uniform1f(r.uGamma,v.gamma),A.uniform1f(r.uInvert,v.invert?1:0),A.uniform1f(r.uVignette,v.vignette),A.uniform1f(r.uSourceColor,"source"===v.color?1:0),A.uniform1f(r.uRefraction,e?e.refraction*(a?1:.25):0),A.uniform1f(r.uGlow,e?e.glow:0),A.uniform1f(r.uTime,fe),A.uniform1f(r.uDrift,a?v.drift:0),A.uniform1f(r.uFluidOn,e&&"fluid"===o&&(B.length||N)?1:0),A.uniform1f(r.uLod,Math.max(0,Math.log2(Math.max(1,t))-.5)),A.uniform1f(r.uSaturation,v.saturation),A.uniform1f(r.uShade,v.shade),A.uniform1f(r.uBackdrop,Q?v.backdrop:0),A.uniform2f(r.uLevels,Q&&"auto"===v.levels?le[0]:0,Q&&"auto"===v.levels?le[1]:1),A.bindVertexArray(V),A.drawArrays(A.TRIANGLES,0,3),A.bindFramebuffer(A.FRAMEBUFFER,null),A.viewport(0,0,z,O),A.useProgram(C.program),A.activeTexture(A.TEXTURE1),A.bindTexture(A.TEXTURE_2D,S),A.uniform1i(i.uAtlas,1),A.activeTexture(A.TEXTURE4),A.bindTexture(A.TEXTURE_2D,n.ink),A.uniform1i(i.uCells,4),A.activeTexture(A.TEXTURE5),A.bindTexture(A.TEXTURE_2D,n.under),A.uniform1i(i.uUnder,5),A.uniform2f(i.uResolution,z,O),A.uniform2f(i.uCell,q.width,q.height),A.uniform1f(i.uColumns,q.columns),A.uniform1f(i.uDotScale,v.dotScale),A.uniform1i(i.uMark,"dot"===v.mark?1:0),A.drawArrays(A.TRIANGLES,0,3)},pt=()=>!de&&!me&&!ve&&(pe||Ie)&&!("undefined"!=typeof document&&document.hidden),xt=e=>{if(se=0,!pt())return;const t=(e-ce)/1e3,n=Math.min(1/30,Math.max(1/480,t));ce=e,Te=.9*Te+.1*Math.min(t,.25),ye=Te>1/24?ye+1:0;try{ye>45&&be<2&&(ye=0,be++,1===be&&W>1?(v={...v,maxPixelRatio:1},ot()):be=2);const t=He(),r=!(ge||be>=2&&!(Q instanceof HTMLVideoElement)||(!(Q instanceof HTMLVideoElement)||Q.paused||Q.ended)&&(Ue()||!(!Q&&"none"!==v.scene||v.drift>0)));r&&(fe+=n*v.speed),Q instanceof HTMLVideoElement&&Q.readyState>=2&&oe&&(oe=!("requestVideoFrameCallback"in Q),rt(),he=!0);const i=w(v);let o="fluid"===i&&(null!==Ee||e<ke);if(o?((e,t)=>{const n=v.fluid;if(!n||!I.length&&!N)return;if(N?!X?.ready():!U||!Ge(U))return;const r=z/O,i=Ee;if(Ee=null,N&&X){const o=5200*n.force*(Ue()?.35:1);return i&&(ke=t+1e3*Math.min(9,6/Math.max(.3,n.dissipation))),void X.step(i?{from:i.from,to:i.to,force:[(i.to[0]-i.from[0])*o,(i.to[1]-i.from[1])*o]}:null,{dt:Math.min(e,1/60),radius:1.1*n.radius,curl:n.curl,dissipation:n.dissipation,aspect:r})}let o=0,a=0;if(i){const e=(Ue()?1:3)*n.force;o=(i.to[0]-i.from[0])*r*e*4,a=(i.to[1]-i.from[1])*e*4,ke=t+7/Math.min(n.dissipation,1.1*n.dissipation)*1e3}const l=U.locations;A.bindFramebuffer(A.FRAMEBUFFER,I[1-J]),A.viewport(0,0,j,K),A.useProgram(U.program),A.activeTexture(A.TEXTURE0),A.bindTexture(A.TEXTURE_2D,B[J]),A.uniform1i(l.uPrevious,0),A.uniform2f(l.uSize,j,K),A.uniform1f(l.uAspect,r),A.uniform1f(l.uDt,e),A.uniform1f(l.uDecay,n.dissipation),A.uniform1f(l.uEnergyDecay,1.1*n.dissipation),A.uniform1f(l.uRadius,n.radius),A.uniform1f(l.uSplat,i?1:0),A.uniform2f(l.uFrom,i?.from[0]??0,i?.from[1]??0),A.uniform2f(l.uTo,i?.to[0]??0,i?.to[1]??0),A.uniform2f(l.uForce,o,a),A.drawArrays(A.TRIANGLES,0,3),J=1-J})(n,e):Ee=null,"lantern"===i&&(o=((e,t)=>{const n=v.lantern,r=Fe.target;if(r){const i=Fe.at;if(i)if(Ue())i[0]=r[0],i[1]=r[1];else{const t=1-Math.exp(-n.follow*e);i[0]+=(r[0]-i[0])*t,i[1]+=(r[1]-i[1])*t}else Fe.at=[r[0],r[1]];const o=Fe.at,a=Fe.trail[Fe.trail.length-1];n.trail>0&&(!a||t-a.born>28&&Math.hypot(o[0]-a.x,o[1]-a.y)>.004)&&(Fe.trail.push({x:o[0],y:o[1],born:t}),Fe.trail.length>12&&Fe.trail.shift())}Fe.strength+=((r?1:0)-Fe.strength)*(1-Math.exp(-e*(r?9:5)));const i=Fe.trail;for(let e=i.length-1;e>=0;e--)n.trail>0&&t-i[e].born<4e3*n.trail||i.splice(e,1);Ae.fill(0);for(let e=0;e<i.length;e++){const r=i[e];Ae[3*e]=r.x,Ae[3*e+1]=r.y,Ae[3*e+2]=.55*Math.exp(-(t-r.born)/1e3/Math.max(.01,n.trail))}return Fe.strength>.003||i.length>0})(n,e)||o),"trail"===i){const e=!!Le?.active;o=(e=>{const t=De();return!(!t||!t.active||(t.step(e),A.bindTexture(A.TEXTURE_2D,_e),A.texSubImage2D(A.TEXTURE_2D,0,0,0,t.width,t.height,A.RGBA,A.UNSIGNED_BYTE,t.pixels),Se=!0,0))})(n)||o,e&&!Le?.active&&(he=!0)}if(we.length){for(let t=we.length-1;t>=0;t--)e-we[t].born>=2400&&we.splice(t,1);o=!0,we.length||(he=!0)}t&&(r||o||he)&&(gt(),he=!1,!Ie||ne&&!Q||(Ie=!1,Pe())),(r||o||Ee||!t)&&(se=requestAnimationFrame(xt))}catch(e){ve=!0,Xe(e)}};function Et(){!se&&pt()&&(ce=performance.now(),se=requestAnimationFrame(xt))}const Tt=(e,n)=>{const r=t.getBoundingClientRect();if(!r.width||!r.height)return null;const i=(e-r.left)/r.width,o=(n-r.top)/r.height;return i<-.05||i>1.05||o<-.05||o>1.05?null:[i,1-o]},yt=e=>{const t=w(v);if("none"===t||ge)return xe=null,void(Fe.target=null);if("lantern"===t)return Fe.target=e?[e[0],1-e[1]]:null,xe=null,void Et();if("trail"===t){const t=De();if(!t)return;return e?t.move(e[0],1-e[1],v.trail.radius,performance.now()):t.leave(),xe=null,void Et()}e?(xe&&(Ee=Ee?{from:Ee.from,to:e}:{from:xe,to:e}),xe=e,Et()):xe=null},bt=e=>yt(Tt(e.clientX,e.clientY)),wt=()=>{xe=null,Le?.leave(),Fe.target&&(Fe.target=null,Et())},Mt=f.interactionTarget??window;Mt.addEventListener("pointermove",bt,{passive:!0}),Mt.addEventListener("pointerdown",bt,{passive:!0}),document.addEventListener("pointerleave",wt),Mt!==window&&Mt.addEventListener("pointerleave",wt);const Rt="function"==typeof ResizeObserver?new ResizeObserver(()=>{const e=ot();Re(),(e||he)&&Et()}):null;Rt?.observe(t),m&&Rt?.observe(m.element);const Ft="function"==typeof IntersectionObserver?new IntersectionObserver(([e])=>{pe=e.isIntersecting,pe&&(he=!0,Et())},{rootMargin:"120px"}):null;Ft?.observe(t);const At=()=>{document.hidden||(he=!0,Et())};document.addEventListener("visibilitychange",At);const Lt=()=>{he=!0,Et()};Ce?.addEventListener?.("change",Lt),Qe?.addEventListener?.("loadingdone",tt);const _t=e=>{e.preventDefault(),me=!0,se&&cancelAnimationFrame(se),se=0},St=()=>{me=!1,H.length=0,G=null,k=C=U=null,P=null,B=[],I=[],X=null,N=!1,_e=null,Le=null,Se=!1;try{it(),he=!0,Et()}catch(e){Xe(e)}};t.addEventListener("webglcontextlost",_t),t.addEventListener("webglcontextrestored",St);const Dt=()=>{Z&&(Z.pause(),Z.removeAttribute("src"),Z.load(),Z=null),ae&&Q instanceof HTMLVideoElement&&"cancelVideoFrameCallback"in Q&&Q.cancelVideoFrameCallback(ae),ae=0},kt=()=>{re?.close(),re=null},Ct=async e=>{const t=await createImageBitmap(e,{premultiplyAlpha:"none"}),n=[t.width,t.height],r=function(e,t,n,r,i,o,a=8){if(!(n>=16&&r>=16&&e>0&&t>0&&i>0))return 1;const l=a/i/("cover"===o?Math.min(e/n,t/r):Math.max(e/n,t/r));return l>.5?1:2**-Math.floor(Math.log2(1/l))}(t.width,t.height,z,O,q.height,v.fit);if(1===r)return{element:t,owned:!0,natural:n};try{return{element:await createImageBitmap(t,{resizeWidth:Math.max(1,Math.round(t.width*r)),resizeHeight:Math.max(1,Math.round(t.height*r)),resizeQuality:"medium",premultiplyAlpha:"none"}),owned:!0,natural:n}}finally{t.close()}},Ut=e=>new Promise((t,n)=>{const r=new Image;r.crossOrigin="anonymous",r.decoding="async",r.onload=()=>t({element:r,owned:!1}),r.onerror=()=>n(new Error("The glyph field could not load this image.")),r.src=e}),Pt=async e=>{const t=++ne;if(!e)return Dt(),kt(),Q=null,ee=te=ie=0,he=!0,void Et();try{const n=await(async e=>{if("string"!=typeof e)return e instanceof HTMLImageElement&&!e.complete?e.decode().then(()=>({element:e,owned:!1})):e instanceof HTMLVideoElement&&e.readyState<2?new Promise((t,n)=>{e.addEventListener("loadeddata",()=>t({element:e,owned:!1}),{once:!0}),e.addEventListener("error",()=>n(new Error("The glyph field could not load this video.")),{once:!0})}):{element:e,owned:!1};if(F.test(e))return new Promise((t,n)=>{const r=document.createElement("video");r.crossOrigin="anonymous",r.muted=!0,r.loop=!0,r.playsInline=!0,r.preload="auto",r.addEventListener("loadeddata",()=>{r.play().catch(()=>{}),t({element:r,owned:!0})},{once:!0}),r.addEventListener("error",()=>n(new Error("The glyph field could not load this video.")),{once:!0}),r.src=e});if("function"!=typeof createImageBitmap||"function"!=typeof fetch)return Ut(e);let t;try{t=await(await fetch(e).then(e=>{if(!e.ok)throw new Error("The glyph field could not load this image.");return e})).blob()}catch(t){if(t instanceof Error&&t.message.startsWith("The glyph field"))throw t;return Ut(e)}if(!/svg/i.test(t.type))try{return await Ct(t)}catch{}const n=URL.createObjectURL(t);try{return await Ut(n)}finally{URL.revokeObjectURL(n)}})(e),{element:r,owned:i}=n;if(de||t!==ne)return void(i&&r instanceof HTMLVideoElement?(r.pause(),r.removeAttribute("src"),r.load()):i&&r.close());Dt(),kt(),Z=i&&r instanceof HTMLVideoElement?r:null,!i||r instanceof HTMLVideoElement||(re=r),Q=r,ie=r instanceof HTMLVideoElement?r.videoHeight:r instanceof HTMLImageElement?r.naturalHeight:r.height,ee=n.natural?.[0]??(r instanceof HTMLVideoElement?r.videoWidth:r instanceof HTMLImageElement?r.naturalWidth:r.width),te=n.natural?.[1]??ie,oe=!0,r instanceof HTMLVideoElement&&(e=>{const t=e;if(!t.requestVideoFrameCallback)return;const n=()=>{de||Q!==e||(oe=!0,Et(),ae=t.requestVideoFrameCallback(n))};ae=t.requestVideoFrameCallback(n)})(r),me||rt(),he=!0,Et()}catch(e){Ie&&t===ne&&(Ie=!1,Pe()),Xe(e)}};it(),Re();const Bt=f.source??null;return Bt?(ne=1,Pt(Bt)):Et(),{canvas:t,ready:Be,get settings(){return v},update(e){const n=v,{element:r,rest:i}=d(e);"clear"in e&&(m&&m.element!==r?.element&&Rt?.unobserve(m.element),m=r,r&&Rt?.observe(r.element)),v=x(r?{...i,clear:v.clear}:i,v),Re(),e.label&&t.setAttribute("aria-label",e.label),v.maxPixelRatio!==n.maxPixelRatio&&ot(),v.charset===n.charset&&v.cellSize===n.cellSize&&v.cellAspect===n.cellAspect&&v.font===n.font&&v.fontWeight===n.fontWeight&&v.glyphScale===n.glyphScale||(Je(),Ze()),v.levels!==n.levels&&nt();((!!n.fluid&&"fluid"===w(n))!==We()||v.fluid&&n.fluid&&v.fluid.resolution!==n.fluid.resolution)&&$e(),"source"in e&&Pt(e.source??null),he=!0,Et()},setSource:Pt,pointer(e,n=0){if(null===e)return void yt(null);const r=t.getBoundingClientRect();yt(Tt(r.left+e,r.top+n))},redraw(){if(!(de||me||ve)&&z)try{He(!0)&&gt()}catch(e){ve=!0,Xe(e)}},pulse(e={}){if(de||Ue())return;const n=t.getBoundingClientRect();if(!n.width||!n.height)return;let r=e.x??n.width/2,i=e.y??n.height/2;if(e.element){const t=e.element.getBoundingClientRect();r=t.left+t.width/2-n.left,i=t.top+t.height/2-n.top}const o=Math.max(0,Math.min(2,e.strength??1));we=[...we,{x:r/n.width,y:i/n.height,born:performance.now(),strength:o}].slice(-4),Et()},play(){ge=!1,Q instanceof HTMLVideoElement&&Q.play().catch(()=>{}),he=!0,Et()},pause(){ge=!0,Q instanceof HTMLVideoElement&&Q.pause()},resize(){ot()&&Et()},destroy(){if(!de&&(de=!0,se&&cancelAnimationFrame(se),Mt.removeEventListener("pointermove",bt),Mt.removeEventListener("pointerdown",bt),document.removeEventListener("pointerleave",wt),Mt.removeEventListener("pointerleave",wt),document.removeEventListener("visibilitychange",At),Ce?.removeEventListener?.("change",Lt),Qe?.removeEventListener?.("loadingdone",tt),t.removeEventListener("webglcontextlost",_t),t.removeEventListener("webglcontextrestored",St),Rt?.disconnect(),Ft?.disconnect(),Dt(),kt(),Q=null,!me)){Ye(),Oe(),A.deleteTexture(S),A.deleteTexture(D);for(const e of[k,C,U])e&&A.deleteProgram(e.program);X?.dispose(),_e&&A.deleteTexture(_e),H.forEach(e=>A.deleteShader(e)),V&&A.deleteVertexArray(V)}}}},exports.normalizeFieldSettings=x;//# sourceMappingURL=field.cjs.map
package/dist/field.js CHANGED
@@ -1 +1 @@
1
- var e=e=>e?"\nconst float VSCALE=6.;\nvec4 decodeFluid(vec4 t){return vec4((t.xy*2.-1.)*VSCALE,t.z,t.w);}\nvec4 encodeFluid(vec4 v){return vec4(clamp(v.xy/VSCALE*.5+.5,0.,1.),clamp(v.z,0.,1.),1.);}":"\nvec4 decodeFluid(vec4 t){return t;}\nvec4 encodeFluid(vec4 v){return vec4(v.xyz,1.);}",t="#version 300 es\nprecision highp float;\nprecision highp sampler2D;\nuniform vec2 uTexel;\nout vec4 outColor;\nvec2 here(){return gl_FragCoord.xy*uTexel;}\n",n=t+"\nuniform sampler2D uVelocity;\nuniform vec2 uFrom, uTo, uForce;\nuniform float uAspect, uRadius;\nfloat segment(vec2 p,vec2 a,vec2 b){vec2 pa=p-a,ba=b-a;float h=clamp(dot(pa,ba)/max(dot(ba,ba),1e-8),0.,1.);return length(pa-ba*h);}\nvoid main(){\n vec2 uv=here(),scale=vec2(uAspect,1.);\n float d=segment(uv*scale,uFrom*scale,uTo*scale);\n outColor=vec4(texture(uVelocity,uv).xy+uForce*exp(-d*d/(uRadius*uRadius)),0.,1.);\n}",r=t+"\nuniform sampler2D uVelocity;\nvoid main(){\n vec2 uv=here();\n float l=texture(uVelocity,uv-vec2(uTexel.x,0.)).y,r=texture(uVelocity,uv+vec2(uTexel.x,0.)).y;\n float t=texture(uVelocity,uv+vec2(0.,uTexel.y)).x,b=texture(uVelocity,uv-vec2(0.,uTexel.y)).x;\n outColor=vec4(.5*(r-l-t+b),0.,0.,1.);\n}",i=t+"\nuniform sampler2D uVelocity, uCurl;\nuniform float uStrength, uDt;\nvoid main(){\n vec2 uv=here();\n float l=texture(uCurl,uv-vec2(uTexel.x,0.)).x,r=texture(uCurl,uv+vec2(uTexel.x,0.)).x;\n float t=texture(uCurl,uv+vec2(0.,uTexel.y)).x,b=texture(uCurl,uv-vec2(0.,uTexel.y)).x,c=texture(uCurl,uv).x;\n vec2 force=.5*vec2(abs(t)-abs(b),abs(r)-abs(l));\n force=force/(length(force)+1e-4)*uStrength*c;\n force.y=-force.y;\n outColor=vec4(clamp(texture(uVelocity,uv).xy+force*uDt,-1000.,1000.),0.,1.);\n}",o=t+"\nuniform sampler2D uVelocity;\nvoid main(){\n vec2 uv=here(),c=texture(uVelocity,uv).xy;\n float l=texture(uVelocity,uv-vec2(uTexel.x,0.)).x,r=texture(uVelocity,uv+vec2(uTexel.x,0.)).x;\n float t=texture(uVelocity,uv+vec2(0.,uTexel.y)).y,b=texture(uVelocity,uv-vec2(0.,uTexel.y)).y;\n if(uv.x-uTexel.x<0.)l=-c.x;\n if(uv.x+uTexel.x>1.)r=-c.x;\n if(uv.y+uTexel.y>1.)t=-c.y;\n if(uv.y-uTexel.y<0.)b=-c.y;\n outColor=vec4(.5*(r-l+t-b),0.,0.,1.);\n}",a=t+"\nuniform sampler2D uSource;\nuniform float uAmount;\nvoid main(){outColor=vec4(texture(uSource,here()).x*uAmount,0.,0.,1.);}",l=t+"\nuniform sampler2D uPressure, uDivergence;\nvoid main(){\n vec2 uv=here();\n float l=texture(uPressure,uv-vec2(uTexel.x,0.)).x,r=texture(uPressure,uv+vec2(uTexel.x,0.)).x;\n float t=texture(uPressure,uv+vec2(0.,uTexel.y)).x,b=texture(uPressure,uv-vec2(0.,uTexel.y)).x;\n outColor=vec4((l+r+b+t-texture(uDivergence,uv).x)*.25,0.,0.,1.);\n}",u=t+"\nuniform sampler2D uPressure, uVelocity;\nvoid main(){\n vec2 uv=here();\n float l=texture(uPressure,uv-vec2(uTexel.x,0.)).x,r=texture(uPressure,uv+vec2(uTexel.x,0.)).x;\n float t=texture(uPressure,uv+vec2(0.,uTexel.y)).x,b=texture(uPressure,uv-vec2(0.,uTexel.y)).x;\n outColor=vec4(texture(uVelocity,uv).xy-vec2(r-l,t-b),0.,1.);\n}",s=t+"\nuniform sampler2D uVelocity;\nuniform float uDt, uDissipation;\nvoid main(){\n vec2 uv=here();\n vec2 back=uv-uDt*texture(uVelocity,uv).xy*uTexel;\n outColor=vec4(texture(uVelocity,back).xy/(1.+uDissipation*uDt),0.,1.);\n}";var c=class{constructor(e,t){this.previous=null,this.peak=0,this.width=e,this.height=t;const n=e*t;this.pixels=new Uint8Array(4*n),this.signal=new Float32Array(n),this.offsetX=new Float32Array(n),this.offsetY=new Float32Array(n),this.driftX=new Float32Array(n),this.driftY=new Float32Array(n),this.u=new Float32Array(n),this.v=new Float32Array(n),this.ink=new Float32Array(n),this.a=new Float32Array(n),this.b=new Float32Array(n),this.c=new Float32Array(n),this.pressure=new Float32Array(n),this.pressureNext=new Float32Array(n),this.divergence=new Float32Array(n),this.curl=new Float32Array(n)}get active(){return this.peak>2e-4}clear(){for(const e 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])e.fill(0);this.previous=null,this.peak=0}leave(){this.previous=null}move(e,t,n,r=performance.now()){if(!Number.isFinite(e+t+n+r))return;e=Math.max(0,Math.min(1,e)),t=Math.max(0,Math.min(1,t)),n=Math.max(.1,Math.min(1,n));const i=this.previous;if(this.previous={x:e,y:t,time:r},!i)return;const o=(e-i.x)*(this.width-1),a=(t-i.y)*(this.height-1),l=Math.hypot(o,a);if(l<.001)return;const u=Math.min(this.width,this.height),s=r>i.time?Math.max(1/240,(r-i.time)/1e3):1/60,c=Math.min(12,l/u/s),f=1-Math.exp(-c/2.2),h=u*(.022+.075*n)*(.65+.95*f),d=Math.min(128,Math.max(1,Math.ceil(l/(.4*h)))),m=l/h*(.32+.22*f)/d,v=o/l,g=a/l,p=-g,x=v,E=l*(2+18*f)/d;for(let n=0;n<d;n++){const r=(n+.5)/d,o=(i.x+(e-i.x)*r)*(this.width-1),a=(i.y+(t-i.y)*r)*(this.height-1),l=Math.max(1,Math.floor(o-3*h)),u=Math.min(this.width-2,Math.ceil(o+3*h)),s=Math.max(1,Math.floor(a-3*h)),c=Math.min(this.height-2,Math.ceil(a+3*h));for(let e=s;e<=c;e++)for(let t=l;t<=u;t++){const n=((t-o)*v+(e-a)*g)/h,r=((t-o)*p+(e-a)*x)/h,i=Math.exp(-n*n*.65-r*r*1.7),l=e*this.width+t;this.ink[l]=1-(1-this.ink[l])*Math.exp(-i*m);const u=1-r*r*.55,s=n*r*.7;this.u[l]=Math.max(-80,Math.min(80,this.u[l]+(v*u+p*s)*i*E)),this.v[l]=Math.max(-80,Math.min(80,this.v[l]+(g*u+x*s)*i*E))}}this.peak=1}read(e,t,n){const r=0|(t=Math.max(.5,Math.min(this.width-1.5,t))),i=0|(n=Math.max(.5,Math.min(this.height-1.5,n))),o=t-r,a=n-i,l=i*this.width+r;return(e[l]*(1-o)+e[l+1]*o)*(1-a)+(e[l+this.width]*(1-o)+e[l+this.width+1]*o)*a}sample(e,t){return this.read(this.signal,e*(this.width-1),t*(this.height-1))}displacement(e,t,n){n[0]=this.read(this.offsetX,e*(this.width-1),t*(this.height-1)),n[1]=this.read(this.offsetY,e*(this.width-1),t*(this.height-1))}step(e){if(!this.active||!Number.isFinite(e))return;let t=Math.max(0,Math.min(.05,e));const n=this.width,r=this.height;for(;t>1e-7;){const e=Math.min(1/60,t);t-=e;const i=Math.exp(-1.9*e),o=Math.exp(-3.2*e);for(let t=1;t<r-1;t++)for(let a=1;a<n-1;a++){const l=t*n+a,u=Math.max(.5,Math.min(n-1.5,a-this.u[l]*e)),s=Math.max(.5,Math.min(r-1.5,t-this.v[l]*e)),c=0|u,f=0|s,h=u-c,d=s-f,m=f*n+c,v=(1-h)*(1-d),g=h*(1-d),p=(1-h)*d,x=h*d;this.a[l]=(this.u[m]*v+this.u[m+1]*g+this.u[m+n]*p+this.u[m+n+1]*x)*i,this.b[l]=(this.v[m]*v+this.v[m+1]*g+this.v[m+n]*p+this.v[m+n+1]*x)*i,this.c[l]=(this.ink[m]*v+this.ink[m+1]*g+this.ink[m+n]*p+this.ink[m+n+1]*x)*o}[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 e=1;e<r-1;e++)for(let t=1;t<n-1;t++){const r=e*n+t;this.curl[r]=.5*(this.v[r+1]-this.v[r-1]-this.u[r+n]+this.u[r-n])}for(let t=1;t<r-1;t++)for(let r=1;r<n-1;r++){const i=t*n+r,o=Math.abs(this.curl[i+1])-Math.abs(this.curl[i-1]),a=Math.abs(this.curl[i+n])-Math.abs(this.curl[i-n]),l=Math.hypot(o,a)+1e-4,u=this.curl[i]*e*10,s=Math.hypot(this.u[i],this.v[i]);this.u[i]+=a/l*u,this.v[i]-=o/l*u;const c=Math.min(1,s/(Math.hypot(this.u[i],this.v[i])+1e-6));this.u[i]*=c,this.v[i]*=c,this.divergence[i]=-.5*(this.u[i+1]-this.u[i-1]+this.v[i+n]-this.v[i-n])}this.pressure.fill(0);for(let e=0;e<4;e++){for(let e=1;e<r-1;e++)for(let t=1;t<n-1;t++){const r=e*n+t;this.pressureNext[r]=.25*(this.divergence[r]+this.pressure[r-1]+this.pressure[r+1]+this.pressure[r-n]+this.pressure[r+n])}[this.pressure,this.pressureNext]=[this.pressureNext,this.pressure]}let a=0;for(let t=1;t<r-1;t++)for(let r=1;r<n-1;r++){const i=t*n+r;this.u[i]=Math.max(-80,Math.min(80,this.u[i]-.5*(this.pressure[i+1]-this.pressure[i-1]))),this.v[i]=Math.max(-80,Math.min(80,this.v[i]-.5*(this.pressure[i+n]-this.pressure[i-n]))),this.driftX[i]+=(3.6*this.u[i]-58*this.offsetX[i]-12*this.driftX[i])*e,this.driftY[i]+=(3.6*this.v[i]-58*this.offsetY[i]-12*this.driftY[i])*e,this.offsetX[i]=Math.max(-2,Math.min(2,this.offsetX[i]+this.driftX[i]*e)),this.offsetY[i]=Math.max(-2,Math.min(2,this.offsetY[i]+this.driftY[i]*e)),this.signal[i]=1-Math.exp(.06*-Math.hypot(this.u[i],this.v[i])-.16*this.ink[i]),a=Math.max(a,this.signal[i])}this.peak=a}if(this.active)for(let e=0;e<this.ink.length;e++){const t=Math.round(65535*Math.min(1,this.signal[e]));this.pixels[4*e]=t>>>8,this.pixels[4*e+1]=255&t,this.pixels[4*e+2]=Math.round(128+63.5*this.offsetX[e]),this.pixels[4*e+3]=Math.round(128+63.5*this.offsetY[e])}else this.clear()}},f=[{value:"fluid",label:"Fluid",description:"An ink-in-water wake that bends the image and swirls the characters."},{value:"lantern",label:"Lantern",description:"A soft light follows the pointer and lifts the characters it passes, without distortion."},{value:"trail",label:"Trail",description:"The Asciify Trail: in the wake, sparse characters fill in and dense ones open up, then settle. No distortion."},{value:"none",label:"Off",description:"The field stays still under the pointer."}],h={radius:.13,strength:.5,trail:.45,follow:12},d={strength:.55,radius:.45},m={radius:.075,force:1,dissipation:1.6,refraction:.03,glow:.55,resolution:128,curl:24},v={charset:" .,:-=+*#%@",cellSize:12,cellAspect:1,font:'ui-monospace, "SF Mono", Menlo, Consolas, monospace',fontWeight:500,glyphScale:.8,mark:"glyph",dotScale:.9,color:"ink",ink:"#f3ba32",highlight:"auto",background:"transparent",brightness:0,contrast:1,gamma:1,invert:!1,levels:"auto",saturation:1.1,shade:.5,backdrop:0,fit:"cover",focus:{x:.5,y:.5},vignette:0,drift:0,scene:"flow",scale:1,clear:!1,speed:1,hover:"fluid",fluid:m,lantern:h,trail:d,maxPixelRatio:2,respectReducedMotion:!0},g=(e,t,n,r)=>"number"==typeof e&&Number.isFinite(e)?Math.min(n,Math.max(t,e)):r,p=(e,t)=>"string"==typeof e&&/^#([\da-f]{3}|[\da-f]{6})$/i.test(e.trim())?e.trim().toLowerCase():t;function x(e){const t=Intl.Segmenter;return(t?Array.from(new t(void 0,{granularity:"grapheme"}).segment(e),e=>e.segment):Array.from(e)).filter(e=>"\n"!==e&&"\r"!==e&&"\t"!==e).slice(0,256)}function E(e={},t=v){const n="string"==typeof e.charset&&x(e.charset).length>=2?e.charset:t.charset,r=t.fluid||m,i=void 0===e.fluid?t.fluid:e.fluid,o=!1!==i&&(()=>{const e=!0!==i&&i?{...r,...i}:r;return{radius:g(e.radius,.005,.5,m.radius),force:g(e.force,0,8,m.force),dissipation:g(e.dissipation,.05,20,m.dissipation),refraction:g(e.refraction,0,.5,m.refraction),glow:g(e.glow,0,2,m.glow),resolution:Math.round(g(e.resolution,16,512,m.resolution)),curl:g(e.curl,0,80,m.curl)}})(),a="none"===e.scene||"flow"===e.scene||"orb"===e.scene||"drift"===e.scene?e.scene:t.scene,l=!!e.clear&&"object"==typeof e.clear&&("element"in e.clear||"nodeType"in e.clear),u=void 0===e.clear||l?t.clear:e.clear,s=!!u&&{x:g(u.x,-.5,1.5,.25),y:g(u.y,-.5,1.5,.3),width:g(u.width,0,2,.5),height:g(u.height,0,2,.4),feather:g(u.feather,.005,1,.12),shape:"rect"===u.shape?"rect":"ellipse"};return{charset:n,cellSize:g(e.cellSize,3,96,t.cellSize),cellAspect:g(e.cellAspect,.3,2,t.cellAspect),font:"string"==typeof e.font&&e.font.trim()?e.font:t.font,fontWeight:Math.round(g(e.fontWeight,100,900,t.fontWeight)),glyphScale:g(e.glyphScale,.3,1.6,t.glyphScale),mark:"dot"===e.mark||"glyph"===e.mark?e.mark:t.mark,dotScale:g(e.dotScale,.1,1.4,t.dotScale),color:"source"===e.color||"ink"===e.color?e.color:t.color,ink:p(e.ink,t.ink),highlight:"auto"===e.highlight?"auto":p(e.highlight,t.highlight),background:"transparent"===e.background?"transparent":p(e.background,t.background),brightness:g(e.brightness,-1,1,t.brightness),contrast:g(e.contrast,0,4,t.contrast),gamma:g(e.gamma,.2,5,t.gamma),invert:"boolean"==typeof e.invert?e.invert:t.invert,levels:"none"===e.levels||"auto"===e.levels?e.levels:t.levels,saturation:g(e.saturation,0,3,t.saturation),shade:g(e.shade,0,1,t.shade),backdrop:g(e.backdrop,0,1,t.backdrop),fit:"contain"===e.fit||"cover"===e.fit?e.fit:t.fit,focus:{x:g(e.focus?.x,0,1,t.focus.x),y:g(e.focus?.y,0,1,t.focus.y)},vignette:g(e.vignette,0,1,t.vignette),drift:g(e.drift,0,2,t.drift),scene:a,scale:g(e.scale,.2,6,t.scale),clear:s,speed:g(e.speed,0,5,t.speed),hover:"fluid"===e.hover||"lantern"===e.hover||"trail"===e.hover||"none"===e.hover?e.hover:t.hover,fluid:o,lantern:{radius:g(e.lantern?.radius,.02,.6,t.lantern.radius),strength:g(e.lantern?.strength,0,1.5,t.lantern.strength),trail:g(e.lantern?.trail,0,3,t.lantern.trail),follow:g(e.lantern?.follow,1,60,t.lantern.follow)},trail:{strength:g(e.trail?.strength,0,1,t.trail.strength),radius:g(e.trail?.radius,.1,1,t.trail.radius)},maxPixelRatio:g(e.maxPixelRatio,.5,4,t.maxPixelRatio),respectReducedMotion:"boolean"==typeof e.respectReducedMotion?e.respectReducedMotion:t.respectReducedMotion}}function T(e,t,n,r,i,o={x:.5,y:.5}){if(!(e>0&&t>0&&n>0&&r>0))return{scale:[1,1],offset:[0,0]};const a=e/t,l=n/r,u=l>a,s="cover"===i?u?[a/l,1]:[1,l/a]:u?[1,l/a]:[a/l,1];return{scale:s,offset:"cover"===i?[(1-s[0])*o.x,(1-s[1])*o.y]:[.5*(1-s[0]),.5*(1-s[1])]}}function y(e,t,n,r){const i=Math.max(2,Math.round(e*n)),o=Math.max(2,Math.round(i*t)),a=Math.min(16,Math.max(1,r)),l=Math.ceil(r/a);return{width:o,height:i,columns:a,rows:l,atlasWidth:a*o,atlasHeight:l*i}}function b(e){const t=e.replace("#",""),n=3===t.length?t.split("").map(e=>e+e).join(""):t,r=Number.parseInt(n,16);return Number.isFinite(r)&&6===n.length?[(r>>16&255)/255,(r>>8&255)/255,(255&r)/255]:[0,0,0]}function w(e){const t=new Uint32Array(256);let n=0;for(let r=0;r+3<e.length;r+=4)e[r+3]<8||(t[Math.round(.2126*e[r]+.7152*e[r+1]+.0722*e[r+2])]++,n++);if(!n)return[0,1];const r=e=>{let r=0;for(let i=0;i<256;i++)if(r+=t[i],r>=n*e)return i/255;return 1},i=r(.02),o=r(.98);return o-i<.08?[0,1]:[i,o]}var M=e=>"fluid"!==e.hover||e.fluid?e.hover:"none";function R(e,t,n=24,r=.12,i="ellipse"){return e.width>0&&e.height>0&&{x:(t.left-n-e.left)/e.width,y:(t.top-n-e.top)/e.height,width:(t.width+2*n)/e.width,height:(t.height+2*n)/e.height,feather:r,shape:i}}var A,F=/\.(mp4|webm|mov|m4v|ogv)(\?|#|$)/i,L=/swiftshader|llvmpipe|softpipe|software|basic render/i;function _(){if(A)return A;if("undefined"==typeof document||void 0===globalThis.WebGL2RenderingContext)return A="none";try{const e=document.createElement("canvas").getContext("webgl2");if(!e)return A="none";const t=e.getExtension("WEBGL_debug_renderer_info"),n=String(e.getParameter(t?t.UNMASKED_RENDERER_WEBGL:e.RENDERER)??"");return e.getExtension("WEBGL_lose_context")?.loseContext(),A=L.test(n)?"software":"gpu"}catch{return A="none"}}function k(e={}){const t=_();return"gpu"===t||"software"===t&&!!e.allowSoftware}function S(t,f={}){const h=e=>"undefined"!=typeof HTMLElement&&e instanceof HTMLElement,d=e=>{const t=e.clear;return h(t)?{element:{element:t},rest:{...e,clear:void 0}}:t&&"object"==typeof t&&h(t.element)?{element:t,rest:{...e,clear:void 0}}:{element:null,rest:e}};let m=d(f).element,v=E(d(f).rest);const g=f.onError,p=t.getContext("webgl2",{alpha:!0,antialias:!1,depth:!1,stencil:!1,premultipliedAlpha:!0,powerPreference:"high-performance"});if(!p)throw new Error("WebGL2 is not available for the glyph field.");const A=p;t.setAttribute("role","img"),t.setAttribute("aria-label",f.label??"ASCII artwork"),t.dataset.renderer="glyph-field";let L=!1;const _=A.getExtension("KHR_parallel_shader_compile");let k,S,C=null,D=null,U=null,P=null,B=[],I=[],X=null,N=!1,V=null,G=null;const H=[];let z=0,O=0,W=1,Y=0,q=y(v.cellSize,v.cellAspect,1,2),$=2,j=0,K=0,J=0,Q=null,Z=null,ee=0,te=0,ne=0,re=null,ie=0,oe=!0,ae=0,le=[0,1],ue=-1/0,se=0,ce=0,fe=0,he=!0,de=!1,me=!1,ve=!1,ge=!1,pe=!0,xe=null,Ee=null,Te=1/60,ye=0,be=0,we=[];const Me=new Float32Array(16),Re=()=>{if(!m)return;const{element:e,padding:n=32,feather:r=.16,shape:i="rect"}=m,o=R(t.getBoundingClientRect(),e.getBoundingClientRect(),n,r,i),a=v.clear;JSON.stringify(a)!==JSON.stringify(o)&&(v={...v,clear:o},he=!0)},Ae={target:null,at:null,strength:0,trail:[]},Fe=new Float32Array(36);let Le=null,_e=null,ke=!1;const Se=()=>{if(!z||!O)return null;const e=z/O,t=Math.round(e>=1?128:128*e),n=Math.round(e>=1?128/e:128);return Le&&Le.width===t&&Le.height===n||(Le=new c(t,n),_e||(_e=ze(A.LINEAR)),A.bindTexture(A.TEXTURE_2D,_e),A.texImage2D(A.TEXTURE_2D,0,A.RGBA8,t,n,0,A.RGBA,A.UNSIGNED_BYTE,Le.pixels),ke=!0),Le};let Ce=0;const De="function"==typeof matchMedia?matchMedia("(prefers-reduced-motion: reduce)"):null,Ue=()=>v.respectReducedMotion&&!!De?.matches;let Pe;const Be=new Promise(e=>{Pe=e});let Ie=!0;const Xe=e=>g?.(e instanceof Error?e:new Error(String(e))),Ne=(e,t)=>{const n=A.createShader(e);return H.push(n),A.shaderSource(n,t),A.compileShader(n),n},Ve=e=>{G??(G=Ne(A.VERTEX_SHADER,"#version 300 es\nvoid main(){vec2 p=vec2((gl_VertexID<<1)&2,gl_VertexID&2);gl_Position=vec4(p*2.-1.,0.,1.);}"));const t=A.createProgram(),n=Ne(A.FRAGMENT_SHADER,e);return A.attachShader(t,G),A.attachShader(t,n),A.linkProgram(t),{program:t,locations:null,fragment:n}},Ge=(e,t=!1)=>{if(e.locations)return!0;const{program:n,fragment:r}=e;if(!t&&_&&!A.getProgramParameter(n,_.COMPLETION_STATUS_KHR))return!1;if(!A.getProgramParameter(n,A.LINK_STATUS)){if(A.isContextLost())return!1;throw new Error(A.getShaderInfoLog(r)||A.getProgramInfoLog(n)||"Glyph field program failed to link.")}const i={};for(let e=A.getProgramParameter(n,A.ACTIVE_UNIFORMS)-1;e>=0;e--){const t=A.getActiveUniform(n,e).name.replace(/\[0\]$/,"");i[t]=A.getUniformLocation(n,t)}return e.locations=i,!0},He=(e=!1)=>!!C&&!!D&&Ge(C,e)&&Ge(D,e),ze=e=>{const t=A.createTexture();return A.bindTexture(A.TEXTURE_2D,t),A.texParameteri(A.TEXTURE_2D,A.TEXTURE_MIN_FILTER,e),A.texParameteri(A.TEXTURE_2D,A.TEXTURE_MAG_FILTER,e===A.LINEAR_MIPMAP_LINEAR?A.LINEAR:e),A.texParameteri(A.TEXTURE_2D,A.TEXTURE_WRAP_S,A.CLAMP_TO_EDGE),A.texParameteri(A.TEXTURE_2D,A.TEXTURE_WRAP_T,A.CLAMP_TO_EDGE),t},Oe=()=>{P&&(A.deleteTexture(P.ink),A.deleteTexture(P.under),A.deleteFramebuffer(P.buffer),P=null)},We=()=>!!v.fluid&&"fluid"===M(v),Ye=()=>{X?.release(),N=!1,B.forEach(e=>A.deleteTexture(e)),I.forEach(e=>A.deleteFramebuffer(e)),B=[],I=[]};function qe(){U&&A.deleteProgram(U.program),U=Ve((t=>`#version 300 es\nprecision highp float;\nprecision highp sampler2D;\nuniform sampler2D uPrevious;\nuniform vec2 uSize;\nuniform float uAspect, uDt, uDecay, uEnergyDecay, uRadius, uSplat;\nuniform vec2 uFrom, uTo, uForce;\nout vec4 outColor;\n${e(t)}\nfloat segment(vec2 p,vec2 a,vec2 b){vec2 pa=p-a,ba=b-a;float h=clamp(dot(pa,ba)/max(dot(ba,ba),1e-8),0.,1.);return length(pa-ba*h);}\nvoid main(){\n vec2 uv=gl_FragCoord.xy/uSize;\n vec4 here=decodeFluid(texture(uPrevious,uv));\n vec4 state=decodeFluid(texture(uPrevious,uv-uDt*here.xy*vec2(1./uAspect,1.)));\n state.xy*=exp(-uDt*uDecay);\n state.z*=exp(-uDt*uEnergyDecay);\n if(uSplat>.5){\n vec2 scale=vec2(uAspect,1.);\n float d=segment(uv*scale,uFrom*scale,uTo*scale);\n float g=exp(-d*d/(uRadius*uRadius));\n state.xy+=uForce*g;\n state.z=min(1.,state.z+g*min(1.,length(uForce)*.5+.08));\n }\n float speed=length(state.xy);\n if(speed>5.)state.xy*=5./speed;\n outColor=encodeFluid(state);\n}`)(L)),X?.dispose(),X=L?null:function(e,t,c){const f={splat:t(n),curl:t(r),vorticity:t(i),divergence:t(o),fade:t(a),pressure:t(l),gradient:t(u),advect:t(s)},h=Object.values(f);let d=0,m=0,v=[],g=[],p=null,x=null;const E=t=>{const n=e.createTexture();e.bindTexture(e.TEXTURE_2D,n),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,t),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,t),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),e.texImage2D(e.TEXTURE_2D,0,e.RGBA16F,d,m,0,e.RGBA,e.HALF_FLOAT,null);const r=e.createFramebuffer();return e.bindFramebuffer(e.FRAMEBUFFER,r),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,n,0),e.checkFramebufferStatus(e.FRAMEBUFFER)!==e.FRAMEBUFFER_COMPLETE?(e.deleteFramebuffer(r),e.deleteTexture(n),null):(e.viewport(0,0,d,m),e.clearColor(0,0,0,1),e.clear(e.COLOR_BUFFER_BIT),{texture:n,buffer:r})},T=t=>{t&&(e.deleteTexture(t.texture),e.deleteFramebuffer(t.buffer))},y=()=>{[...v,...g,p,x].forEach(T),v=[],g=[],p=x=null},b=(t,n)=>{const r=t.locations;return e.useProgram(t.program),e.bindFramebuffer(e.FRAMEBUFFER,n.buffer),e.uniform2f(r.uTexel,1/d,1/m),r},w=(t,n,r,i)=>{e.activeTexture(e.TEXTURE0+r),e.bindTexture(e.TEXTURE_2D,i),e.uniform1i(t[n],r)},M=()=>e.drawArrays(e.TRIANGLES,0,3),R=e=>{const t=e[0];e[0]=e[1],e[1]=t};return{resize(t,n){y(),d=t,m=n;const r=[E(e.LINEAR),E(e.LINEAR),E(e.NEAREST),E(e.NEAREST),E(e.NEAREST),E(e.NEAREST)];return e.bindFramebuffer(e.FRAMEBUFFER,null),r.some(e=>!e)?(r.forEach(T),!1):(v=[r[0],r[1]],g=[r[2],r[3]],p=r[4],x=r[5],!0)},ready:()=>h.every(c),step(t,n){if(!v.length||!p||!x)return;let r;e.viewport(0,0,d,m),t&&(r=b(f.splat,v[1]),w(r,"uVelocity",0,v[0].texture),e.uniform2f(r.uFrom,t.from[0],t.from[1]),e.uniform2f(r.uTo,t.to[0],t.to[1]),e.uniform2f(r.uForce,t.force[0],t.force[1]),e.uniform1f(r.uAspect,n.aspect),e.uniform1f(r.uRadius,n.radius),M(),R(v)),n.curl>0&&(r=b(f.curl,x),w(r,"uVelocity",0,v[0].texture),M(),r=b(f.vorticity,v[1]),w(r,"uVelocity",0,v[0].texture),w(r,"uCurl",1,x.texture),e.uniform1f(r.uStrength,n.curl),e.uniform1f(r.uDt,n.dt),M(),R(v)),r=b(f.divergence,p),w(r,"uVelocity",0,v[0].texture),M(),r=b(f.fade,g[1]),w(r,"uSource",0,g[0].texture),e.uniform1f(r.uAmount,.8),M(),R(g);for(let e=0;e<20;e++)r=b(f.pressure,g[1]),w(r,"uPressure",0,g[0].texture),w(r,"uDivergence",1,p.texture),M(),R(g);r=b(f.gradient,v[1]),w(r,"uPressure",0,g[0].texture),w(r,"uVelocity",1,v[0].texture),M(),R(v),r=b(f.advect,v[1]),w(r,"uVelocity",0,v[0].texture),e.uniform1f(r.uDt,n.dt),e.uniform1f(r.uDissipation,n.dissipation),M(),R(v),e.bindFramebuffer(e.FRAMEBUFFER,null)},get velocity(){return v[0]?.texture??null},get size(){return[d,m]},release:y,dispose(){y(),h.forEach(({program:t})=>e.deleteProgram(t))}}}(A,Ve,e=>Ge(e))}const $e=()=>{Ye();const e=v.fluid;if(!(e&&We()&&z&&O))return;U||qe();const t=e.resolution,n=z/O;if(j=n>=1?t:Math.max(8,Math.round(t*n)),K=n>=1?Math.max(8,Math.round(t/n)):t,N=!!X&&X.resize(j,K),N)return void(J=0);const r=()=>{Ye();for(let e=0;e<2;e++){const e=ze(A.LINEAR);A.texImage2D(A.TEXTURE_2D,0,L?A.RGBA8:A.RGBA16F,j,K,0,A.RGBA,L?A.UNSIGNED_BYTE:A.HALF_FLOAT,null);const t=A.createFramebuffer();A.bindFramebuffer(A.FRAMEBUFFER,t),A.framebufferTexture2D(A.FRAMEBUFFER,A.COLOR_ATTACHMENT0,A.TEXTURE_2D,e,0),B.push(e),I.push(t)}return A.checkFramebufferStatus(A.FRAMEBUFFER)===A.FRAMEBUFFER_COMPLETE};if(!r()){if(L)throw new Error("The glyph field could not allocate its fluid buffers.");L=!0,je(),qe(),r()}for(const e of I)A.bindFramebuffer(A.FRAMEBUFFER,e),A.viewport(0,0,j,K),A.clearColor(L?.5:0,L?.5:0,0,1),A.clear(A.COLOR_BUFFER_BIT);A.bindFramebuffer(A.FRAMEBUFFER,null),J=0};function je(){C&&A.deleteProgram(C.program),D&&A.deleteProgram(D.program),Oe(),C=Ve((t=>`#version 300 es\nprecision highp float;\nprecision highp sampler2D;\nuniform sampler2D uSource, uFluid;\nuniform vec2 uResolution, uCell;\nuniform float uCount;\nuniform vec4 uSourceMap;\nuniform int uScene;\nuniform vec3 uInk, uHighlight;\nuniform vec4 uBackground;\nuniform float uBrightness, uContrast, uGamma, uInvert, uVignette, uSourceColor;\nuniform float uRefraction, uGlow, uTime, uDrift, uFluidOn, uLod, uSaturation, uBackdrop, uShade, uHighlightAuto, uFluidMode;\nuniform vec2 uFluidTexel;\nuniform int uHoverMode;\nuniform vec3 uLight;\nuniform vec3 uTrail[12];\nuniform float uLightRadius, uLightGain, uClock, uFlicker, uTrailAmount;\nuniform sampler2D uTrailMap;\nuniform vec2 uLevels;\nuniform int uMark;\nuniform float uScale, uClearFeather, uClearShape;\nuniform vec4 uClear;\nuniform vec4 uPulse[4];\nlayout(location=0) out vec4 outInk;\nlayout(location=1) out vec4 outUnder;\n${e(t)}\nfloat hash(vec2 p){p=fract(p*vec2(123.34,456.21));p+=dot(p,p+45.32);return fract(p.x*p.y);}\nfloat noise(vec2 p){vec2 i=floor(p),f=fract(p),u=f*f*(3.-2.*f);\n return mix(mix(hash(i),hash(i+vec2(1,0)),u.x),mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),u.x),u.y);}\nfloat fbm(vec2 p){float v=0.,a=.5;for(int i=0;i<5;i++){v+=a*noise(p);p=p*2.03+vec2(1.7,9.2);a*=.5;}return v;}\nvec3 generated(vec2 uv,float aspect){\n vec2 p=(uv-.5)*vec2(aspect,1.)*uScale;\n if(uScene==1){\n float t=uTime*.08;\n vec2 q=vec2(fbm(p*1.6+t),fbm(p*1.6-t+3.1));\n return vec3(smoothstep(.28,.86,fbm(p*1.8+q*1.6+vec2(t*.6,-t*.4))));\n }\n if(uScene==2){\n float r=.36,d=length(p),a=uTime*.25;\n vec3 l=normalize(vec3(cos(a)*.8,.55,sin(a)*.6+.6));\n if(d<r){\n vec3 n=normalize(vec3(p.x,-p.y,sqrt(r*r-d*d)));\n float diffuse=max(dot(n,l),0.),rim=pow(1.-n.z,2.5);\n float lon=atan(n.x,n.z)+uTime*.35,lat=asin(clamp(n.y,-1.,1.));\n float bands=.5+.25*sin(lat*14.)+.25*sin(lon*6.);\n return vec3(clamp(diffuse*.85+rim*.5+bands*.12,0.,1.));\n }\n return vec3(exp(-pow((d-r)*9.,2.))*.35);\n }\n if(uScene==4){\n // Drift: a few broad lobes that wander slowly. Built for halftone, where large soft shapes read best.\n float t=uTime*.045;\n vec2 q=p*1.15;\n vec2 w=vec2(fbm(q*.7+vec2(t,-t*.7)),fbm(q*.7+vec2(4.3-t*.8,t+1.7)));\n float n=fbm(q+w*1.3+vec2(t*.4,t*.25));\n return vec3(smoothstep(.36,.74,n));\n }\n return vec3(0.);\n}\nvoid main(){\n vec2 cell=floor(gl_FragCoord.xy);\n vec2 uv=(cell+.5)*uCell/uResolution;\n float aspect=uResolution.x/uResolution.y;\n vec4 fluid=vec4(0.);\n // lift: signed tone change. The solver's flow brightens one side of each eddy and opens the other into gaps.\n float lift=0.,wake=0.,bend=1.,swirl=0.,speed=0.;\n if(uFluidOn>.5){\n vec4 raw=texture(uFluid,vec2(uv.x,1.-uv.y));\n if(uFluidMode>.5){\n vec2 v=raw.xy*uFluidTexel*vec2(aspect,1.);\n fluid=vec4(v,0.,1.);\n swirl=clamp(dot(v,vec2(.4,.65))*uGlow*.32,-.65,.65);\n speed=clamp(length(v)*uGlow*.055,0.,.38);\n bend=.4;\n } else {\n fluid=decodeFluid(raw);\n wake=clamp(fluid.z*uGlow,0.,1.);\n lift=wake*.55;\n }\n }\n vec2 drift=uDrift*.006*vec2(sin(uv.y*5.3+uTime*.6),cos(uv.x*4.1-uTime*.5));\n vec2 sampleAt=uv-vec2(fluid.x/aspect,-fluid.y)*uRefraction*bend+drift;\n vec3 rgb,glow=vec3(0.);\n if(uScene==0){\n vec2 s=sampleAt*uSourceMap.xy+uSourceMap.zw;\n bool inside=!(s.x<0.||s.y<0.||s.x>1.||s.y>1.);\n rgb=inside?textureLod(uSource,s,uLod).rgb:vec3(0.);\n if(uBackdrop>0.){\n vec2 b=(uv-vec2(fluid.x/aspect,-fluid.y)*uRefraction*bend*.5)*uSourceMap.xy+uSourceMap.zw;\n glow=(b.x<0.||b.y<0.||b.x>1.||b.y>1.)?vec3(0.):textureLod(uSource,b,uLod+3.).rgb*uBackdrop;\n }\n } else rgb=generated(sampleAt,aspect);\n float tone=dot(rgb,vec3(.2126,.7152,.0722));\n tone=clamp((tone-uLevels.x)/max(uLevels.y-uLevels.x,.01),0.,1.);\n tone=clamp((tone-.5)*uContrast+.5+uBrightness,0.,1.);\n tone=pow(tone,1./uGamma);\n if(uInvert>.5)tone=1.-tone;\n float flick=0.,trailFold=0.;\n if(uHoverMode==2){\n // Lantern: a soft light and its fading tail lift the characters they pass. Nothing moves the image.\n vec2 ap=vec2(aspect,1.),d=(uv-uLight.xy)*ap;\n float r2=uLightRadius*uLightRadius,glowL=uLight.z*exp(-dot(d,d)/r2);\n for(int i=0;i<12;i++){vec2 e=(uv-uTrail[i].xy)*ap;glowL+=uTrail[i].z*exp(-dot(e,e)/(r2*.55));}\n glowL=min(glowL,1.);\n float grain=fract(sin(dot(cell,vec2(12.9898,78.233)))*43758.5453);\n lift=glowL*uLightGain*(.7+.6*grain);\n wake=glowL*.4;\n // A few letters at the light's rim step up the ramp and back, as if being retyped.\n flick=uFlicker*step(.9,fract(grain*13.7+floor(uClock*7.+grain*5.)*.37))*glowL*(1.-glowL)*4.;\n }\n if(uHoverMode==3){\n // Trail: the studio's ink field, as a 16-bit density. Sparse marks fill in, dense marks open up.\n vec4 ink=texture(uTrailMap,uv);\n float density=(ink.r*65280.+ink.g*255.)/65535.*uTrailAmount;\n float t=clamp(density/.38,0.,1.);\n trailFold=t*t*(3.-2.*t);\n }\n if(uFluidMode>.5&&uHoverMode==1){\n // Dark ground: any motion lights the characters. Lit ground: the flow lights one side of each eddy and opens the other.\n // A little cell-to-cell grain keeps the lit area a shimmer of letters rather than a solid block.\n float grain=fract(sin(dot(cell,vec2(12.9898,78.233)))*43758.5453);\n lift=mix(abs(swirl)*.5+speed,swirl+speed*.35,smoothstep(.12,.55,tone))*(.65+.7*grain);\n wake=clamp(lift*1.6,0.,1.);\n }\n tone=clamp(tone+lift,0.,1.);\n tone+=(1.-2.*tone)*trailFold;\n // Pulses: rings sent by the interface (field.pulse()), e.g. on a save or a notification.\n for(int i=0;i<4;i++){\n if(uPulse[i].w<=0.)continue;\n float dist=length((uv-uPulse[i].xy)*vec2(aspect,1.)),front=uPulse[i].z*.95;\n float ring=exp(-pow((dist-front)/.1,2.))*uPulse[i].w*exp(-uPulse[i].z*.85);\n tone=clamp(tone+ring*.85,0.,1.);\n }\n tone*=1.-uVignette*smoothstep(.35,1.05,length((uv-.5)*vec2(min(aspect,1.8),1.)*2.));\n if(uClear.z>0.){\n // Clear zone: the field fades out around content so text above it stays readable.\n vec2 half_=uClear.zw*.5*vec2(aspect,1.),q=(uv-uClear.xy-uClear.zw*.5)*vec2(aspect,1.);\n float dz;\n if(uClearShape>.5){vec2 e=abs(q)-half_;dz=length(max(e,0.))+min(max(e.x,e.y),0.);}\n else dz=(length(q/max(half_,vec2(1e-4)))-1.)*min(half_.x,half_.y);\n tone*=smoothstep(0.,uClearFeather,dz);\n }\n float index=min(uCount-1.,floor(tone*(uCount-1.)+.5)+(flick>.5&&tone>.02?1.:0.));\n vec3 hue=rgb/max(max(rgb.r,rgb.g),max(rgb.b,.03));\n hue=mix(vec3(dot(hue,vec3(.2126,.7152,.0722))),hue,uSaturation);\n vec3 ink=mix(uInk*mix(1.,.3+.7*tone,uShade),clamp(hue*(.38+.62*tone),0.,1.),uSourceColor);\n vec3 lifted=uHighlightAuto>.5?ink/max(max(ink.r,ink.g),max(ink.b,.06)):uHighlight;\n ink=mix(ink,lifted,wake*.7);\n float underAlpha=max(uBackground.a,min(1.,max(glow.r,max(glow.g,glow.b))));\n vec3 under=min(uBackground.rgb*uBackground.a+glow,vec3(underAlpha));\n // Halftone keeps the tone for the dot size; glyphs keep their atlas index, exact in 8 bits.\n outInk=vec4(ink,uMark==1?(tone>.003?tone:0.):index/255.);\n outUnder=vec4(under,underAlpha);\n}`)(L)),D=Ve("#version 300 es\nprecision highp float;\nprecision highp sampler2D;\nuniform sampler2D uAtlas, uCells, uUnder;\nuniform vec2 uResolution, uCell;\nuniform float uColumns, uDotScale;\nuniform int uMark;\nout vec4 outColor;\nvoid main(){\n vec2 frag=vec2(gl_FragCoord.x,uResolution.y-gl_FragCoord.y);\n vec2 cell=floor(frag/uCell);\n vec4 ink=texelFetch(uCells,ivec2(cell),0),under=texelFetch(uUnder,ivec2(cell),0);\n vec2 local=frag-cell*uCell;\n float coverage;\n if(uMark==1){\n // Halftone: dot area follows tone, so midtones read true; edges are antialiased to one device pixel.\n float r=sqrt(ink.a)*min(uCell.x,uCell.y)*.5*uDotScale;\n coverage=ink.a>.003?clamp(r-length(local-uCell*.5)+.5,0.,1.):0.;\n } else {\n float index=floor(ink.a*255.+.5);\n ivec2 tile=ivec2(int(mod(index,uColumns)),int(floor(index/uColumns)));\n coverage=texelFetch(uAtlas,tile*ivec2(uCell)+ivec2(local),0).a;\n }\n outColor=vec4(ink.rgb*coverage+under.rgb*(1.-coverage),coverage+under.a*(1.-coverage));\n}"),_||He(!0)}const Ke=()=>`${v.fontWeight} ${Math.max(1,q.height*v.glyphScale)}px ${v.font}`,Je=()=>{const e=x(v.charset);$=e.length,q=y(v.cellSize,v.cellAspect,W,$),Y=W;const t=document.createElement("canvas");t.width=q.atlasWidth,t.height=q.atlasHeight;const n=t.getContext("2d");n.fillStyle="#fff",n.textAlign="center",n.textBaseline="alphabetic",n.font=Ke();const r=n.measureText("M"),i=r.actualBoundingBoxAscent||q.height*v.glyphScale*.7,o=r.actualBoundingBoxDescent||0,a=(q.height+i-o)/2;e.forEach((e,t)=>{const r=t%q.columns*q.width+q.width/2,i=Math.floor(t/q.columns)*q.height+a,o=n.measureText(e).width;n.save(),n.beginPath(),n.rect(r-q.width/2,i-a,q.width,q.height),n.clip(),n.translate(r,i),o>.96*q.width&&n.scale(.96*q.width/o,1),n.fillText(e,0,0),n.restore()}),A.bindTexture(A.TEXTURE_2D,k),A.pixelStorei(A.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),A.texImage2D(A.TEXTURE_2D,0,A.RGBA8,A.RGBA,A.UNSIGNED_BYTE,t),t.width=t.height=1,he=!0},Qe="undefined"==typeof document?void 0:document.fonts,Ze=()=>{if(!Qe?.load)return;const e=Ke();Qe.load(e,x(v.charset).join("")).then(()=>{de||me||Ke()!==e||(Je(),Et())},()=>{})},et=e=>e.trim().replace(/^["']|["']$/g,"").toLowerCase(),tt=e=>{const t=e.fontfaces??[],n=v.font.split(",").map(et);t.some(e=>n.includes(et(e.family)))&&Ze()},nt=()=>{if(le=[0,1],Q&&"none"!==v.levels)try{const e=document.createElement("canvas");e.width=e.height=64;const t=e.getContext("2d",{willReadFrequently:!0});Q===re&&(t.imageSmoothingQuality="medium"),t.drawImage(Q,0,0,64,64),le=w(t.getImageData(0,0,64,64).data)}catch{le=[0,1]}},rt=()=>{if(!Q)return;const e=performance.now();Q instanceof HTMLVideoElement&&!(e-ue>1500)||(nt(),ue=e),A.bindTexture(A.TEXTURE_2D,S),A.pixelStorei(A.UNPACK_FLIP_Y_WEBGL,!1),A.pixelStorei(A.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1);try{A.texImage2D(A.TEXTURE_2D,0,A.RGBA8,A.RGBA,A.UNSIGNED_BYTE,Q),A.generateMipmap(A.TEXTURE_2D)}catch(e){Xe(e)}},it=()=>{L=!A.getExtension("EXT_color_buffer_float")&&!A.getExtension("EXT_color_buffer_half_float"),je(),V=A.createVertexArray(),k=ze(A.NEAREST),S=ze(A.LINEAR_MIPMAP_LINEAR),A.bindTexture(A.TEXTURE_2D,S),A.texImage2D(A.TEXTURE_2D,0,A.RGBA8,1,1,0,A.RGBA,A.UNSIGNED_BYTE,new Uint8Array([0,0,0,255])),A.generateMipmap(A.TEXTURE_2D),z=O=0,Y=0,ot(),Ze(),Q&&rt()};function ot(){const e=t.getBoundingClientRect(),n=Math.min(globalThis.devicePixelRatio||1,v.maxPixelRatio),r=Math.max(1,Math.round(e.width*n)),i=Math.max(1,Math.round(e.height*n));if(r===z&&i===O&&n===W)return!1;const o=!z||Math.abs(r/i-z/O)>.01;return W=n,z=r,O=i,t.width=z,t.height=O,Y!==W&&Je(),(o||We()&&!B.length&&!N)&&$e(),he=!0,!0}let at=null,lt=[0,0,0],ut=[1,1,1],st=[0,0,0,0],ct=T(1,1,1,1,"cover"),ft=null,ht=0,dt=0,mt=0,vt=0;const gt=()=>{at!==v&&(at=v,lt=b(v.ink),ut=b("auto"===v.highlight?"#ffffff":v.highlight),st="transparent"===v.background?[0,0,0,0]:[...b(v.background),1]),ft===v&&ht===z&&dt===O&&mt===ee&&vt===te||(ft=v,ht=z,dt=O,mt=ee,vt=te,ct=T(z,O,ee||1,te||1,v.fit,v.focus));const e=v.fluid,t=Q&&ie?q.height/O*ie*ct.scale[1]:1,n=(()=>{const e=Math.ceil(z/q.width),t=Math.ceil(O/q.height);if(P&&P.columns===e&&P.rows===t)return P;Oe();const n=()=>{const n=ze(A.NEAREST);return A.texStorage2D(A.TEXTURE_2D,1,L?A.RGBA8:A.RGBA16F,e,t),n},r=n(),i=n(),o=A.createFramebuffer();return A.bindFramebuffer(A.FRAMEBUFFER,o),A.framebufferTexture2D(A.FRAMEBUFFER,A.COLOR_ATTACHMENT0,A.TEXTURE_2D,r,0),A.framebufferTexture2D(A.FRAMEBUFFER,A.COLOR_ATTACHMENT1,A.TEXTURE_2D,i,0),A.drawBuffers([A.COLOR_ATTACHMENT0,A.COLOR_ATTACHMENT1]),P={ink:r,under:i,buffer:o,columns:e,rows:t}})(),r=C.locations,i=D.locations,o=M(v),a=!Ue();A.bindFramebuffer(A.FRAMEBUFFER,n.buffer),A.viewport(0,0,n.columns,n.rows),A.useProgram(C.program),A.activeTexture(A.TEXTURE0),A.bindTexture(A.TEXTURE_2D,S),A.uniform1i(r.uSource,0),A.activeTexture(A.TEXTURE2),A.bindTexture(A.TEXTURE_2D,(N?X?.velocity:B[J])??S),A.uniform1i(r.uFluid,2),A.activeTexture(A.TEXTURE3),A.bindTexture(A.TEXTURE_2D,_e??S),A.uniform1i(r.uTrailMap,3),A.uniform1f(r.uFluidMode,N?1:0),A.uniform1i(r.uHoverMode,"fluid"===o?1:"lantern"===o?2:"trail"===o&&ke?3:0),A.uniform1f(r.uTrailAmount,v.trail.strength),A.uniform3f(r.uLight,Ae.at?.[0]??-1,Ae.at?.[1]??-1,"lantern"===o?Ae.strength:0),A.uniform3fv(r.uTrail,Fe),A.uniform1f(r.uLightRadius,v.lantern.radius),A.uniform1f(r.uLightGain,v.lantern.strength),A.uniform1f(r.uClock,performance.now()/1e3),A.uniform1f(r.uFlicker,a?1:0),A.uniform2f(r.uFluidTexel,1/Math.max(1,j),1/Math.max(1,K)),A.uniform2f(r.uResolution,z,O),A.uniform2f(r.uCell,q.width,q.height),A.uniform1f(r.uCount,$),A.uniform4f(r.uSourceMap,ct.scale[0],ct.scale[1],ct.offset[0],ct.offset[1]),A.uniform1i(r.uScene,Q?0:"flow"===v.scene?1:"orb"===v.scene?2:"drift"===v.scene?4:3),A.uniform1i(r.uMark,"dot"===v.mark?1:0),A.uniform1f(r.uScale,v.scale);const l=v.clear;A.uniform4f(r.uClear,l?l.x:0,l?l.y:0,l?l.width:0,l?l.height:0),A.uniform1f(r.uClearFeather,l?l.feather:.1),A.uniform1f(r.uClearShape,l&&"rect"===l.shape?1:0);const u=performance.now();Me.fill(0);for(let e=0;e<we.length;e++){const t=we[e],n=4*e;Me[n]=t.x,Me[n+1]=t.y,Me[n+2]=(u-t.born)/1e3,Me[n+3]=t.strength}A.uniform4fv(r.uPulse,Me),A.uniform3fv(r.uInk,lt),A.uniform3fv(r.uHighlight,ut),A.uniform1f(r.uHighlightAuto,"auto"===v.highlight?1:0),A.uniform4fv(r.uBackground,st),A.uniform1f(r.uBrightness,v.brightness),A.uniform1f(r.uContrast,v.contrast),A.uniform1f(r.uGamma,v.gamma),A.uniform1f(r.uInvert,v.invert?1:0),A.uniform1f(r.uVignette,v.vignette),A.uniform1f(r.uSourceColor,"source"===v.color?1:0),A.uniform1f(r.uRefraction,e?e.refraction*(a?1:.25):0),A.uniform1f(r.uGlow,e?e.glow:0),A.uniform1f(r.uTime,fe),A.uniform1f(r.uDrift,a?v.drift:0),A.uniform1f(r.uFluidOn,e&&"fluid"===o&&(B.length||N)?1:0),A.uniform1f(r.uLod,Math.max(0,Math.log2(Math.max(1,t))-.5)),A.uniform1f(r.uSaturation,v.saturation),A.uniform1f(r.uShade,v.shade),A.uniform1f(r.uBackdrop,Q?v.backdrop:0),A.uniform2f(r.uLevels,Q&&"auto"===v.levels?le[0]:0,Q&&"auto"===v.levels?le[1]:1),A.bindVertexArray(V),A.drawArrays(A.TRIANGLES,0,3),A.bindFramebuffer(A.FRAMEBUFFER,null),A.viewport(0,0,z,O),A.useProgram(D.program),A.activeTexture(A.TEXTURE1),A.bindTexture(A.TEXTURE_2D,k),A.uniform1i(i.uAtlas,1),A.activeTexture(A.TEXTURE4),A.bindTexture(A.TEXTURE_2D,n.ink),A.uniform1i(i.uCells,4),A.activeTexture(A.TEXTURE5),A.bindTexture(A.TEXTURE_2D,n.under),A.uniform1i(i.uUnder,5),A.uniform2f(i.uResolution,z,O),A.uniform2f(i.uCell,q.width,q.height),A.uniform1f(i.uColumns,q.columns),A.uniform1f(i.uDotScale,v.dotScale),A.uniform1i(i.uMark,"dot"===v.mark?1:0),A.drawArrays(A.TRIANGLES,0,3)},pt=()=>!de&&!me&&!ve&&(pe||Ie)&&!("undefined"!=typeof document&&document.hidden),xt=e=>{if(se=0,!pt())return;const t=(e-ce)/1e3,n=Math.min(1/30,Math.max(1/480,t));ce=e,Te=.9*Te+.1*Math.min(t,.25),ye=Te>1/24?ye+1:0;try{ye>45&&be<2&&(ye=0,be++,1===be&&W>1?(v={...v,maxPixelRatio:1},ot()):be=2);const t=He(),r=!(ge||be>=2&&!(Q instanceof HTMLVideoElement)||(!(Q instanceof HTMLVideoElement)||Q.paused||Q.ended)&&(Ue()||!(!Q&&"none"!==v.scene||v.drift>0)));r&&(fe+=n*v.speed),Q instanceof HTMLVideoElement&&Q.readyState>=2&&oe&&(oe=!("requestVideoFrameCallback"in Q),rt(),he=!0);const i=M(v);let o="fluid"===i&&(null!==Ee||e<Ce);if(o?((e,t)=>{const n=v.fluid;if(!n||!I.length&&!N)return;if(N?!X?.ready():!U||!Ge(U))return;const r=z/O,i=Ee;if(Ee=null,N&&X){const o=5200*n.force*(Ue()?.35:1);return i&&(Ce=t+1e3*Math.min(9,6/Math.max(.3,n.dissipation))),void X.step(i?{from:i.from,to:i.to,force:[(i.to[0]-i.from[0])*o,(i.to[1]-i.from[1])*o]}:null,{dt:Math.min(e,1/60),radius:1.1*n.radius,curl:n.curl,dissipation:n.dissipation,aspect:r})}let o=0,a=0;if(i){const e=(Ue()?1:3)*n.force;o=(i.to[0]-i.from[0])*r*e*4,a=(i.to[1]-i.from[1])*e*4,Ce=t+7/Math.min(n.dissipation,1.1*n.dissipation)*1e3}const l=U.locations;A.bindFramebuffer(A.FRAMEBUFFER,I[1-J]),A.viewport(0,0,j,K),A.useProgram(U.program),A.activeTexture(A.TEXTURE0),A.bindTexture(A.TEXTURE_2D,B[J]),A.uniform1i(l.uPrevious,0),A.uniform2f(l.uSize,j,K),A.uniform1f(l.uAspect,r),A.uniform1f(l.uDt,e),A.uniform1f(l.uDecay,n.dissipation),A.uniform1f(l.uEnergyDecay,1.1*n.dissipation),A.uniform1f(l.uRadius,n.radius),A.uniform1f(l.uSplat,i?1:0),A.uniform2f(l.uFrom,i?.from[0]??0,i?.from[1]??0),A.uniform2f(l.uTo,i?.to[0]??0,i?.to[1]??0),A.uniform2f(l.uForce,o,a),A.drawArrays(A.TRIANGLES,0,3),J=1-J})(n,e):Ee=null,"lantern"===i&&(o=((e,t)=>{const n=v.lantern,r=Ae.target;if(r){const i=Ae.at;if(i)if(Ue())i[0]=r[0],i[1]=r[1];else{const t=1-Math.exp(-n.follow*e);i[0]+=(r[0]-i[0])*t,i[1]+=(r[1]-i[1])*t}else Ae.at=[r[0],r[1]];const o=Ae.at,a=Ae.trail[Ae.trail.length-1];n.trail>0&&(!a||t-a.born>28&&Math.hypot(o[0]-a.x,o[1]-a.y)>.004)&&(Ae.trail.push({x:o[0],y:o[1],born:t}),Ae.trail.length>12&&Ae.trail.shift())}Ae.strength+=((r?1:0)-Ae.strength)*(1-Math.exp(-e*(r?9:5)));const i=Ae.trail;for(let e=i.length-1;e>=0;e--)n.trail>0&&t-i[e].born<4e3*n.trail||i.splice(e,1);Fe.fill(0);for(let e=0;e<i.length;e++){const r=i[e];Fe[3*e]=r.x,Fe[3*e+1]=r.y,Fe[3*e+2]=.55*Math.exp(-(t-r.born)/1e3/Math.max(.01,n.trail))}return Ae.strength>.003||i.length>0})(n,e)||o),"trail"===i){const e=!!Le?.active;o=(e=>{const t=Se();return!(!t||!t.active||(t.step(e),A.bindTexture(A.TEXTURE_2D,_e),A.texSubImage2D(A.TEXTURE_2D,0,0,0,t.width,t.height,A.RGBA,A.UNSIGNED_BYTE,t.pixels),ke=!0,0))})(n)||o,e&&!Le?.active&&(he=!0)}if(we.length){for(let t=we.length-1;t>=0;t--)e-we[t].born>=2400&&we.splice(t,1);o=!0,we.length||(he=!0)}t&&(r||o||he)&&(gt(),he=!1,!Ie||ne&&!Q||(Ie=!1,Pe())),(r||o||Ee||!t)&&(se=requestAnimationFrame(xt))}catch(e){ve=!0,Xe(e)}};function Et(){!se&&pt()&&(ce=performance.now(),se=requestAnimationFrame(xt))}const Tt=(e,n)=>{const r=t.getBoundingClientRect();if(!r.width||!r.height)return null;const i=(e-r.left)/r.width,o=(n-r.top)/r.height;return i<-.05||i>1.05||o<-.05||o>1.05?null:[i,1-o]},yt=e=>{const t=M(v);if("none"===t||ge)return xe=null,void(Ae.target=null);if("lantern"===t)return Ae.target=e?[e[0],1-e[1]]:null,xe=null,void Et();if("trail"===t){const t=Se();if(!t)return;return e?t.move(e[0],1-e[1],v.trail.radius,performance.now()):t.leave(),xe=null,void Et()}e?(xe&&(Ee=Ee?{from:Ee.from,to:e}:{from:xe,to:e}),xe=e,Et()):xe=null},bt=e=>yt(Tt(e.clientX,e.clientY)),wt=()=>{xe=null,Le?.leave(),Ae.target&&(Ae.target=null,Et())},Mt=f.interactionTarget??window;Mt.addEventListener("pointermove",bt,{passive:!0}),Mt.addEventListener("pointerdown",bt,{passive:!0}),document.addEventListener("pointerleave",wt),Mt!==window&&Mt.addEventListener("pointerleave",wt);const Rt="function"==typeof ResizeObserver?new ResizeObserver(()=>{const e=ot();Re(),(e||he)&&Et()}):null;Rt?.observe(t),m&&Rt?.observe(m.element);const At="function"==typeof IntersectionObserver?new IntersectionObserver(([e])=>{pe=e.isIntersecting,pe&&(he=!0,Et())},{rootMargin:"120px"}):null;At?.observe(t);const Ft=()=>{document.hidden||(he=!0,Et())};document.addEventListener("visibilitychange",Ft);const Lt=()=>{he=!0,Et()};De?.addEventListener?.("change",Lt),Qe?.addEventListener?.("loadingdone",tt);const _t=e=>{e.preventDefault(),me=!0,se&&cancelAnimationFrame(se),se=0},kt=()=>{me=!1,H.length=0,G=null,C=D=U=null,P=null,B=[],I=[],X=null,N=!1,_e=null,Le=null,ke=!1;try{it(),he=!0,Et()}catch(e){Xe(e)}};t.addEventListener("webglcontextlost",_t),t.addEventListener("webglcontextrestored",kt);const St=()=>{Z&&(Z.pause(),Z.removeAttribute("src"),Z.load(),Z=null),ae&&Q instanceof HTMLVideoElement&&"cancelVideoFrameCallback"in Q&&Q.cancelVideoFrameCallback(ae),ae=0},Ct=()=>{re?.close(),re=null},Dt=async e=>{const t=await createImageBitmap(e,{premultiplyAlpha:"none"}),n=[t.width,t.height],r=function(e,t,n,r,i,o,a=8){if(!(n>=16&&r>=16&&e>0&&t>0&&i>0))return 1;const l=a/i/("cover"===o?Math.min(e/n,t/r):Math.max(e/n,t/r));return l>.5?1:2**-Math.floor(Math.log2(1/l))}(t.width,t.height,z,O,q.height,v.fit);if(1===r)return{element:t,owned:!0,natural:n};try{return{element:await createImageBitmap(t,{resizeWidth:Math.max(1,Math.round(t.width*r)),resizeHeight:Math.max(1,Math.round(t.height*r)),resizeQuality:"medium",premultiplyAlpha:"none"}),owned:!0,natural:n}}finally{t.close()}},Ut=e=>new Promise((t,n)=>{const r=new Image;r.crossOrigin="anonymous",r.decoding="async",r.onload=()=>t({element:r,owned:!1}),r.onerror=()=>n(new Error("The glyph field could not load this image.")),r.src=e}),Pt=async e=>{const t=++ne;if(!e)return St(),Ct(),Q=null,ee=te=ie=0,he=!0,void Et();try{const n=await(async e=>{if("string"!=typeof e)return e instanceof HTMLImageElement&&!e.complete?e.decode().then(()=>({element:e,owned:!1})):e instanceof HTMLVideoElement&&e.readyState<2?new Promise((t,n)=>{e.addEventListener("loadeddata",()=>t({element:e,owned:!1}),{once:!0}),e.addEventListener("error",()=>n(new Error("The glyph field could not load this video.")),{once:!0})}):{element:e,owned:!1};if(F.test(e))return new Promise((t,n)=>{const r=document.createElement("video");r.crossOrigin="anonymous",r.muted=!0,r.loop=!0,r.playsInline=!0,r.preload="auto",r.addEventListener("loadeddata",()=>{r.play().catch(()=>{}),t({element:r,owned:!0})},{once:!0}),r.addEventListener("error",()=>n(new Error("The glyph field could not load this video.")),{once:!0}),r.src=e});if("function"!=typeof createImageBitmap||"function"!=typeof fetch)return Ut(e);let t;try{t=await(await fetch(e).then(e=>{if(!e.ok)throw new Error("The glyph field could not load this image.");return e})).blob()}catch(t){if(t instanceof Error&&t.message.startsWith("The glyph field"))throw t;return Ut(e)}if(!/svg/i.test(t.type))try{return await Dt(t)}catch{}const n=URL.createObjectURL(t);try{return await Ut(n)}finally{URL.revokeObjectURL(n)}})(e),{element:r,owned:i}=n;if(de||t!==ne)return void(i&&r instanceof HTMLVideoElement?(r.pause(),r.removeAttribute("src"),r.load()):i&&r.close());St(),Ct(),Z=i&&r instanceof HTMLVideoElement?r:null,!i||r instanceof HTMLVideoElement||(re=r),Q=r,ie=r instanceof HTMLVideoElement?r.videoHeight:r instanceof HTMLImageElement?r.naturalHeight:r.height,ee=n.natural?.[0]??(r instanceof HTMLVideoElement?r.videoWidth:r instanceof HTMLImageElement?r.naturalWidth:r.width),te=n.natural?.[1]??ie,oe=!0,r instanceof HTMLVideoElement&&(e=>{const t=e;if(!t.requestVideoFrameCallback)return;const n=()=>{de||Q!==e||(oe=!0,Et(),ae=t.requestVideoFrameCallback(n))};ae=t.requestVideoFrameCallback(n)})(r),me||rt(),he=!0,Et()}catch(e){Ie&&t===ne&&(Ie=!1,Pe()),Xe(e)}};it(),Re();const Bt=f.source??null;return Bt?(ne=1,Pt(Bt)):Et(),{canvas:t,ready:Be,get settings(){return v},update(e){const n=v,{element:r,rest:i}=d(e);"clear"in e&&(m&&m.element!==r?.element&&Rt?.unobserve(m.element),m=r,r&&Rt?.observe(r.element)),v=E(r?{...i,clear:v.clear}:i,v),Re(),e.label&&t.setAttribute("aria-label",e.label),v.maxPixelRatio!==n.maxPixelRatio&&ot(),v.charset===n.charset&&v.cellSize===n.cellSize&&v.cellAspect===n.cellAspect&&v.font===n.font&&v.fontWeight===n.fontWeight&&v.glyphScale===n.glyphScale||(Je(),Ze()),v.levels!==n.levels&&nt();((!!n.fluid&&"fluid"===M(n))!==We()||v.fluid&&n.fluid&&v.fluid.resolution!==n.fluid.resolution)&&$e(),"source"in e&&Pt(e.source??null),he=!0,Et()},setSource:Pt,pointer(e,n=0){if(null===e)return void yt(null);const r=t.getBoundingClientRect();yt(Tt(r.left+e,r.top+n))},redraw(){if(!(de||me||ve)&&z)try{He(!0)&&gt()}catch(e){ve=!0,Xe(e)}},pulse(e={}){if(de||Ue())return;const n=t.getBoundingClientRect();if(!n.width||!n.height)return;let r=e.x??n.width/2,i=e.y??n.height/2;if(e.element){const t=e.element.getBoundingClientRect();r=t.left+t.width/2-n.left,i=t.top+t.height/2-n.top}const o=Math.max(0,Math.min(2,e.strength??1));we=[...we,{x:r/n.width,y:i/n.height,born:performance.now(),strength:o}].slice(-4),Et()},play(){ge=!1,Q instanceof HTMLVideoElement&&Q.play().catch(()=>{}),he=!0,Et()},pause(){ge=!0,Q instanceof HTMLVideoElement&&Q.pause()},resize(){ot()&&Et()},destroy(){if(!de&&(de=!0,se&&cancelAnimationFrame(se),Mt.removeEventListener("pointermove",bt),Mt.removeEventListener("pointerdown",bt),document.removeEventListener("pointerleave",wt),Mt.removeEventListener("pointerleave",wt),document.removeEventListener("visibilitychange",Ft),De?.removeEventListener?.("change",Lt),Qe?.removeEventListener?.("loadingdone",tt),t.removeEventListener("webglcontextlost",_t),t.removeEventListener("webglcontextrestored",kt),Rt?.disconnect(),At?.disconnect(),St(),Ct(),Q=null,!me)){Ye(),Oe(),A.deleteTexture(k),A.deleteTexture(S);for(const e of[C,D,U])e&&A.deleteProgram(e.program);X?.dispose(),_e&&A.deleteTexture(_e),H.forEach(e=>A.deleteShader(e)),V&&A.deleteVertexArray(V)}}}}export{m as DEFAULT_FIELD_FLUID,h as DEFAULT_FIELD_LANTERN,v as DEFAULT_FIELD_SETTINGS,d as DEFAULT_FIELD_TRAIL,f as FIELD_HOVERS,M as activeFieldHover,y as fieldCellLayout,R as fieldClearFromRects,x as fieldGlyphs,w as fieldLevels,T as fieldSourceRect,_ as glyphFieldTier,k as isGlyphFieldSupported,S as mountGlyphField,E as normalizeFieldSettings};//# sourceMappingURL=field.js.map
1
+ var e=e=>e?"\nconst float VSCALE=6.;\nvec4 decodeFluid(vec4 t){return vec4((t.xy*2.-1.)*VSCALE,t.z,t.w);}\nvec4 encodeFluid(vec4 v){return vec4(clamp(v.xy/VSCALE*.5+.5,0.,1.),clamp(v.z,0.,1.),1.);}":"\nvec4 decodeFluid(vec4 t){return t;}\nvec4 encodeFluid(vec4 v){return vec4(v.xyz,1.);}",t="#version 300 es\nprecision highp float;\nprecision highp sampler2D;\nuniform vec2 uTexel;\nout vec4 outColor;\nvec2 here(){return gl_FragCoord.xy*uTexel;}\n",n=t+"\nuniform sampler2D uVelocity;\nuniform vec2 uFrom, uTo, uForce;\nuniform float uAspect, uRadius;\nfloat segment(vec2 p,vec2 a,vec2 b){vec2 pa=p-a,ba=b-a;float h=clamp(dot(pa,ba)/max(dot(ba,ba),1e-8),0.,1.);return length(pa-ba*h);}\nvoid main(){\n vec2 uv=here(),scale=vec2(uAspect,1.);\n float d=segment(uv*scale,uFrom*scale,uTo*scale);\n outColor=vec4(texture(uVelocity,uv).xy+uForce*exp(-d*d/(uRadius*uRadius)),0.,1.);\n}",r=t+"\nuniform sampler2D uVelocity;\nvoid main(){\n vec2 uv=here();\n float l=texture(uVelocity,uv-vec2(uTexel.x,0.)).y,r=texture(uVelocity,uv+vec2(uTexel.x,0.)).y;\n float t=texture(uVelocity,uv+vec2(0.,uTexel.y)).x,b=texture(uVelocity,uv-vec2(0.,uTexel.y)).x;\n outColor=vec4(.5*(r-l-t+b),0.,0.,1.);\n}",i=t+"\nuniform sampler2D uVelocity, uCurl;\nuniform float uStrength, uDt;\nvoid main(){\n vec2 uv=here();\n float l=texture(uCurl,uv-vec2(uTexel.x,0.)).x,r=texture(uCurl,uv+vec2(uTexel.x,0.)).x;\n float t=texture(uCurl,uv+vec2(0.,uTexel.y)).x,b=texture(uCurl,uv-vec2(0.,uTexel.y)).x,c=texture(uCurl,uv).x;\n vec2 force=.5*vec2(abs(t)-abs(b),abs(r)-abs(l));\n force=force/(length(force)+1e-4)*uStrength*c;\n force.y=-force.y;\n outColor=vec4(clamp(texture(uVelocity,uv).xy+force*uDt,-1000.,1000.),0.,1.);\n}",o=t+"\nuniform sampler2D uVelocity;\nvoid main(){\n vec2 uv=here(),c=texture(uVelocity,uv).xy;\n float l=texture(uVelocity,uv-vec2(uTexel.x,0.)).x,r=texture(uVelocity,uv+vec2(uTexel.x,0.)).x;\n float t=texture(uVelocity,uv+vec2(0.,uTexel.y)).y,b=texture(uVelocity,uv-vec2(0.,uTexel.y)).y;\n if(uv.x-uTexel.x<0.)l=-c.x;\n if(uv.x+uTexel.x>1.)r=-c.x;\n if(uv.y+uTexel.y>1.)t=-c.y;\n if(uv.y-uTexel.y<0.)b=-c.y;\n outColor=vec4(.5*(r-l+t-b),0.,0.,1.);\n}",a=t+"\nuniform sampler2D uSource;\nuniform float uAmount;\nvoid main(){outColor=vec4(texture(uSource,here()).x*uAmount,0.,0.,1.);}",l=t+"\nuniform sampler2D uPressure, uDivergence;\nvoid main(){\n vec2 uv=here();\n float l=texture(uPressure,uv-vec2(uTexel.x,0.)).x,r=texture(uPressure,uv+vec2(uTexel.x,0.)).x;\n float t=texture(uPressure,uv+vec2(0.,uTexel.y)).x,b=texture(uPressure,uv-vec2(0.,uTexel.y)).x;\n outColor=vec4((l+r+b+t-texture(uDivergence,uv).x)*.25,0.,0.,1.);\n}",u=t+"\nuniform sampler2D uPressure, uVelocity;\nvoid main(){\n vec2 uv=here();\n float l=texture(uPressure,uv-vec2(uTexel.x,0.)).x,r=texture(uPressure,uv+vec2(uTexel.x,0.)).x;\n float t=texture(uPressure,uv+vec2(0.,uTexel.y)).x,b=texture(uPressure,uv-vec2(0.,uTexel.y)).x;\n outColor=vec4(texture(uVelocity,uv).xy-vec2(r-l,t-b),0.,1.);\n}",s=t+"\nuniform sampler2D uVelocity;\nuniform float uDt, uDissipation;\nvoid main(){\n vec2 uv=here();\n vec2 back=uv-uDt*texture(uVelocity,uv).xy*uTexel;\n outColor=vec4(texture(uVelocity,back).xy/(1.+uDissipation*uDt),0.,1.);\n}";var c=class{constructor(e,t){this.previous=null,this.peak=0,this.width=e,this.height=t;const n=e*t;this.pixels=new Uint8Array(4*n),this.signal=new Float32Array(n),this.offsetX=new Float32Array(n),this.offsetY=new Float32Array(n),this.driftX=new Float32Array(n),this.driftY=new Float32Array(n),this.u=new Float32Array(n),this.v=new Float32Array(n),this.ink=new Float32Array(n),this.a=new Float32Array(n),this.b=new Float32Array(n),this.c=new Float32Array(n),this.pressure=new Float32Array(n),this.pressureNext=new Float32Array(n),this.divergence=new Float32Array(n),this.curl=new Float32Array(n)}get active(){return this.peak>2e-4}clear(){for(const e 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])e.fill(0);this.previous=null,this.peak=0}leave(){this.previous=null}move(e,t,n,r=performance.now()){if(!Number.isFinite(e+t+n+r))return;e=Math.max(0,Math.min(1,e)),t=Math.max(0,Math.min(1,t)),n=Math.max(.1,Math.min(1,n));const i=this.previous;if(this.previous={x:e,y:t,time:r},!i)return;const o=(e-i.x)*(this.width-1),a=(t-i.y)*(this.height-1),l=Math.hypot(o,a);if(l<.001)return;const u=Math.min(this.width,this.height),s=r>i.time?Math.max(1/240,(r-i.time)/1e3):1/60,c=Math.min(12,l/u/s),f=1-Math.exp(-c/2.2),h=u*(.022+.075*n)*(.65+.95*f),d=Math.min(128,Math.max(1,Math.ceil(l/(.4*h)))),m=l/h*(.32+.22*f)/d,v=o/l,g=a/l,p=-g,x=v,E=l*(2+18*f)/d;for(let n=0;n<d;n++){const r=(n+.5)/d,o=(i.x+(e-i.x)*r)*(this.width-1),a=(i.y+(t-i.y)*r)*(this.height-1),l=Math.max(1,Math.floor(o-3*h)),u=Math.min(this.width-2,Math.ceil(o+3*h)),s=Math.max(1,Math.floor(a-3*h)),c=Math.min(this.height-2,Math.ceil(a+3*h));for(let e=s;e<=c;e++)for(let t=l;t<=u;t++){const n=((t-o)*v+(e-a)*g)/h,r=((t-o)*p+(e-a)*x)/h,i=Math.exp(-n*n*.65-r*r*1.7),l=e*this.width+t;this.ink[l]=1-(1-this.ink[l])*Math.exp(-i*m);const u=1-r*r*.55,s=n*r*.7;this.u[l]=Math.max(-80,Math.min(80,this.u[l]+(v*u+p*s)*i*E)),this.v[l]=Math.max(-80,Math.min(80,this.v[l]+(g*u+x*s)*i*E))}}this.peak=1}read(e,t,n){const r=0|(t=Math.max(.5,Math.min(this.width-1.5,t))),i=0|(n=Math.max(.5,Math.min(this.height-1.5,n))),o=t-r,a=n-i,l=i*this.width+r;return(e[l]*(1-o)+e[l+1]*o)*(1-a)+(e[l+this.width]*(1-o)+e[l+this.width+1]*o)*a}sample(e,t){return this.read(this.signal,e*(this.width-1),t*(this.height-1))}displacement(e,t,n){n[0]=this.read(this.offsetX,e*(this.width-1),t*(this.height-1)),n[1]=this.read(this.offsetY,e*(this.width-1),t*(this.height-1))}step(e){if(!this.active||!Number.isFinite(e))return;let t=Math.max(0,Math.min(.05,e));const n=this.width,r=this.height;for(;t>1e-7;){const e=Math.min(1/60,t);t-=e;const i=Math.exp(-1.9*e),o=Math.exp(-3.2*e);for(let t=1;t<r-1;t++)for(let a=1;a<n-1;a++){const l=t*n+a,u=Math.max(.5,Math.min(n-1.5,a-this.u[l]*e)),s=Math.max(.5,Math.min(r-1.5,t-this.v[l]*e)),c=0|u,f=0|s,h=u-c,d=s-f,m=f*n+c,v=(1-h)*(1-d),g=h*(1-d),p=(1-h)*d,x=h*d;this.a[l]=(this.u[m]*v+this.u[m+1]*g+this.u[m+n]*p+this.u[m+n+1]*x)*i,this.b[l]=(this.v[m]*v+this.v[m+1]*g+this.v[m+n]*p+this.v[m+n+1]*x)*i,this.c[l]=(this.ink[m]*v+this.ink[m+1]*g+this.ink[m+n]*p+this.ink[m+n+1]*x)*o}[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 e=1;e<r-1;e++)for(let t=1;t<n-1;t++){const r=e*n+t;this.curl[r]=.5*(this.v[r+1]-this.v[r-1]-this.u[r+n]+this.u[r-n])}for(let t=1;t<r-1;t++)for(let r=1;r<n-1;r++){const i=t*n+r,o=Math.abs(this.curl[i+1])-Math.abs(this.curl[i-1]),a=Math.abs(this.curl[i+n])-Math.abs(this.curl[i-n]),l=Math.hypot(o,a)+1e-4,u=this.curl[i]*e*10,s=Math.hypot(this.u[i],this.v[i]);this.u[i]+=a/l*u,this.v[i]-=o/l*u;const c=Math.min(1,s/(Math.hypot(this.u[i],this.v[i])+1e-6));this.u[i]*=c,this.v[i]*=c,this.divergence[i]=-.5*(this.u[i+1]-this.u[i-1]+this.v[i+n]-this.v[i-n])}this.pressure.fill(0);for(let e=0;e<4;e++){for(let e=1;e<r-1;e++)for(let t=1;t<n-1;t++){const r=e*n+t;this.pressureNext[r]=.25*(this.divergence[r]+this.pressure[r-1]+this.pressure[r+1]+this.pressure[r-n]+this.pressure[r+n])}[this.pressure,this.pressureNext]=[this.pressureNext,this.pressure]}let a=0;for(let t=1;t<r-1;t++)for(let r=1;r<n-1;r++){const i=t*n+r;this.u[i]=Math.max(-80,Math.min(80,this.u[i]-.5*(this.pressure[i+1]-this.pressure[i-1]))),this.v[i]=Math.max(-80,Math.min(80,this.v[i]-.5*(this.pressure[i+n]-this.pressure[i-n]))),this.driftX[i]+=(3.6*this.u[i]-58*this.offsetX[i]-12*this.driftX[i])*e,this.driftY[i]+=(3.6*this.v[i]-58*this.offsetY[i]-12*this.driftY[i])*e,this.offsetX[i]=Math.max(-2,Math.min(2,this.offsetX[i]+this.driftX[i]*e)),this.offsetY[i]=Math.max(-2,Math.min(2,this.offsetY[i]+this.driftY[i]*e)),this.signal[i]=1-Math.exp(.06*-Math.hypot(this.u[i],this.v[i])-.16*this.ink[i]),a=Math.max(a,this.signal[i])}this.peak=a}if(this.active)for(let e=0;e<this.ink.length;e++){const t=Math.round(65535*Math.min(1,this.signal[e]));this.pixels[4*e]=t>>>8,this.pixels[4*e+1]=255&t,this.pixels[4*e+2]=Math.round(128+63.5*this.offsetX[e]),this.pixels[4*e+3]=Math.round(128+63.5*this.offsetY[e])}else this.clear()}},f=[{value:"fluid",label:"Fluid",description:"An ink-in-water wake that bends the image and swirls the characters."},{value:"lantern",label:"Lantern",description:"A soft light follows the pointer and lifts the characters it passes, without distortion."},{value:"trail",label:"Trail",description:"The Asciify Trail: in the wake, sparse characters fill in and dense ones open up, then settle. No distortion."},{value:"none",label:"Off",description:"The field stays still under the pointer."}],h={radius:.13,strength:.5,trail:.45,follow:12},d={strength:.55,radius:.45},m={radius:.075,force:1,dissipation:1.6,refraction:.03,glow:.55,resolution:128,curl:24},v={charset:" .,:-=+*#%@",cellSize:12,cellAspect:1,font:'ui-monospace, "SF Mono", Menlo, Consolas, monospace',fontWeight:500,glyphScale:.8,mark:"glyph",dotScale:.9,color:"ink",ink:"#f3ba32",highlight:"auto",background:"transparent",brightness:0,contrast:1,gamma:1,invert:!1,levels:"auto",saturation:1.1,shade:.5,backdrop:0,fit:"cover",focus:{x:.5,y:.5},vignette:0,drift:0,scene:"flow",scale:1,clear:!1,speed:1,hover:"fluid",fluid:m,lantern:h,trail:d,maxPixelRatio:2,respectReducedMotion:!0},g=(e,t,n,r)=>"number"==typeof e&&Number.isFinite(e)?Math.min(n,Math.max(t,e)):r,p=(e,t)=>"string"==typeof e&&/^#([\da-f]{3}|[\da-f]{6})$/i.test(e.trim())?e.trim().toLowerCase():t;function x(e){const t=Intl.Segmenter;return(t?Array.from(new t(void 0,{granularity:"grapheme"}).segment(e),e=>e.segment):Array.from(e)).filter(e=>"\n"!==e&&"\r"!==e&&"\t"!==e).slice(0,256)}function E(e={},t=v){const n="string"==typeof e.charset&&x(e.charset).length>=2?e.charset:t.charset,r=t.fluid||m,i=void 0===e.fluid?t.fluid:e.fluid,o=!1!==i&&(()=>{const e=!0!==i&&i?{...r,...i}:r;return{radius:g(e.radius,.005,.5,m.radius),force:g(e.force,0,8,m.force),dissipation:g(e.dissipation,.05,20,m.dissipation),refraction:g(e.refraction,0,.5,m.refraction),glow:g(e.glow,0,2,m.glow),resolution:Math.round(g(e.resolution,16,512,m.resolution)),curl:g(e.curl,0,80,m.curl)}})(),a="none"===e.scene||"flow"===e.scene||"orb"===e.scene||"drift"===e.scene?e.scene:t.scene,l=!!e.clear&&"object"==typeof e.clear&&("element"in e.clear||"nodeType"in e.clear),u=void 0===e.clear||l?t.clear:e.clear,s=!!u&&{x:g(u.x,-.5,1.5,.25),y:g(u.y,-.5,1.5,.3),width:g(u.width,0,2,.5),height:g(u.height,0,2,.4),feather:g(u.feather,.005,1,.12),shape:"rect"===u.shape?"rect":"ellipse"};return{charset:n,cellSize:g(e.cellSize,3,96,t.cellSize),cellAspect:g(e.cellAspect,.3,2,t.cellAspect),font:"string"==typeof e.font&&e.font.trim()?e.font:t.font,fontWeight:Math.round(g(e.fontWeight,100,900,t.fontWeight)),glyphScale:g(e.glyphScale,.3,1.6,t.glyphScale),mark:"dot"===e.mark||"glyph"===e.mark?e.mark:t.mark,dotScale:g(e.dotScale,.1,1.4,t.dotScale),color:"source"===e.color||"ink"===e.color?e.color:t.color,ink:p(e.ink,t.ink),highlight:"auto"===e.highlight?"auto":p(e.highlight,t.highlight),background:"transparent"===e.background?"transparent":p(e.background,t.background),brightness:g(e.brightness,-1,1,t.brightness),contrast:g(e.contrast,0,4,t.contrast),gamma:g(e.gamma,.2,5,t.gamma),invert:"boolean"==typeof e.invert?e.invert:t.invert,levels:"none"===e.levels||"auto"===e.levels?e.levels:t.levels,saturation:g(e.saturation,0,3,t.saturation),shade:g(e.shade,0,1,t.shade),backdrop:g(e.backdrop,0,1,t.backdrop),fit:"contain"===e.fit||"cover"===e.fit?e.fit:t.fit,focus:{x:g(e.focus?.x,0,1,t.focus.x),y:g(e.focus?.y,0,1,t.focus.y)},vignette:g(e.vignette,0,1,t.vignette),drift:g(e.drift,0,2,t.drift),scene:a,scale:g(e.scale,.2,6,t.scale),clear:s,speed:g(e.speed,0,5,t.speed),hover:"fluid"===e.hover||"lantern"===e.hover||"trail"===e.hover||"none"===e.hover?e.hover:t.hover,fluid:o,lantern:{radius:g(e.lantern?.radius,.02,.6,t.lantern.radius),strength:g(e.lantern?.strength,0,1.5,t.lantern.strength),trail:g(e.lantern?.trail,0,3,t.lantern.trail),follow:g(e.lantern?.follow,1,60,t.lantern.follow)},trail:{strength:g(e.trail?.strength,0,1,t.trail.strength),radius:g(e.trail?.radius,.1,1,t.trail.radius)},maxPixelRatio:g(e.maxPixelRatio,.5,4,t.maxPixelRatio),respectReducedMotion:"boolean"==typeof e.respectReducedMotion?e.respectReducedMotion:t.respectReducedMotion}}function T(e,t,n,r,i,o={x:.5,y:.5}){if(!(e>0&&t>0&&n>0&&r>0))return{scale:[1,1],offset:[0,0]};const a=e/t,l=n/r,u=l>a,s="cover"===i?u?[a/l,1]:[1,l/a]:u?[1,l/a]:[a/l,1];return{scale:s,offset:"cover"===i?[(1-s[0])*o.x,(1-s[1])*o.y]:[.5*(1-s[0]),.5*(1-s[1])]}}function y(e,t,n,r){const i=Math.max(2,Math.round(e*n)),o=Math.max(2,Math.round(i*t)),a=Math.min(16,Math.max(1,r)),l=Math.ceil(r/a);return{width:o,height:i,columns:a,rows:l,atlasWidth:a*o,atlasHeight:l*i}}function b(e){const t=e.replace("#",""),n=3===t.length?t.split("").map(e=>e+e).join(""):t,r=Number.parseInt(n,16);return Number.isFinite(r)&&6===n.length?[(r>>16&255)/255,(r>>8&255)/255,(255&r)/255]:[0,0,0]}function w(e){const t=new Uint32Array(256);let n=0;for(let r=0;r+3<e.length;r+=4)e[r+3]<8||(t[Math.round(.2126*e[r]+.7152*e[r+1]+.0722*e[r+2])]++,n++);if(!n)return[0,1];const r=e=>{let r=0;for(let i=0;i<256;i++)if(r+=t[i],r>=n*e)return i/255;return 1},i=r(.02),o=r(.98);return o-i<.08?[0,1]:[i,o]}var M=e=>"fluid"!==e.hover||e.fluid?e.hover:"none";function R(e,t,n=24,r=.12,i="ellipse"){return e.width>0&&e.height>0&&{x:(t.left-n-e.left)/e.width,y:(t.top-n-e.top)/e.height,width:(t.width+2*n)/e.width,height:(t.height+2*n)/e.height,feather:r,shape:i}}var A,F=/\.(mp4|webm|mov|m4v|ogv)(\?|#|$)/i,L=/swiftshader|llvmpipe|softpipe|software|basic render/i;function _(){if(A)return A;if("undefined"==typeof document||void 0===globalThis.WebGL2RenderingContext)return A="none";try{const e=document.createElement("canvas").getContext("webgl2");if(!e)return A="none";const t=e.getExtension("WEBGL_debug_renderer_info"),n=String(e.getParameter(t?t.UNMASKED_RENDERER_WEBGL:e.RENDERER)??"");return e.getExtension("WEBGL_lose_context")?.loseContext(),A=L.test(n)?"software":"gpu"}catch{return A="none"}}function k(e={}){const t=_();return"gpu"===t||"software"===t&&!!e.allowSoftware}function S(t,f={}){const h=e=>"undefined"!=typeof HTMLElement&&e instanceof HTMLElement,d=e=>{const t=e.clear;return h(t)?{element:{element:t},rest:{...e,clear:void 0}}:t&&"object"==typeof t&&h(t.element)?{element:t,rest:{...e,clear:void 0}}:{element:null,rest:e}};let m=d(f).element,v=E(d(f).rest);const g=f.onError,p=t.getContext("webgl2",{alpha:!0,antialias:!1,depth:!1,stencil:!1,premultipliedAlpha:!0,powerPreference:"high-performance"});if(!p)throw new Error("WebGL2 is not available for the glyph field.");const A=p;t.setAttribute("role","img"),t.setAttribute("aria-label",f.label??"ASCII artwork"),t.dataset.renderer="glyph-field";let L=!1;const _=A.getExtension("KHR_parallel_shader_compile");let k,S,C=null,D=null,U=null,P=null,B=[],I=[],X=null,N=!1,V=null,G=null;const H=[];let z=0,O=0,W=1,Y=0,q=y(v.cellSize,v.cellAspect,1,2),$=2,j=0,K=0,J=0,Q=null,Z=null,ee=0,te=0,ne=0,re=null,ie=0,oe=!0,ae=0,le=[0,1],ue=-1/0,se=0,ce=0,fe=0,he=!0,de=!1,me=!1,ve=!1,ge=!1,pe=!0,xe=null,Ee=null,Te=1/60,ye=0,be=0,we=[];const Me=new Float32Array(16),Re=()=>{if(!m)return;const{element:e,padding:n=32,feather:r=.16,shape:i="rect"}=m,o=R(t.getBoundingClientRect(),e.getBoundingClientRect(),n,r,i),a=v.clear;JSON.stringify(a)!==JSON.stringify(o)&&(v={...v,clear:o},he=!0)},Ae={target:null,at:null,strength:0,trail:[]},Fe=new Float32Array(36);let Le=null,_e=null,ke=!1;const Se=()=>{if(!z||!O)return null;const e=z/O,t=Math.round(e>=1?128:128*e),n=Math.round(e>=1?128/e:128);return Le&&Le.width===t&&Le.height===n||(Le=new c(t,n),_e||(_e=ze(A.LINEAR)),A.bindTexture(A.TEXTURE_2D,_e),A.texImage2D(A.TEXTURE_2D,0,A.RGBA8,t,n,0,A.RGBA,A.UNSIGNED_BYTE,Le.pixels),ke=!0),Le};let Ce=0;const De="function"==typeof matchMedia?matchMedia("(prefers-reduced-motion: reduce)"):null,Ue=()=>v.respectReducedMotion&&!!De?.matches;let Pe;const Be=new Promise(e=>{Pe=e});let Ie=!0;const Xe=e=>g?.(e instanceof Error?e:new Error(String(e))),Ne=(e,t)=>{const n=A.createShader(e);return H.push(n),A.shaderSource(n,t),A.compileShader(n),n},Ve=e=>{G??(G=Ne(A.VERTEX_SHADER,"#version 300 es\nvoid main(){vec2 p=vec2((gl_VertexID<<1)&2,gl_VertexID&2);gl_Position=vec4(p*2.-1.,0.,1.);}"));const t=A.createProgram(),n=Ne(A.FRAGMENT_SHADER,e);return A.attachShader(t,G),A.attachShader(t,n),A.linkProgram(t),{program:t,locations:null,fragment:n}},Ge=(e,t=!1)=>{if(e.locations)return!0;const{program:n,fragment:r}=e;if(!t&&_&&!A.getProgramParameter(n,_.COMPLETION_STATUS_KHR))return!1;if(!A.getProgramParameter(n,A.LINK_STATUS)){if(A.isContextLost())return!1;throw new Error(A.getShaderInfoLog(r)||A.getProgramInfoLog(n)||"Glyph field program failed to link.")}const i={};for(let e=A.getProgramParameter(n,A.ACTIVE_UNIFORMS)-1;e>=0;e--){const t=A.getActiveUniform(n,e).name.replace(/\[0\]$/,"");i[t]=A.getUniformLocation(n,t)}return e.locations=i,!0},He=(e=!1)=>!!C&&!!D&&Ge(C,e)&&Ge(D,e),ze=e=>{const t=A.createTexture();return A.bindTexture(A.TEXTURE_2D,t),A.texParameteri(A.TEXTURE_2D,A.TEXTURE_MIN_FILTER,e),A.texParameteri(A.TEXTURE_2D,A.TEXTURE_MAG_FILTER,e===A.LINEAR_MIPMAP_LINEAR?A.LINEAR:e),A.texParameteri(A.TEXTURE_2D,A.TEXTURE_WRAP_S,A.CLAMP_TO_EDGE),A.texParameteri(A.TEXTURE_2D,A.TEXTURE_WRAP_T,A.CLAMP_TO_EDGE),t},Oe=()=>{P&&(A.deleteTexture(P.ink),A.deleteTexture(P.under),A.deleteFramebuffer(P.buffer),P=null)},We=()=>!!v.fluid&&"fluid"===M(v),Ye=()=>{X?.release(),N=!1,B.forEach(e=>A.deleteTexture(e)),I.forEach(e=>A.deleteFramebuffer(e)),B=[],I=[]};function qe(){U&&A.deleteProgram(U.program),U=Ve((t=>`#version 300 es\nprecision highp float;\nprecision highp sampler2D;\nuniform sampler2D uPrevious;\nuniform vec2 uSize;\nuniform float uAspect, uDt, uDecay, uEnergyDecay, uRadius, uSplat;\nuniform vec2 uFrom, uTo, uForce;\nout vec4 outColor;\n${e(t)}\nfloat segment(vec2 p,vec2 a,vec2 b){vec2 pa=p-a,ba=b-a;float h=clamp(dot(pa,ba)/max(dot(ba,ba),1e-8),0.,1.);return length(pa-ba*h);}\nvoid main(){\n vec2 uv=gl_FragCoord.xy/uSize;\n vec4 here=decodeFluid(texture(uPrevious,uv));\n vec4 state=decodeFluid(texture(uPrevious,uv-uDt*here.xy*vec2(1./uAspect,1.)));\n state.xy*=exp(-uDt*uDecay);\n state.z*=exp(-uDt*uEnergyDecay);\n if(uSplat>.5){\n vec2 scale=vec2(uAspect,1.);\n float d=segment(uv*scale,uFrom*scale,uTo*scale);\n float g=exp(-d*d/(uRadius*uRadius));\n state.xy+=uForce*g;\n state.z=min(1.,state.z+g*min(1.,length(uForce)*.5+.08));\n }\n float speed=length(state.xy);\n if(speed>5.)state.xy*=5./speed;\n outColor=encodeFluid(state);\n}`)(L)),X?.dispose(),X=L?null:function(e,t,c){const f={splat:t(n),curl:t(r),vorticity:t(i),divergence:t(o),fade:t(a),pressure:t(l),gradient:t(u),advect:t(s)},h=Object.values(f);let d=0,m=0,v=[],g=[],p=null,x=null;const E=t=>{const n=e.createTexture();e.bindTexture(e.TEXTURE_2D,n),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,t),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,t),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),e.texImage2D(e.TEXTURE_2D,0,e.RGBA16F,d,m,0,e.RGBA,e.HALF_FLOAT,null);const r=e.createFramebuffer();return e.bindFramebuffer(e.FRAMEBUFFER,r),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,n,0),e.checkFramebufferStatus(e.FRAMEBUFFER)!==e.FRAMEBUFFER_COMPLETE?(e.deleteFramebuffer(r),e.deleteTexture(n),null):(e.viewport(0,0,d,m),e.clearColor(0,0,0,1),e.clear(e.COLOR_BUFFER_BIT),{texture:n,buffer:r})},T=t=>{t&&(e.deleteTexture(t.texture),e.deleteFramebuffer(t.buffer))},y=()=>{[...v,...g,p,x].forEach(T),v=[],g=[],p=x=null},b=(t,n)=>{const r=t.locations;return e.useProgram(t.program),e.bindFramebuffer(e.FRAMEBUFFER,n.buffer),e.uniform2f(r.uTexel,1/d,1/m),r},w=(t,n,r,i)=>{e.activeTexture(e.TEXTURE0+r),e.bindTexture(e.TEXTURE_2D,i),e.uniform1i(t[n],r)},M=()=>e.drawArrays(e.TRIANGLES,0,3),R=e=>{const t=e[0];e[0]=e[1],e[1]=t};return{resize(t,n){y(),d=t,m=n;const r=[E(e.LINEAR),E(e.LINEAR),E(e.NEAREST),E(e.NEAREST),E(e.NEAREST),E(e.NEAREST)];return e.bindFramebuffer(e.FRAMEBUFFER,null),r.some(e=>!e)?(r.forEach(T),!1):(v=[r[0],r[1]],g=[r[2],r[3]],p=r[4],x=r[5],!0)},ready:()=>h.every(c),step(t,n){if(!v.length||!p||!x)return;let r;e.viewport(0,0,d,m),t&&(r=b(f.splat,v[1]),w(r,"uVelocity",0,v[0].texture),e.uniform2f(r.uFrom,t.from[0],t.from[1]),e.uniform2f(r.uTo,t.to[0],t.to[1]),e.uniform2f(r.uForce,t.force[0],t.force[1]),e.uniform1f(r.uAspect,n.aspect),e.uniform1f(r.uRadius,n.radius),M(),R(v)),n.curl>0&&(r=b(f.curl,x),w(r,"uVelocity",0,v[0].texture),M(),r=b(f.vorticity,v[1]),w(r,"uVelocity",0,v[0].texture),w(r,"uCurl",1,x.texture),e.uniform1f(r.uStrength,n.curl),e.uniform1f(r.uDt,n.dt),M(),R(v)),r=b(f.divergence,p),w(r,"uVelocity",0,v[0].texture),M(),r=b(f.fade,g[1]),w(r,"uSource",0,g[0].texture),e.uniform1f(r.uAmount,.8),M(),R(g);for(let e=0;e<20;e++)r=b(f.pressure,g[1]),w(r,"uPressure",0,g[0].texture),w(r,"uDivergence",1,p.texture),M(),R(g);r=b(f.gradient,v[1]),w(r,"uPressure",0,g[0].texture),w(r,"uVelocity",1,v[0].texture),M(),R(v),r=b(f.advect,v[1]),w(r,"uVelocity",0,v[0].texture),e.uniform1f(r.uDt,n.dt),e.uniform1f(r.uDissipation,n.dissipation),M(),R(v),e.bindFramebuffer(e.FRAMEBUFFER,null)},get velocity(){return v[0]?.texture??null},get size(){return[d,m]},release:y,dispose(){y(),h.forEach(({program:t})=>e.deleteProgram(t))}}}(A,Ve,e=>Ge(e))}const $e=()=>{Ye();const e=v.fluid;if(!(e&&We()&&z&&O))return;U||qe();const t=e.resolution,n=z/O;if(j=n>=1?t:Math.max(8,Math.round(t*n)),K=n>=1?Math.max(8,Math.round(t/n)):t,N=!!X&&X.resize(j,K),N)return void(J=0);const r=()=>{Ye();for(let e=0;e<2;e++){const e=ze(A.LINEAR);A.texImage2D(A.TEXTURE_2D,0,L?A.RGBA8:A.RGBA16F,j,K,0,A.RGBA,L?A.UNSIGNED_BYTE:A.HALF_FLOAT,null);const t=A.createFramebuffer();A.bindFramebuffer(A.FRAMEBUFFER,t),A.framebufferTexture2D(A.FRAMEBUFFER,A.COLOR_ATTACHMENT0,A.TEXTURE_2D,e,0),B.push(e),I.push(t)}return A.checkFramebufferStatus(A.FRAMEBUFFER)===A.FRAMEBUFFER_COMPLETE};if(!r()){if(L)throw new Error("The glyph field could not allocate its fluid buffers.");L=!0,je(),qe(),r()}for(const e of I)A.bindFramebuffer(A.FRAMEBUFFER,e),A.viewport(0,0,j,K),A.clearColor(L?.5:0,L?.5:0,0,1),A.clear(A.COLOR_BUFFER_BIT);A.bindFramebuffer(A.FRAMEBUFFER,null),J=0};function je(){C&&A.deleteProgram(C.program),D&&A.deleteProgram(D.program),Oe(),C=Ve((t=>`#version 300 es\nprecision highp float;\nprecision highp sampler2D;\nuniform sampler2D uSource, uFluid;\nuniform vec2 uResolution, uCell;\nuniform float uCount;\nuniform vec4 uSourceMap;\nuniform int uScene;\nuniform vec3 uInk, uHighlight;\nuniform vec4 uBackground;\nuniform float uBrightness, uContrast, uGamma, uInvert, uVignette, uSourceColor;\nuniform float uRefraction, uGlow, uTime, uDrift, uFluidOn, uLod, uSaturation, uBackdrop, uShade, uHighlightAuto, uFluidMode;\nuniform vec2 uFluidTexel;\nuniform int uHoverMode;\nuniform vec3 uLight;\nuniform vec3 uTrail[12];\nuniform float uLightRadius, uLightGain, uClock, uFlicker, uTrailAmount;\nuniform sampler2D uTrailMap;\nuniform vec2 uLevels;\nuniform int uMark;\nuniform float uScale, uClearFeather, uClearShape;\nuniform vec4 uClear;\nuniform vec4 uPulse[4];\nlayout(location=0) out vec4 outInk;\nlayout(location=1) out vec4 outUnder;\n${e(t)}\nfloat hash(vec2 p){p=fract(p*vec2(123.34,456.21));p+=dot(p,p+45.32);return fract(p.x*p.y);}\nfloat noise(vec2 p){vec2 i=floor(p),f=fract(p),u=f*f*(3.-2.*f);\n return mix(mix(hash(i),hash(i+vec2(1,0)),u.x),mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),u.x),u.y);}\nfloat fbm(vec2 p){float v=0.,a=.5;for(int i=0;i<5;i++){v+=a*noise(p);p=p*2.03+vec2(1.7,9.2);a*=.5;}return v;}\nvec3 generated(vec2 uv,float aspect){\n vec2 p=(uv-.5)*vec2(aspect,1.)*uScale;\n if(uScene==1){\n float t=uTime*.08;\n vec2 q=vec2(fbm(p*1.6+t),fbm(p*1.6-t+3.1));\n return vec3(smoothstep(.28,.86,fbm(p*1.8+q*1.6+vec2(t*.6,-t*.4))));\n }\n if(uScene==2){\n float r=.36,d=length(p),a=uTime*.25;\n vec3 l=normalize(vec3(cos(a)*.8,.55,sin(a)*.6+.6));\n if(d<r){\n vec3 n=normalize(vec3(p.x,-p.y,sqrt(r*r-d*d)));\n float diffuse=max(dot(n,l),0.),rim=pow(1.-n.z,2.5);\n float lon=atan(n.x,n.z)+uTime*.35,lat=asin(clamp(n.y,-1.,1.));\n float bands=.5+.25*sin(lat*14.)+.25*sin(lon*6.);\n return vec3(clamp(diffuse*.85+rim*.5+bands*.12,0.,1.));\n }\n return vec3(exp(-pow((d-r)*9.,2.))*.35);\n }\n if(uScene==4){\n // Drift: a few broad lobes that wander slowly. Built for halftone, where large soft shapes read best.\n float t=uTime*.045;\n vec2 q=p*1.15;\n vec2 w=vec2(fbm(q*.7+vec2(t,-t*.7)),fbm(q*.7+vec2(4.3-t*.8,t+1.7)));\n float n=fbm(q+w*1.3+vec2(t*.4,t*.25));\n return vec3(smoothstep(.36,.74,n));\n }\n return vec3(0.);\n}\nvoid main(){\n vec2 cell=floor(gl_FragCoord.xy);\n vec2 uv=(cell+.5)*uCell/uResolution;\n float aspect=uResolution.x/uResolution.y;\n vec4 fluid=vec4(0.);\n // lift: signed tone change. The solver's flow brightens one side of each eddy and opens the other into gaps.\n float lift=0.,wake=0.,bend=1.,swirl=0.,speed=0.;\n if(uFluidOn>.5){\n vec4 raw=texture(uFluid,vec2(uv.x,1.-uv.y));\n if(uFluidMode>.5){\n vec2 v=raw.xy*uFluidTexel*vec2(aspect,1.);\n fluid=vec4(v,0.,1.);\n swirl=clamp(dot(v,vec2(.4,.65))*uGlow*.32,-.65,.65);\n speed=clamp(length(v)*uGlow*.055,0.,.38);\n bend=.4;\n } else {\n fluid=decodeFluid(raw);\n wake=clamp(fluid.z*uGlow,0.,1.);\n lift=wake*.55;\n }\n }\n vec2 drift=uDrift*.006*vec2(sin(uv.y*5.3+uTime*.6),cos(uv.x*4.1-uTime*.5));\n vec2 sampleAt=uv-vec2(fluid.x/aspect,-fluid.y)*uRefraction*bend+drift;\n vec3 rgb,glow=vec3(0.);\n if(uScene==0){\n vec2 s=sampleAt*uSourceMap.xy+uSourceMap.zw;\n bool inside=!(s.x<0.||s.y<0.||s.x>1.||s.y>1.);\n rgb=inside?textureLod(uSource,s,uLod).rgb:vec3(0.);\n if(uBackdrop>0.){\n vec2 b=(uv-vec2(fluid.x/aspect,-fluid.y)*uRefraction*bend*.5)*uSourceMap.xy+uSourceMap.zw;\n glow=(b.x<0.||b.y<0.||b.x>1.||b.y>1.)?vec3(0.):textureLod(uSource,b,uLod+3.).rgb*uBackdrop;\n }\n } else rgb=generated(sampleAt,aspect);\n float tone=dot(rgb,vec3(.2126,.7152,.0722));\n tone=clamp((tone-uLevels.x)/max(uLevels.y-uLevels.x,.01),0.,1.);\n tone=clamp((tone-.5)*uContrast+.5+uBrightness,0.,1.);\n tone=pow(tone,1./uGamma);\n if(uInvert>.5)tone=1.-tone;\n float flick=0.,trailFold=0.;\n if(uHoverMode==2){\n // Lantern: a soft light and its fading tail lift the characters they pass. Nothing moves the image.\n vec2 ap=vec2(aspect,1.),d=(uv-uLight.xy)*ap;\n float r2=uLightRadius*uLightRadius,glowL=uLight.z*exp(-dot(d,d)/r2);\n for(int i=0;i<12;i++){vec2 e=(uv-uTrail[i].xy)*ap;glowL+=uTrail[i].z*exp(-dot(e,e)/(r2*.55));}\n glowL=min(glowL,1.);\n float grain=fract(sin(dot(cell,vec2(12.9898,78.233)))*43758.5453);\n lift=glowL*uLightGain*(.7+.6*grain);\n wake=glowL*.4;\n // A few letters at the light's rim step up the ramp and back, as if being retyped.\n flick=uFlicker*step(.9,fract(grain*13.7+floor(uClock*7.+grain*5.)*.37))*glowL*(1.-glowL)*4.;\n }\n if(uHoverMode==3){\n // Trail: the studio's ink field, as a 16-bit density. Sparse marks fill in, dense marks open up.\n vec4 ink=texture(uTrailMap,uv);\n float density=(ink.r*65280.+ink.g*255.)/65535.*uTrailAmount;\n float t=clamp(density/.38,0.,1.);\n trailFold=t*t*(3.-2.*t);\n }\n if(uFluidMode>.5&&uHoverMode==1){\n // Dark ground: any motion lights the characters. Lit ground: the flow lights one side of each eddy and opens the other.\n // A little cell-to-cell grain keeps the lit area a shimmer of letters rather than a solid block.\n float grain=fract(sin(dot(cell,vec2(12.9898,78.233)))*43758.5453);\n lift=mix(abs(swirl)*.5+speed,swirl+speed*.35,smoothstep(.12,.55,tone))*(.65+.7*grain);\n wake=clamp(lift*1.6,0.,1.);\n }\n tone=clamp(tone+lift,0.,1.);\n tone+=(1.-2.*tone)*trailFold;\n // Pulses: rings sent by the interface (field.pulse()), e.g. on a save or a notification.\n for(int i=0;i<4;i++){\n if(uPulse[i].w<=0.)continue;\n float dist=length((uv-uPulse[i].xy)*vec2(aspect,1.)),front=uPulse[i].z*.95;\n float ring=exp(-pow((dist-front)/.1,2.))*uPulse[i].w*exp(-uPulse[i].z*.85);\n tone=clamp(tone+ring*.85,0.,1.);\n }\n tone*=1.-uVignette*smoothstep(.35,1.05,length((uv-.5)*vec2(min(aspect,1.8),1.)*2.));\n if(uClear.z>0.){\n // Clear zone: the field fades out around content so text above it stays readable.\n vec2 half_=uClear.zw*.5*vec2(aspect,1.),q=(uv-uClear.xy-uClear.zw*.5)*vec2(aspect,1.);\n float dz;\n if(uClearShape>.5){vec2 e=abs(q)-half_;dz=length(max(e,0.))+min(max(e.x,e.y),0.);}\n else dz=(length(q/max(half_,vec2(1e-4)))-1.)*min(half_.x,half_.y);\n tone*=smoothstep(0.,uClearFeather,dz);\n }\n float index=min(uCount-1.,floor(tone*(uCount-1.)+.5)+(flick>.5&&tone>.02?1.:0.));\n vec3 hue=rgb/max(max(rgb.r,rgb.g),max(rgb.b,.03));\n hue=mix(vec3(dot(hue,vec3(.2126,.7152,.0722))),hue,uSaturation);\n vec3 ink=mix(uInk*mix(1.,.3+.7*tone,uShade),clamp(hue*(.38+.62*tone),0.,1.),uSourceColor);\n vec3 lifted=uHighlightAuto>.5?ink/max(max(ink.r,ink.g),max(ink.b,.06)):uHighlight;\n ink=mix(ink,lifted,wake*.7);\n float underAlpha=max(uBackground.a,min(1.,max(glow.r,max(glow.g,glow.b))));\n vec3 under=min(uBackground.rgb*uBackground.a+glow,vec3(underAlpha));\n // Halftone keeps the tone for the dot size; glyphs keep their atlas index, exact in 8 bits.\n outInk=vec4(ink,uMark==1?(tone>.003?tone:0.):index/255.);\n outUnder=vec4(under,underAlpha);\n}`)(L)),D=Ve("#version 300 es\nprecision highp float;\nprecision highp sampler2D;\nuniform sampler2D uAtlas, uCells, uUnder;\nuniform vec2 uResolution, uCell;\nuniform float uColumns, uDotScale;\nuniform int uMark;\nout vec4 outColor;\nvoid main(){\n vec2 frag=vec2(gl_FragCoord.x,uResolution.y-gl_FragCoord.y);\n vec2 cell=floor(frag/uCell);\n vec4 ink=texelFetch(uCells,ivec2(cell),0),under=texelFetch(uUnder,ivec2(cell),0);\n vec2 local=frag-cell*uCell;\n float coverage;\n if(uMark==1){\n // Halftone: dot area follows tone, so midtones read true; edges are antialiased to one device pixel.\n float r=sqrt(ink.a)*min(uCell.x,uCell.y)*.5*uDotScale;\n coverage=ink.a>.003?clamp(r-length(local-uCell*.5)+.5,0.,1.):0.;\n } else {\n float index=floor(ink.a*255.+.5);\n ivec2 tile=ivec2(int(mod(index,uColumns)),int(floor(index/uColumns)));\n coverage=texelFetch(uAtlas,tile*ivec2(uCell)+ivec2(local),0).a;\n }\n outColor=vec4(ink.rgb*coverage+under.rgb*(1.-coverage),coverage+under.a*(1.-coverage));\n}"),_||He(!0)}const Ke=()=>`${v.fontWeight} ${Math.max(1,q.height*v.glyphScale)}px ${v.font}`,Je=()=>{const e=x(v.charset);$=e.length,q=y(v.cellSize,v.cellAspect,W,$),Y=W;const t=document.createElement("canvas");t.width=q.atlasWidth,t.height=q.atlasHeight;const n=t.getContext("2d");n.fillStyle="#fff",n.textAlign="center",n.textBaseline="alphabetic",n.font=Ke();const r=n.measureText("M"),i=r.actualBoundingBoxAscent||q.height*v.glyphScale*.7,o=r.actualBoundingBoxDescent||0,a=(q.height+i-o)/2;e.forEach((e,t)=>{const r=t%q.columns*q.width+q.width/2,i=Math.floor(t/q.columns)*q.height+a,o=n.measureText(e).width;n.save(),n.beginPath(),n.rect(r-q.width/2,i-a,q.width,q.height),n.clip(),n.translate(r,i),o>.96*q.width&&n.scale(.96*q.width/o,1),n.fillText(e,0,0),n.restore()}),A.bindTexture(A.TEXTURE_2D,k),A.pixelStorei(A.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),A.texImage2D(A.TEXTURE_2D,0,A.RGBA8,A.RGBA,A.UNSIGNED_BYTE,t),t.width=t.height=1,he=!0},Qe="undefined"==typeof document?void 0:document.fonts,Ze=()=>{if(!Qe?.load)return;const e=Ke();Qe.load(e,x(v.charset).join("")).then(()=>{de||me||Ke()!==e||(Je(),Et())},()=>{})},et=e=>e.trim().replace(/^["']|["']$/g,"").toLowerCase(),tt=e=>{const t=e.fontfaces??[],n=v.font.split(",").map(et);t.some(e=>n.includes(et(e.family)))&&Ze()},nt=()=>{if(le=[0,1],Q&&"none"!==v.levels)try{const e=document.createElement("canvas");e.width=e.height=64;const t=e.getContext("2d",{willReadFrequently:!0});Q===re&&(t.imageSmoothingQuality="medium"),t.drawImage(Q,0,0,64,64),le=w(t.getImageData(0,0,64,64).data)}catch{le=[0,1]}},rt=()=>{if(!Q)return;const e=performance.now();Q instanceof HTMLVideoElement&&!(e-ue>1500)||(nt(),ue=e),A.bindTexture(A.TEXTURE_2D,S),A.pixelStorei(A.UNPACK_FLIP_Y_WEBGL,!1),A.pixelStorei(A.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1);try{A.texImage2D(A.TEXTURE_2D,0,A.RGBA8,A.RGBA,A.UNSIGNED_BYTE,Q),A.generateMipmap(A.TEXTURE_2D)}catch(e){Xe(e)}},it=()=>{L=!A.getExtension("EXT_color_buffer_float")&&!A.getExtension("EXT_color_buffer_half_float"),je(),V=A.createVertexArray(),k=ze(A.NEAREST),S=ze(A.LINEAR_MIPMAP_LINEAR),A.bindTexture(A.TEXTURE_2D,S),A.texImage2D(A.TEXTURE_2D,0,A.RGBA8,1,1,0,A.RGBA,A.UNSIGNED_BYTE,new Uint8Array([0,0,0,255])),A.generateMipmap(A.TEXTURE_2D),z=O=0,Y=0,ot(),Ze(),Q&&rt()};function ot(){const e=t.clientWidth||t.clientHeight?{width:t.clientWidth,height:t.clientHeight}:t.getBoundingClientRect(),n=Math.min(globalThis.devicePixelRatio||1,v.maxPixelRatio),r=Math.max(1,Math.round(e.width*n)),i=Math.max(1,Math.round(e.height*n));if(r===z&&i===O&&n===W)return!1;const o=!z||Math.abs(r/i-z/O)>.01;return W=n,z=r,O=i,t.width=z,t.height=O,Y!==W&&Je(),(o||We()&&!B.length&&!N)&&$e(),he=!0,!0}let at=null,lt=[0,0,0],ut=[1,1,1],st=[0,0,0,0],ct=T(1,1,1,1,"cover"),ft=null,ht=0,dt=0,mt=0,vt=0;const gt=()=>{at!==v&&(at=v,lt=b(v.ink),ut=b("auto"===v.highlight?"#ffffff":v.highlight),st="transparent"===v.background?[0,0,0,0]:[...b(v.background),1]),ft===v&&ht===z&&dt===O&&mt===ee&&vt===te||(ft=v,ht=z,dt=O,mt=ee,vt=te,ct=T(z,O,ee||1,te||1,v.fit,v.focus));const e=v.fluid,t=Q&&ie?q.height/O*ie*ct.scale[1]:1,n=(()=>{const e=Math.ceil(z/q.width),t=Math.ceil(O/q.height);if(P&&P.columns===e&&P.rows===t)return P;Oe();const n=()=>{const n=ze(A.NEAREST);return A.texStorage2D(A.TEXTURE_2D,1,L?A.RGBA8:A.RGBA16F,e,t),n},r=n(),i=n(),o=A.createFramebuffer();return A.bindFramebuffer(A.FRAMEBUFFER,o),A.framebufferTexture2D(A.FRAMEBUFFER,A.COLOR_ATTACHMENT0,A.TEXTURE_2D,r,0),A.framebufferTexture2D(A.FRAMEBUFFER,A.COLOR_ATTACHMENT1,A.TEXTURE_2D,i,0),A.drawBuffers([A.COLOR_ATTACHMENT0,A.COLOR_ATTACHMENT1]),P={ink:r,under:i,buffer:o,columns:e,rows:t}})(),r=C.locations,i=D.locations,o=M(v),a=!Ue();A.bindFramebuffer(A.FRAMEBUFFER,n.buffer),A.viewport(0,0,n.columns,n.rows),A.useProgram(C.program),A.activeTexture(A.TEXTURE0),A.bindTexture(A.TEXTURE_2D,S),A.uniform1i(r.uSource,0),A.activeTexture(A.TEXTURE2),A.bindTexture(A.TEXTURE_2D,(N?X?.velocity:B[J])??S),A.uniform1i(r.uFluid,2),A.activeTexture(A.TEXTURE3),A.bindTexture(A.TEXTURE_2D,_e??S),A.uniform1i(r.uTrailMap,3),A.uniform1f(r.uFluidMode,N?1:0),A.uniform1i(r.uHoverMode,"fluid"===o?1:"lantern"===o?2:"trail"===o&&ke?3:0),A.uniform1f(r.uTrailAmount,v.trail.strength),A.uniform3f(r.uLight,Ae.at?.[0]??-1,Ae.at?.[1]??-1,"lantern"===o?Ae.strength:0),A.uniform3fv(r.uTrail,Fe),A.uniform1f(r.uLightRadius,v.lantern.radius),A.uniform1f(r.uLightGain,v.lantern.strength),A.uniform1f(r.uClock,performance.now()/1e3),A.uniform1f(r.uFlicker,a?1:0),A.uniform2f(r.uFluidTexel,1/Math.max(1,j),1/Math.max(1,K)),A.uniform2f(r.uResolution,z,O),A.uniform2f(r.uCell,q.width,q.height),A.uniform1f(r.uCount,$),A.uniform4f(r.uSourceMap,ct.scale[0],ct.scale[1],ct.offset[0],ct.offset[1]),A.uniform1i(r.uScene,Q?0:"flow"===v.scene?1:"orb"===v.scene?2:"drift"===v.scene?4:3),A.uniform1i(r.uMark,"dot"===v.mark?1:0),A.uniform1f(r.uScale,v.scale);const l=v.clear;A.uniform4f(r.uClear,l?l.x:0,l?l.y:0,l?l.width:0,l?l.height:0),A.uniform1f(r.uClearFeather,l?l.feather:.1),A.uniform1f(r.uClearShape,l&&"rect"===l.shape?1:0);const u=performance.now();Me.fill(0);for(let e=0;e<we.length;e++){const t=we[e],n=4*e;Me[n]=t.x,Me[n+1]=t.y,Me[n+2]=(u-t.born)/1e3,Me[n+3]=t.strength}A.uniform4fv(r.uPulse,Me),A.uniform3fv(r.uInk,lt),A.uniform3fv(r.uHighlight,ut),A.uniform1f(r.uHighlightAuto,"auto"===v.highlight?1:0),A.uniform4fv(r.uBackground,st),A.uniform1f(r.uBrightness,v.brightness),A.uniform1f(r.uContrast,v.contrast),A.uniform1f(r.uGamma,v.gamma),A.uniform1f(r.uInvert,v.invert?1:0),A.uniform1f(r.uVignette,v.vignette),A.uniform1f(r.uSourceColor,"source"===v.color?1:0),A.uniform1f(r.uRefraction,e?e.refraction*(a?1:.25):0),A.uniform1f(r.uGlow,e?e.glow:0),A.uniform1f(r.uTime,fe),A.uniform1f(r.uDrift,a?v.drift:0),A.uniform1f(r.uFluidOn,e&&"fluid"===o&&(B.length||N)?1:0),A.uniform1f(r.uLod,Math.max(0,Math.log2(Math.max(1,t))-.5)),A.uniform1f(r.uSaturation,v.saturation),A.uniform1f(r.uShade,v.shade),A.uniform1f(r.uBackdrop,Q?v.backdrop:0),A.uniform2f(r.uLevels,Q&&"auto"===v.levels?le[0]:0,Q&&"auto"===v.levels?le[1]:1),A.bindVertexArray(V),A.drawArrays(A.TRIANGLES,0,3),A.bindFramebuffer(A.FRAMEBUFFER,null),A.viewport(0,0,z,O),A.useProgram(D.program),A.activeTexture(A.TEXTURE1),A.bindTexture(A.TEXTURE_2D,k),A.uniform1i(i.uAtlas,1),A.activeTexture(A.TEXTURE4),A.bindTexture(A.TEXTURE_2D,n.ink),A.uniform1i(i.uCells,4),A.activeTexture(A.TEXTURE5),A.bindTexture(A.TEXTURE_2D,n.under),A.uniform1i(i.uUnder,5),A.uniform2f(i.uResolution,z,O),A.uniform2f(i.uCell,q.width,q.height),A.uniform1f(i.uColumns,q.columns),A.uniform1f(i.uDotScale,v.dotScale),A.uniform1i(i.uMark,"dot"===v.mark?1:0),A.drawArrays(A.TRIANGLES,0,3)},pt=()=>!de&&!me&&!ve&&(pe||Ie)&&!("undefined"!=typeof document&&document.hidden),xt=e=>{if(se=0,!pt())return;const t=(e-ce)/1e3,n=Math.min(1/30,Math.max(1/480,t));ce=e,Te=.9*Te+.1*Math.min(t,.25),ye=Te>1/24?ye+1:0;try{ye>45&&be<2&&(ye=0,be++,1===be&&W>1?(v={...v,maxPixelRatio:1},ot()):be=2);const t=He(),r=!(ge||be>=2&&!(Q instanceof HTMLVideoElement)||(!(Q instanceof HTMLVideoElement)||Q.paused||Q.ended)&&(Ue()||!(!Q&&"none"!==v.scene||v.drift>0)));r&&(fe+=n*v.speed),Q instanceof HTMLVideoElement&&Q.readyState>=2&&oe&&(oe=!("requestVideoFrameCallback"in Q),rt(),he=!0);const i=M(v);let o="fluid"===i&&(null!==Ee||e<Ce);if(o?((e,t)=>{const n=v.fluid;if(!n||!I.length&&!N)return;if(N?!X?.ready():!U||!Ge(U))return;const r=z/O,i=Ee;if(Ee=null,N&&X){const o=5200*n.force*(Ue()?.35:1);return i&&(Ce=t+1e3*Math.min(9,6/Math.max(.3,n.dissipation))),void X.step(i?{from:i.from,to:i.to,force:[(i.to[0]-i.from[0])*o,(i.to[1]-i.from[1])*o]}:null,{dt:Math.min(e,1/60),radius:1.1*n.radius,curl:n.curl,dissipation:n.dissipation,aspect:r})}let o=0,a=0;if(i){const e=(Ue()?1:3)*n.force;o=(i.to[0]-i.from[0])*r*e*4,a=(i.to[1]-i.from[1])*e*4,Ce=t+7/Math.min(n.dissipation,1.1*n.dissipation)*1e3}const l=U.locations;A.bindFramebuffer(A.FRAMEBUFFER,I[1-J]),A.viewport(0,0,j,K),A.useProgram(U.program),A.activeTexture(A.TEXTURE0),A.bindTexture(A.TEXTURE_2D,B[J]),A.uniform1i(l.uPrevious,0),A.uniform2f(l.uSize,j,K),A.uniform1f(l.uAspect,r),A.uniform1f(l.uDt,e),A.uniform1f(l.uDecay,n.dissipation),A.uniform1f(l.uEnergyDecay,1.1*n.dissipation),A.uniform1f(l.uRadius,n.radius),A.uniform1f(l.uSplat,i?1:0),A.uniform2f(l.uFrom,i?.from[0]??0,i?.from[1]??0),A.uniform2f(l.uTo,i?.to[0]??0,i?.to[1]??0),A.uniform2f(l.uForce,o,a),A.drawArrays(A.TRIANGLES,0,3),J=1-J})(n,e):Ee=null,"lantern"===i&&(o=((e,t)=>{const n=v.lantern,r=Ae.target;if(r){const i=Ae.at;if(i)if(Ue())i[0]=r[0],i[1]=r[1];else{const t=1-Math.exp(-n.follow*e);i[0]+=(r[0]-i[0])*t,i[1]+=(r[1]-i[1])*t}else Ae.at=[r[0],r[1]];const o=Ae.at,a=Ae.trail[Ae.trail.length-1];n.trail>0&&(!a||t-a.born>28&&Math.hypot(o[0]-a.x,o[1]-a.y)>.004)&&(Ae.trail.push({x:o[0],y:o[1],born:t}),Ae.trail.length>12&&Ae.trail.shift())}Ae.strength+=((r?1:0)-Ae.strength)*(1-Math.exp(-e*(r?9:5)));const i=Ae.trail;for(let e=i.length-1;e>=0;e--)n.trail>0&&t-i[e].born<4e3*n.trail||i.splice(e,1);Fe.fill(0);for(let e=0;e<i.length;e++){const r=i[e];Fe[3*e]=r.x,Fe[3*e+1]=r.y,Fe[3*e+2]=.55*Math.exp(-(t-r.born)/1e3/Math.max(.01,n.trail))}return Ae.strength>.003||i.length>0})(n,e)||o),"trail"===i){const e=!!Le?.active;o=(e=>{const t=Se();return!(!t||!t.active||(t.step(e),A.bindTexture(A.TEXTURE_2D,_e),A.texSubImage2D(A.TEXTURE_2D,0,0,0,t.width,t.height,A.RGBA,A.UNSIGNED_BYTE,t.pixels),ke=!0,0))})(n)||o,e&&!Le?.active&&(he=!0)}if(we.length){for(let t=we.length-1;t>=0;t--)e-we[t].born>=2400&&we.splice(t,1);o=!0,we.length||(he=!0)}t&&(r||o||he)&&(gt(),he=!1,!Ie||ne&&!Q||(Ie=!1,Pe())),(r||o||Ee||!t)&&(se=requestAnimationFrame(xt))}catch(e){ve=!0,Xe(e)}};function Et(){!se&&pt()&&(ce=performance.now(),se=requestAnimationFrame(xt))}const Tt=(e,n)=>{const r=t.getBoundingClientRect();if(!r.width||!r.height)return null;const i=(e-r.left)/r.width,o=(n-r.top)/r.height;return i<-.05||i>1.05||o<-.05||o>1.05?null:[i,1-o]},yt=e=>{const t=M(v);if("none"===t||ge)return xe=null,void(Ae.target=null);if("lantern"===t)return Ae.target=e?[e[0],1-e[1]]:null,xe=null,void Et();if("trail"===t){const t=Se();if(!t)return;return e?t.move(e[0],1-e[1],v.trail.radius,performance.now()):t.leave(),xe=null,void Et()}e?(xe&&(Ee=Ee?{from:Ee.from,to:e}:{from:xe,to:e}),xe=e,Et()):xe=null},bt=e=>yt(Tt(e.clientX,e.clientY)),wt=()=>{xe=null,Le?.leave(),Ae.target&&(Ae.target=null,Et())},Mt=f.interactionTarget??window;Mt.addEventListener("pointermove",bt,{passive:!0}),Mt.addEventListener("pointerdown",bt,{passive:!0}),document.addEventListener("pointerleave",wt),Mt!==window&&Mt.addEventListener("pointerleave",wt);const Rt="function"==typeof ResizeObserver?new ResizeObserver(()=>{const e=ot();Re(),(e||he)&&Et()}):null;Rt?.observe(t),m&&Rt?.observe(m.element);const At="function"==typeof IntersectionObserver?new IntersectionObserver(([e])=>{pe=e.isIntersecting,pe&&(he=!0,Et())},{rootMargin:"120px"}):null;At?.observe(t);const Ft=()=>{document.hidden||(he=!0,Et())};document.addEventListener("visibilitychange",Ft);const Lt=()=>{he=!0,Et()};De?.addEventListener?.("change",Lt),Qe?.addEventListener?.("loadingdone",tt);const _t=e=>{e.preventDefault(),me=!0,se&&cancelAnimationFrame(se),se=0},kt=()=>{me=!1,H.length=0,G=null,C=D=U=null,P=null,B=[],I=[],X=null,N=!1,_e=null,Le=null,ke=!1;try{it(),he=!0,Et()}catch(e){Xe(e)}};t.addEventListener("webglcontextlost",_t),t.addEventListener("webglcontextrestored",kt);const St=()=>{Z&&(Z.pause(),Z.removeAttribute("src"),Z.load(),Z=null),ae&&Q instanceof HTMLVideoElement&&"cancelVideoFrameCallback"in Q&&Q.cancelVideoFrameCallback(ae),ae=0},Ct=()=>{re?.close(),re=null},Dt=async e=>{const t=await createImageBitmap(e,{premultiplyAlpha:"none"}),n=[t.width,t.height],r=function(e,t,n,r,i,o,a=8){if(!(n>=16&&r>=16&&e>0&&t>0&&i>0))return 1;const l=a/i/("cover"===o?Math.min(e/n,t/r):Math.max(e/n,t/r));return l>.5?1:2**-Math.floor(Math.log2(1/l))}(t.width,t.height,z,O,q.height,v.fit);if(1===r)return{element:t,owned:!0,natural:n};try{return{element:await createImageBitmap(t,{resizeWidth:Math.max(1,Math.round(t.width*r)),resizeHeight:Math.max(1,Math.round(t.height*r)),resizeQuality:"medium",premultiplyAlpha:"none"}),owned:!0,natural:n}}finally{t.close()}},Ut=e=>new Promise((t,n)=>{const r=new Image;r.crossOrigin="anonymous",r.decoding="async",r.onload=()=>t({element:r,owned:!1}),r.onerror=()=>n(new Error("The glyph field could not load this image.")),r.src=e}),Pt=async e=>{const t=++ne;if(!e)return St(),Ct(),Q=null,ee=te=ie=0,he=!0,void Et();try{const n=await(async e=>{if("string"!=typeof e)return e instanceof HTMLImageElement&&!e.complete?e.decode().then(()=>({element:e,owned:!1})):e instanceof HTMLVideoElement&&e.readyState<2?new Promise((t,n)=>{e.addEventListener("loadeddata",()=>t({element:e,owned:!1}),{once:!0}),e.addEventListener("error",()=>n(new Error("The glyph field could not load this video.")),{once:!0})}):{element:e,owned:!1};if(F.test(e))return new Promise((t,n)=>{const r=document.createElement("video");r.crossOrigin="anonymous",r.muted=!0,r.loop=!0,r.playsInline=!0,r.preload="auto",r.addEventListener("loadeddata",()=>{r.play().catch(()=>{}),t({element:r,owned:!0})},{once:!0}),r.addEventListener("error",()=>n(new Error("The glyph field could not load this video.")),{once:!0}),r.src=e});if("function"!=typeof createImageBitmap||"function"!=typeof fetch)return Ut(e);let t;try{t=await(await fetch(e).then(e=>{if(!e.ok)throw new Error("The glyph field could not load this image.");return e})).blob()}catch(t){if(t instanceof Error&&t.message.startsWith("The glyph field"))throw t;return Ut(e)}if(!/svg/i.test(t.type))try{return await Dt(t)}catch{}const n=URL.createObjectURL(t);try{return await Ut(n)}finally{URL.revokeObjectURL(n)}})(e),{element:r,owned:i}=n;if(de||t!==ne)return void(i&&r instanceof HTMLVideoElement?(r.pause(),r.removeAttribute("src"),r.load()):i&&r.close());St(),Ct(),Z=i&&r instanceof HTMLVideoElement?r:null,!i||r instanceof HTMLVideoElement||(re=r),Q=r,ie=r instanceof HTMLVideoElement?r.videoHeight:r instanceof HTMLImageElement?r.naturalHeight:r.height,ee=n.natural?.[0]??(r instanceof HTMLVideoElement?r.videoWidth:r instanceof HTMLImageElement?r.naturalWidth:r.width),te=n.natural?.[1]??ie,oe=!0,r instanceof HTMLVideoElement&&(e=>{const t=e;if(!t.requestVideoFrameCallback)return;const n=()=>{de||Q!==e||(oe=!0,Et(),ae=t.requestVideoFrameCallback(n))};ae=t.requestVideoFrameCallback(n)})(r),me||rt(),he=!0,Et()}catch(e){Ie&&t===ne&&(Ie=!1,Pe()),Xe(e)}};it(),Re();const Bt=f.source??null;return Bt?(ne=1,Pt(Bt)):Et(),{canvas:t,ready:Be,get settings(){return v},update(e){const n=v,{element:r,rest:i}=d(e);"clear"in e&&(m&&m.element!==r?.element&&Rt?.unobserve(m.element),m=r,r&&Rt?.observe(r.element)),v=E(r?{...i,clear:v.clear}:i,v),Re(),e.label&&t.setAttribute("aria-label",e.label),v.maxPixelRatio!==n.maxPixelRatio&&ot(),v.charset===n.charset&&v.cellSize===n.cellSize&&v.cellAspect===n.cellAspect&&v.font===n.font&&v.fontWeight===n.fontWeight&&v.glyphScale===n.glyphScale||(Je(),Ze()),v.levels!==n.levels&&nt();((!!n.fluid&&"fluid"===M(n))!==We()||v.fluid&&n.fluid&&v.fluid.resolution!==n.fluid.resolution)&&$e(),"source"in e&&Pt(e.source??null),he=!0,Et()},setSource:Pt,pointer(e,n=0){if(null===e)return void yt(null);const r=t.getBoundingClientRect();yt(Tt(r.left+e,r.top+n))},redraw(){if(!(de||me||ve)&&z)try{He(!0)&&gt()}catch(e){ve=!0,Xe(e)}},pulse(e={}){if(de||Ue())return;const n=t.getBoundingClientRect();if(!n.width||!n.height)return;let r=e.x??n.width/2,i=e.y??n.height/2;if(e.element){const t=e.element.getBoundingClientRect();r=t.left+t.width/2-n.left,i=t.top+t.height/2-n.top}const o=Math.max(0,Math.min(2,e.strength??1));we=[...we,{x:r/n.width,y:i/n.height,born:performance.now(),strength:o}].slice(-4),Et()},play(){ge=!1,Q instanceof HTMLVideoElement&&Q.play().catch(()=>{}),he=!0,Et()},pause(){ge=!0,Q instanceof HTMLVideoElement&&Q.pause()},resize(){ot()&&Et()},destroy(){if(!de&&(de=!0,se&&cancelAnimationFrame(se),Mt.removeEventListener("pointermove",bt),Mt.removeEventListener("pointerdown",bt),document.removeEventListener("pointerleave",wt),Mt.removeEventListener("pointerleave",wt),document.removeEventListener("visibilitychange",Ft),De?.removeEventListener?.("change",Lt),Qe?.removeEventListener?.("loadingdone",tt),t.removeEventListener("webglcontextlost",_t),t.removeEventListener("webglcontextrestored",kt),Rt?.disconnect(),At?.disconnect(),St(),Ct(),Q=null,!me)){Ye(),Oe(),A.deleteTexture(k),A.deleteTexture(S);for(const e of[C,D,U])e&&A.deleteProgram(e.program);X?.dispose(),_e&&A.deleteTexture(_e),H.forEach(e=>A.deleteShader(e)),V&&A.deleteVertexArray(V)}}}}export{m as DEFAULT_FIELD_FLUID,h as DEFAULT_FIELD_LANTERN,v as DEFAULT_FIELD_SETTINGS,d as DEFAULT_FIELD_TRAIL,f as FIELD_HOVERS,M as activeFieldHover,y as fieldCellLayout,R as fieldClearFromRects,x as fieldGlyphs,w as fieldLevels,T as fieldSourceRect,_ as glyphFieldTier,k as isGlyphFieldSupported,S as mountGlyphField,E as normalizeFieldSettings};//# sourceMappingURL=field.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "asciify-engine",
3
- "version": "4.12.0",
3
+ "version": "4.12.1",
4
4
  "description": "Framework-agnostic ASCII art engine. Convert images, videos, and GIFs into ASCII art rendered on canvas, or run full-bleed GPU character fields with a fluid cursor wake.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -2,13 +2,13 @@
2
2
  name: asciify-engine
3
3
  description: Build and tune browser ASCII art with asciify-engine so it fits the user's request, preferences and the project's design. Use for photo, video and GIF conversion, interactive heroes, GPU character fields with the user's own words, ambient backgrounds, text banners, art styles and export, hover and motion choices, layered HTML typography, and rendering performance.
4
4
  metadata:
5
- tested-engine: "4.12.0"
5
+ tested-engine: "4.12.1"
6
6
  updated: "2026-10-14"
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.12.0**. Check the application's installed version and its public types before using newer options; a local checkout can contain unpublished changes.
11
+ Use the published `asciify-engine` npm package for browser canvas rendering. Tested against **4.12.1**. Check the application's installed version and its public types before using newer options; a local checkout can contain unpublished changes.
12
12
 
13
13
  - Agent entry: https://asciify.org/skill (redirects to this Markdown document).
14
14
  - Playground: https://asciify.org/editor
@@ -31,7 +31,7 @@ Most requests are a feeling ("make the hero feel alive", "retro", "subtle behind
31
31
  4. **Decide living or still.** A still photo can be made to feel alive (field `drift`, studio `motion`), but some users want it still. Follow their words; keep art behind text, in grids and in data views still; reduced motion always gets a still. See [living or still](references/design-matching.md#living-or-still).
32
32
  5. **Say what you chose in one line** and offer one alternative. Ask only when the media is missing or motion would be new to a site that has none.
33
33
 
34
- [references/components.md](references/components.md) has 21 ready-made components with a shared theme-aware helper: interface pieces (image card, avatar, empty state, loading state, copy feedback), backgrounds (hero, cursor field, brand field), text (CLI banner, wordmark, decode headline), transitions (photo from characters, page transition, scramble button), pages (404, coming soon, sign-in, pricing), AI states (thinking, image generation) and video in characters, each with the prompt a person gives and the reference code. Start there when the request matches one.
34
+ [references/components.md](references/components.md) has 29 ready-made components with a shared theme-aware helper: controls (button loading, slide to confirm, like burst, verification code, command palette), feedback (loading states, skeleton loader, toast stack, pull to refresh), AI (voice orb, thinking, streaming text, image generation), transitions (page transition, delete into characters), data (chart, odometer, border beam), media (before and after, dropzone, accordion, swipe cards, video), text (decode headline, wordmark) and pages (sign-in, pricing, 404, hero background), each with the prompt a person gives and the reference code. Start there when the request matches one.
35
35
 
36
36
  [references/design-matching.md](references/design-matching.md) maps mood words to settings, explains light themes, contrast and fonts, and gives tested recipes for the common requests. Read it whenever the user describes a look rather than an option.
37
37