NP@naman-parashar-s-team / golden-cardPublic
golden-card
A JS snippet on QuikRun, callable at quik.run/r/golden-card.
JS· node:20· 12 runs· 0 forks· updated 59m ago
golden-card.js
// golden-card — a premium 3D AUREUS metal card, centered and tilting toward the// cursor with a live glare/holo sheen. Hover it and a WebGL fluid sim (ported from// React Bits SplashCursor, re-tinted gold) erupts molten-gold ink over the card.// Self-contained; the bound import only selects ES-module mode for the sandbox.import htm from "htm";void htm;const HTML = `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>AUREUS — golden splash card</title><style>html,body{margin:0;height:100%;overflow:hidden;background:radial-gradient(120% 90% at 50% 22%, #0f0c07 0%, #030303 72%)}.stage{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;z-index:1;perspective:1200px}.tilt{position:relative;width:460px;height:290px;transform-style:preserve-3d;transition:transform .18s ease-out;will-change:transform}#card{display:block;border-radius:22px;box-shadow:0 44px 90px -34px rgba(0,0,0,.92), 0 12px 34px -12px rgba(0,0,0,.6)}.glare,.holo,.rim{position:absolute;inset:0;border-radius:22px;pointer-events:none}.glare{background:radial-gradient(circle at var(--gx,50%) var(--gy,50%), rgba(255,246,214,.6), rgba(255,246,214,0) 46%);mix-blend-mode:screen;opacity:0;transition:opacity .2s ease}.holo{background:linear-gradient(120deg, transparent 32%, rgba(255,222,150,.16) 46%, rgba(170,140,255,.10) 53%, transparent 66%);background-size:250% 250%;mix-blend-mode:screen;opacity:.5;animation:holo 6.5s linear infinite}.rim{box-shadow: inset 0 1px 0 rgba(255,242,205,.28), inset 0 0 0 1px rgba(230,210,150,.12)}@keyframes holo{0%{background-position:125% 0}100%{background-position:-45% 0}}#fluid{position:fixed;inset:0;width:100vw;height:100vh;display:block;z-index:3;pointer-events:none;mix-blend-mode:screen}.cursor{position:fixed;left:0;top:0;z-index:5;width:20px;height:20px;border-radius:50%;pointer-events:none;transform:translate(-50%,-50%);mix-blend-mode:screen;background:radial-gradient(circle, rgba(255,214,120,.85), rgba(255,190,70,0) 68%);transition:width .16s ease,height .16s ease}.cursor.on{width:32px;height:32px;background:radial-gradient(circle, rgba(255,228,155,.95), rgba(255,195,80,0) 66%)}.hint{position:fixed;left:50%;bottom:34px;transform:translateX(-50%);z-index:4;pointer-events:none;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;letter-spacing:.34em;text-transform:uppercase;color:#6a5f3a}.hint b{color:#e8c24b;font-weight:600}</style></head><body><div class="stage"><div class="tilt" id="tilt"><canvas id="card"></canvas><div class="holo"></div><div class="glare"></div><div class="rim"></div></div></div><canvas id="fluid"></canvas><div class="cursor" id="cursor"></div><div class="hint">move around · <b>hover the card</b></div><script>(function(){"use strict";/* ============================ THE CARD ============================ */(function drawCard(){const dpr=2, W=460, H=290, cv=document.getElementById('card');cv.width=W*dpr; cv.height=H*dpr; cv.style.width=W+'px'; cv.style.height=H+'px';const c=cv.getContext('2d'); c.scale(dpr,dpr);function rr(x,y,w,h,rad){ c.beginPath(); c.moveTo(x+rad,y); c.arcTo(x+w,y,x+w,y+h,rad); c.arcTo(x+w,y+h,x,y+h,rad); c.arcTo(x,y+h,x,y,rad); c.arcTo(x,y,x+w,y,rad); c.closePath(); }const r=22;c.save(); rr(0,0,W,H,r); c.clip();// obsidian metallet g=c.createLinearGradient(0,0,W,H);g.addColorStop(0,"#1b1712"); g.addColorStop(0.45,"#0c0b09"); g.addColorStop(0.55,"#0a0a0c"); g.addColorStop(1,"#171310");c.fillStyle=g; c.fillRect(0,0,W,H);let sh=c.createRadialGradient(W*0.3,0,0,W*0.3,0,H*1.5); sh.addColorStop(0,"rgba(201,162,75,0.12)"); sh.addColorStop(1,"rgba(0,0,0,0)");c.fillStyle=sh; c.fillRect(0,0,W,H);// brushed micro-linesc.save(); c.globalAlpha=0.05;for(let i=0;i<300;i++){ c.strokeStyle=i%2?"#c9a24b":"#000"; c.lineWidth=0.6; const yy=Math.random()*H; c.beginPath(); c.moveTo(0,yy); c.lineTo(W,yy+(Math.random()-0.5)*2); c.stroke(); }c.restore();// guilloché rosettec.save(); c.translate(W*0.5,H*0.5); c.globalAlpha=0.15; c.lineWidth=0.5; c.strokeStyle="#c9a24b";for(let ring=0;ring<5;ring++){ const base=34+ring*11, amp=6+ring*1.1, k=7+ring; c.beginPath();for(let a=0;a<=6.2832;a+=0.02){ const rad=base+amp*Math.cos(k*a)+amp*0.4*Math.sin((k+3)*a); const px=Math.cos(a)*rad*1.6, py=Math.sin(a)*rad; a===0?c.moveTo(px,py):c.lineTo(px,py); }c.closePath(); c.stroke(); }c.restore();// chipconst chx=28, chy=96, chw=52, chh=40;let cg=c.createLinearGradient(chx,chy,chx+chw,chy+chh); cg.addColorStop(0,"#f4e2a8"); cg.addColorStop(0.5,"#c9a24b"); cg.addColorStop(1,"#8a6a2a");rr(chx,chy,chw,chh,7); c.fillStyle=cg; c.fill();c.strokeStyle="rgba(60,40,10,0.6)"; c.lineWidth=1; c.beginPath();c.moveTo(chx+chw*0.33,chy); c.lineTo(chx+chw*0.33,chy+chh); c.moveTo(chx+chw*0.66,chy); c.lineTo(chx+chw*0.66,chy+chh);c.moveTo(chx,chy+chh*0.5); c.lineTo(chx+chw,chy+chh*0.5); c.stroke();// contactlessc.save(); c.translate(chx+chw+24,chy+chh*0.5); c.strokeStyle="#c9a24b"; c.lineWidth=2.2; c.lineCap="round";for(let w=0;w<3;w++){ c.beginPath(); c.arc(0,0,7+w*6,-0.7,0.7); c.stroke(); } c.restore();// holo patchconst hx=W-92, hy=28, hw=54, hh=40;let holo=c.createLinearGradient(hx,hy,hx+hw,hy+hh); holo.addColorStop(0,"rgba(120,220,200,0.5)"); holo.addColorStop(0.4,"rgba(220,180,120,0.5)"); holo.addColorStop(0.7,"rgba(180,140,220,0.5)"); holo.addColorStop(1,"rgba(120,200,220,0.5)");rr(hx,hy,hw,hh,8); c.fillStyle=holo; c.fill();// brand — personalisedc.fillStyle="#e7d6a6"; c.font="700 22px Georgia, serif"; c.fillText("NAMAN PARASHAR", 28, 52);c.font="10px ui-monospace, monospace"; c.fillStyle="#9c8a5e"; c.fillText("parashar150.com", 28, 68);// emboss helperfunction emboss(txt,ex,ey,font,sp){ c.font=font; let cx=ex; for(let i=0;i<txt.length;i++){ const ch2=txt[i];c.fillStyle="rgba(0,0,0,0.55)"; c.fillText(ch2, cx+1.4, ey+1.6);let lg=c.createLinearGradient(cx,ey-16,cx,ey+4); lg.addColorStop(0,"#f6e7b6"); lg.addColorStop(1,"#b89451");c.fillStyle=lg; c.fillText(ch2, cx, ey); cx+=c.measureText(ch2).width+sp; } }emboss("5412 7508 9021 1808", 28, H-78, "600 24px ui-monospace, Menlo, monospace", 0.4);c.fillStyle="#7d7150"; c.font="8.5px ui-monospace, monospace"; c.fillText("BUILT WITH", 28, H-52); c.fillText("VALID THRU", W-150, H-52);emboss("QUIKRUN", 28, H-30, "600 15px ui-monospace, monospace", 0.4);emboss("08/29", W-150, H-30, "600 15px ui-monospace, monospace", 0.4);// network markconst nx=W-58, ny=H-40; c.globalAlpha=0.95;c.fillStyle="#d9a24b"; c.beginPath(); c.arc(nx,ny,15,0,6.2832); c.fill();c.fillStyle="#f0d488"; c.beginPath(); c.arc(nx+15,ny,15,0,6.2832); c.fill();c.globalCompositeOperation="overlay"; c.fillStyle="#8a6a2a"; c.beginPath(); c.arc(nx+7.5,ny,15,0,6.2832); c.fill();c.globalCompositeOperation="source-over"; c.globalAlpha=1;c.restore();})();/* ============================ FLUID ENGINE (golden) ============================ */const canvas = document.getElementById('fluid');function pointerPrototype(){ this.id=-1; this.texcoordX=0; this.texcoordY=0; this.prevTexcoordX=0; this.prevTexcoordY=0; this.deltaX=0; this.deltaY=0; this.down=false; this.moved=false; this.color=[0,0,0]; }let config={ SIM_RESOLUTION:128, DYE_RESOLUTION:1024, CAPTURE_RESOLUTION:512, DENSITY_DISSIPATION:2.5, VELOCITY_DISSIPATION:2, PRESSURE:0.1, PRESSURE_ITERATIONS:20, CURL:3.5, SPLAT_RADIUS:0.22, SPLAT_FORCE:6000, SHADING:true, COLOR_UPDATE_SPEED:10, PAUSED:false, BACK_COLOR:{r:0,g:0,b:0}, TRANSPARENT:true };let pointers=[new pointerPrototype()];const { gl, ext } = getWebGLContext(canvas);if(!ext.supportLinearFiltering){ config.DYE_RESOLUTION=256; config.SHADING=false; }function getWebGLContext(canvas){const params={ alpha:true, depth:false, stencil:false, antialias:false, preserveDrawingBuffer:false };let gl=canvas.getContext('webgl2',params); const isWebGL2=!!gl;if(!isWebGL2) gl=canvas.getContext('webgl',params)||canvas.getContext('experimental-webgl',params);let halfFloat, supportLinearFiltering;if(isWebGL2){ gl.getExtension('EXT_color_buffer_float'); supportLinearFiltering=gl.getExtension('OES_texture_float_linear'); }else { halfFloat=gl.getExtension('OES_texture_half_float'); supportLinearFiltering=gl.getExtension('OES_texture_half_float_linear'); }gl.clearColor(0,0,0,1);const halfFloatTexType=isWebGL2?gl.HALF_FLOAT:halfFloat&&halfFloat.HALF_FLOAT_OES;let formatRGBA, formatRG, formatR;if(isWebGL2){ formatRGBA=getSupportedFormat(gl,gl.RGBA16F,gl.RGBA,halfFloatTexType); formatRG=getSupportedFormat(gl,gl.RG16F,gl.RG,halfFloatTexType); formatR=getSupportedFormat(gl,gl.R16F,gl.RED,halfFloatTexType); }else { formatRGBA=getSupportedFormat(gl,gl.RGBA,gl.RGBA,halfFloatTexType); formatRG=getSupportedFormat(gl,gl.RGBA,gl.RGBA,halfFloatTexType); formatR=getSupportedFormat(gl,gl.RGBA,gl.RGBA,halfFloatTexType); }return { gl, ext:{ formatRGBA, formatRG, formatR, halfFloatTexType, supportLinearFiltering } };}function getSupportedFormat(gl,internalFormat,format,type){if(!supportRenderTextureFormat(gl,internalFormat,format,type)){ switch(internalFormat){ case gl.R16F: return getSupportedFormat(gl,gl.RG16F,gl.RG,type); case gl.RG16F: return getSupportedFormat(gl,gl.RGBA16F,gl.RGBA,type); default: return null; } }return { internalFormat, format };}function supportRenderTextureFormat(gl,internalFormat,format,type){const texture=gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D,texture);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.texImage2D(gl.TEXTURE_2D,0,internalFormat,4,4,0,format,type,null);const fbo=gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER,fbo);gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,texture,0);return gl.checkFramebufferStatus(gl.FRAMEBUFFER)===gl.FRAMEBUFFER_COMPLETE;}class Material{ constructor(v,f){ this.vertexShader=v; this.fragmentShaderSource=f; this.programs=[]; this.activeProgram=null; this.uniforms=[]; }setKeywords(keywords){ let hash=0; for(let i=0;i<keywords.length;i++) hash+=hashCode(keywords[i]); let program=this.programs[hash];if(program==null){ let fragmentShader=compileShader(gl.FRAGMENT_SHADER,this.fragmentShaderSource,keywords); program=createProgram(this.vertexShader,fragmentShader); this.programs[hash]=program; }if(program===this.activeProgram) return; this.uniforms=getUniforms(program); this.activeProgram=program; } bind(){ gl.useProgram(this.activeProgram); } }class Program{ constructor(v,f){ this.uniforms={}; this.program=createProgram(v,f); this.uniforms=getUniforms(this.program); } bind(){ gl.useProgram(this.program); } }function createProgram(v,f){ let p=gl.createProgram(); gl.attachShader(p,v); gl.attachShader(p,f); gl.linkProgram(p); if(!gl.getProgramParameter(p,gl.LINK_STATUS)) console.trace(gl.getProgramInfoLog(p)); return p; }function getUniforms(program){ let uniforms=[]; let n=gl.getProgramParameter(program,gl.ACTIVE_UNIFORMS); for(let i=0;i<n;i++){ let name=gl.getActiveUniform(program,i).name; uniforms[name]=gl.getUniformLocation(program,name); } return uniforms; }function compileShader(type,source,keywords){ source=addKeywords(source,keywords); const shader=gl.createShader(type); gl.shaderSource(shader,source); gl.compileShader(shader); if(!gl.getShaderParameter(shader,gl.COMPILE_STATUS)) console.trace(gl.getShaderInfoLog(shader)); return shader; }function addKeywords(source,keywords){ if(!keywords) return source; let s=''; keywords.forEach(k=>{ s+='#define '+k+'\\n'; }); return s+source; }const baseVertexShader=compileShader(gl.VERTEX_SHADER,\`precision highp float; attribute vec2 aPosition; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform vec2 texelSize;void main(){ vUv=aPosition*0.5+0.5; vL=vUv-vec2(texelSize.x,0.0); vR=vUv+vec2(texelSize.x,0.0); vT=vUv+vec2(0.0,texelSize.y); vB=vUv-vec2(0.0,texelSize.y); gl_Position=vec4(aPosition,0.0,1.0); }\`);const copyShader=compileShader(gl.FRAGMENT_SHADER,\`precision mediump float; precision mediump sampler2D; varying highp vec2 vUv; uniform sampler2D uTexture; void main(){ gl_FragColor=texture2D(uTexture,vUv); }\`);const clearShader=compileShader(gl.FRAGMENT_SHADER,\`precision mediump float; precision mediump sampler2D; varying highp vec2 vUv; uniform sampler2D uTexture; uniform float value; void main(){ gl_FragColor=value*texture2D(uTexture,vUv); }\`);const displayShaderSource=\`precision highp float; precision highp sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uTexture; uniform vec2 texelSize;void main(){ vec3 c=texture2D(uTexture,vUv).rgb;#ifdef SHADINGvec3 lc=texture2D(uTexture,vL).rgb; vec3 rc=texture2D(uTexture,vR).rgb; vec3 tc=texture2D(uTexture,vT).rgb; vec3 bc=texture2D(uTexture,vB).rgb;float dx=length(rc)-length(lc); float dy=length(tc)-length(bc); vec3 n=normalize(vec3(dx,dy,length(texelSize))); vec3 l=vec3(0.0,0.0,1.0);float diffuse=clamp(dot(n,l)+0.7,0.7,1.0); c*=diffuse;#endiffloat a=max(c.r,max(c.g,c.b)); gl_FragColor=vec4(c,a); }\`;const splatShader=compileShader(gl.FRAGMENT_SHADER,\`precision highp float; precision highp sampler2D; varying vec2 vUv; uniform sampler2D uTarget; uniform float aspectRatio; uniform vec3 color; uniform vec2 point; uniform float radius;void main(){ vec2 p=vUv-point.xy; p.x*=aspectRatio; vec3 splat=exp(-dot(p,p)/radius)*color; vec3 base=texture2D(uTarget,vUv).xyz; gl_FragColor=vec4(base+splat,1.0); }\`);const advectionShader=compileShader(gl.FRAGMENT_SHADER,\`precision highp float; precision highp sampler2D; varying vec2 vUv; uniform sampler2D uVelocity; uniform sampler2D uSource; uniform vec2 texelSize; uniform vec2 dyeTexelSize; uniform float dt; uniform float dissipation;vec4 bilerp(sampler2D sam, vec2 uv, vec2 tsize){ vec2 st=uv/tsize-0.5; vec2 iuv=floor(st); vec2 fuv=fract(st); vec4 a=texture2D(sam,(iuv+vec2(0.5,0.5))*tsize); vec4 b=texture2D(sam,(iuv+vec2(1.5,0.5))*tsize); vec4 c=texture2D(sam,(iuv+vec2(0.5,1.5))*tsize); vec4 d=texture2D(sam,(iuv+vec2(1.5,1.5))*tsize); return mix(mix(a,b,fuv.x),mix(c,d,fuv.x),fuv.y); }void main(){#ifdef MANUAL_FILTERINGvec2 coord=vUv-dt*bilerp(uVelocity,vUv,texelSize).xy*texelSize; vec4 result=bilerp(uSource,coord,dyeTexelSize);#elsevec2 coord=vUv-dt*texture2D(uVelocity,vUv).xy*texelSize; vec4 result=texture2D(uSource,coord);#endiffloat decay=1.0+dissipation*dt; gl_FragColor=result/decay; }\`, ext.supportLinearFiltering?null:['MANUAL_FILTERING']);const divergenceShader=compileShader(gl.FRAGMENT_SHADER,\`precision mediump float; precision mediump sampler2D; varying highp vec2 vUv; varying highp vec2 vL; varying highp vec2 vR; varying highp vec2 vT; varying highp vec2 vB; uniform sampler2D uVelocity;void main(){ float L=texture2D(uVelocity,vL).x; float R=texture2D(uVelocity,vR).x; float T=texture2D(uVelocity,vT).y; float B=texture2D(uVelocity,vB).y; vec2 C=texture2D(uVelocity,vUv).xy; if(vL.x<0.0){L=-C.x;} if(vR.x>1.0){R=-C.x;} if(vT.y>1.0){T=-C.y;} if(vB.y<0.0){B=-C.y;} float div=0.5*(R-L+T-B); gl_FragColor=vec4(div,0.0,0.0,1.0); }\`);const curlShader=compileShader(gl.FRAGMENT_SHADER,\`precision mediump float; precision mediump sampler2D; varying highp vec2 vUv; varying highp vec2 vL; varying highp vec2 vR; varying highp vec2 vT; varying highp vec2 vB; uniform sampler2D uVelocity;void main(){ float L=texture2D(uVelocity,vL).y; float R=texture2D(uVelocity,vR).y; float T=texture2D(uVelocity,vT).x; float B=texture2D(uVelocity,vB).x; float vorticity=R-L-T+B; gl_FragColor=vec4(0.5*vorticity,0.0,0.0,1.0); }\`);const vorticityShader=compileShader(gl.FRAGMENT_SHADER,\`precision highp float; precision highp sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uVelocity; uniform sampler2D uCurl; uniform float curl; uniform float dt;void main(){ float L=texture2D(uCurl,vL).x; float R=texture2D(uCurl,vR).x; float T=texture2D(uCurl,vT).x; float B=texture2D(uCurl,vB).x; float C=texture2D(uCurl,vUv).x; vec2 force=0.5*vec2(abs(T)-abs(B),abs(R)-abs(L)); force/=length(force)+0.0001; force*=curl*C; force.y*=-1.0; vec2 velocity=texture2D(uVelocity,vUv).xy; velocity+=force*dt; velocity=min(max(velocity,-1000.0),1000.0); gl_FragColor=vec4(velocity,0.0,1.0); }\`);const pressureShader=compileShader(gl.FRAGMENT_SHADER,\`precision mediump float; precision mediump sampler2D; varying highp vec2 vUv; varying highp vec2 vL; varying highp vec2 vR; varying highp vec2 vT; varying highp vec2 vB; uniform sampler2D uPressure; uniform sampler2D uDivergence;void main(){ float L=texture2D(uPressure,vL).x; float R=texture2D(uPressure,vR).x; float T=texture2D(uPressure,vT).x; float B=texture2D(uPressure,vB).x; float divergence=texture2D(uDivergence,vUv).x; float pressure=(L+R+B+T-divergence)*0.25; gl_FragColor=vec4(pressure,0.0,0.0,1.0); }\`);const gradientSubtractShader=compileShader(gl.FRAGMENT_SHADER,\`precision mediump float; precision mediump sampler2D; varying highp vec2 vUv; varying highp vec2 vL; varying highp vec2 vR; varying highp vec2 vT; varying highp vec2 vB; uniform sampler2D uPressure; uniform sampler2D uVelocity;void main(){ float L=texture2D(uPressure,vL).x; float R=texture2D(uPressure,vR).x; float T=texture2D(uPressure,vT).x; float B=texture2D(uPressure,vB).x; vec2 velocity=texture2D(uVelocity,vUv).xy; velocity.xy-=vec2(R-L,T-B); gl_FragColor=vec4(velocity,0.0,1.0); }\`);const blit=(()=>{ gl.bindBuffer(gl.ARRAY_BUFFER,gl.createBuffer()); gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([-1,-1,-1,1,1,1,1,-1]),gl.STATIC_DRAW); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,gl.createBuffer()); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,new Uint16Array([0,1,2,0,2,3]),gl.STATIC_DRAW); gl.vertexAttribPointer(0,2,gl.FLOAT,false,0,0); gl.enableVertexAttribArray(0);return (target,clear=false)=>{ if(target==null){ gl.viewport(0,0,gl.drawingBufferWidth,gl.drawingBufferHeight); gl.bindFramebuffer(gl.FRAMEBUFFER,null);} else { gl.viewport(0,0,target.width,target.height); gl.bindFramebuffer(gl.FRAMEBUFFER,target.fbo);} if(clear){ gl.clearColor(0,0,0,1); gl.clear(gl.COLOR_BUFFER_BIT);} gl.drawElements(gl.TRIANGLES,6,gl.UNSIGNED_SHORT,0); }; })();let dye,velocity,divergence,curl,pressure;const copyProgram=new Program(baseVertexShader,copyShader);const clearProgram=new Program(baseVertexShader,clearShader);const splatProgram=new Program(baseVertexShader,splatShader);const advectionProgram=new Program(baseVertexShader,advectionShader);const divergenceProgram=new Program(baseVertexShader,divergenceShader);const curlProgram=new Program(baseVertexShader,curlShader);const vorticityProgram=new Program(baseVertexShader,vorticityShader);const pressureProgram=new Program(baseVertexShader,pressureShader);const gradienSubtractProgram=new Program(baseVertexShader,gradientSubtractShader);const displayMaterial=new Material(baseVertexShader,displayShaderSource);function initFramebuffers(){ let simRes=getResolution(config.SIM_RESOLUTION); let dyeRes=getResolution(config.DYE_RESOLUTION); const texType=ext.halfFloatTexType; const rgba=ext.formatRGBA, rg=ext.formatRG, r=ext.formatR; const filtering=ext.supportLinearFiltering?gl.LINEAR:gl.NEAREST; gl.disable(gl.BLEND);if(!dye) dye=createDoubleFBO(dyeRes.width,dyeRes.height,rgba.internalFormat,rgba.format,texType,filtering); else dye=resizeDoubleFBO(dye,dyeRes.width,dyeRes.height,rgba.internalFormat,rgba.format,texType,filtering);if(!velocity) velocity=createDoubleFBO(simRes.width,simRes.height,rg.internalFormat,rg.format,texType,filtering); else velocity=resizeDoubleFBO(velocity,simRes.width,simRes.height,rg.internalFormat,rg.format,texType,filtering);divergence=createFBO(simRes.width,simRes.height,r.internalFormat,r.format,texType,gl.NEAREST); curl=createFBO(simRes.width,simRes.height,r.internalFormat,r.format,texType,gl.NEAREST); pressure=createDoubleFBO(simRes.width,simRes.height,r.internalFormat,r.format,texType,gl.NEAREST); }function createFBO(w,h,internalFormat,format,type,param){ gl.activeTexture(gl.TEXTURE0); let texture=gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D,texture); gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,param); gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,param); gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE); gl.texImage2D(gl.TEXTURE_2D,0,internalFormat,w,h,0,format,type,null);let fbo=gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER,fbo); gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,texture,0); gl.viewport(0,0,w,h); gl.clear(gl.COLOR_BUFFER_BIT);let texelSizeX=1.0/w, texelSizeY=1.0/h; return { texture,fbo,width:w,height:h,texelSizeX,texelSizeY, attach(id){ gl.activeTexture(gl.TEXTURE0+id); gl.bindTexture(gl.TEXTURE_2D,texture); return id; } }; }function createDoubleFBO(w,h,internalFormat,format,type,param){ let fbo1=createFBO(w,h,internalFormat,format,type,param); let fbo2=createFBO(w,h,internalFormat,format,type,param); return { width:w,height:h,texelSizeX:fbo1.texelSizeX,texelSizeY:fbo1.texelSizeY, get read(){return fbo1;}, set read(v){fbo1=v;}, get write(){return fbo2;}, set write(v){fbo2=v;}, swap(){ let t=fbo1; fbo1=fbo2; fbo2=t; } }; }function resizeFBO(target,w,h,internalFormat,format,type,param){ let newFBO=createFBO(w,h,internalFormat,format,type,param); copyProgram.bind(); gl.uniform1i(copyProgram.uniforms.uTexture,target.attach(0)); blit(newFBO); return newFBO; }function resizeDoubleFBO(target,w,h,internalFormat,format,type,param){ if(target.width===w&&target.height===h) return target; target.read=resizeFBO(target.read,w,h,internalFormat,format,type,param); target.write=createFBO(w,h,internalFormat,format,type,param); target.width=w; target.height=h; target.texelSizeX=1.0/w; target.texelSizeY=1.0/h; return target; }function updateKeywords(){ let k=[]; if(config.SHADING) k.push('SHADING'); displayMaterial.setKeywords(k); }updateKeywords(); initFramebuffers();let lastUpdateTime=Date.now(), colorUpdateTimer=0.0, rafId;function updateFrame(){ const dt=calcDeltaTime(); if(resizeCanvas()) initFramebuffers(); updateColors(dt); applyInputs(); step(dt); render(null); rafId=requestAnimationFrame(updateFrame); }function calcDeltaTime(){ let now=Date.now(); let dt=(now-lastUpdateTime)/1000; dt=Math.min(dt,0.016666); lastUpdateTime=now; return dt; }function resizeCanvas(){ let width=scaleByPixelRatio(canvas.clientWidth); let height=scaleByPixelRatio(canvas.clientHeight); if(canvas.width!==width||canvas.height!==height){ canvas.width=width; canvas.height=height; return true; } return false; }function updateColors(dt){ colorUpdateTimer+=dt*config.COLOR_UPDATE_SPEED; if(colorUpdateTimer>=1){ colorUpdateTimer=wrap(colorUpdateTimer,0,1); pointers.forEach(p=>{ p.color=generateColor(); }); } }function applyInputs(){ pointers.forEach(p=>{ if(p.moved){ p.moved=false; splatPointer(p); } }); }function step(dt){ gl.disable(gl.BLEND);curlProgram.bind(); gl.uniform2f(curlProgram.uniforms.texelSize,velocity.texelSizeX,velocity.texelSizeY); gl.uniform1i(curlProgram.uniforms.uVelocity,velocity.read.attach(0)); blit(curl);vorticityProgram.bind(); gl.uniform2f(vorticityProgram.uniforms.texelSize,velocity.texelSizeX,velocity.texelSizeY); gl.uniform1i(vorticityProgram.uniforms.uVelocity,velocity.read.attach(0)); gl.uniform1i(vorticityProgram.uniforms.uCurl,curl.attach(1)); gl.uniform1f(vorticityProgram.uniforms.curl,config.CURL); gl.uniform1f(vorticityProgram.uniforms.dt,dt); blit(velocity.write); velocity.swap();divergenceProgram.bind(); gl.uniform2f(divergenceProgram.uniforms.texelSize,velocity.texelSizeX,velocity.texelSizeY); gl.uniform1i(divergenceProgram.uniforms.uVelocity,velocity.read.attach(0)); blit(divergence);clearProgram.bind(); gl.uniform1i(clearProgram.uniforms.uTexture,pressure.read.attach(0)); gl.uniform1f(clearProgram.uniforms.value,config.PRESSURE); blit(pressure.write); pressure.swap();pressureProgram.bind(); gl.uniform2f(pressureProgram.uniforms.texelSize,velocity.texelSizeX,velocity.texelSizeY); gl.uniform1i(pressureProgram.uniforms.uDivergence,divergence.attach(0)); for(let i=0;i<config.PRESSURE_ITERATIONS;i++){ gl.uniform1i(pressureProgram.uniforms.uPressure,pressure.read.attach(1)); blit(pressure.write); pressure.swap(); }gradienSubtractProgram.bind(); gl.uniform2f(gradienSubtractProgram.uniforms.texelSize,velocity.texelSizeX,velocity.texelSizeY); gl.uniform1i(gradienSubtractProgram.uniforms.uPressure,pressure.read.attach(0)); gl.uniform1i(gradienSubtractProgram.uniforms.uVelocity,velocity.read.attach(1)); blit(velocity.write); velocity.swap();advectionProgram.bind(); gl.uniform2f(advectionProgram.uniforms.texelSize,velocity.texelSizeX,velocity.texelSizeY); if(!ext.supportLinearFiltering) gl.uniform2f(advectionProgram.uniforms.dyeTexelSize,velocity.texelSizeX,velocity.texelSizeY); let velocityId=velocity.read.attach(0); gl.uniform1i(advectionProgram.uniforms.uVelocity,velocityId); gl.uniform1i(advectionProgram.uniforms.uSource,velocityId); gl.uniform1f(advectionProgram.uniforms.dt,dt); gl.uniform1f(advectionProgram.uniforms.dissipation,config.VELOCITY_DISSIPATION); blit(velocity.write); velocity.swap();if(!ext.supportLinearFiltering) gl.uniform2f(advectionProgram.uniforms.dyeTexelSize,dye.texelSizeX,dye.texelSizeY); gl.uniform1i(advectionProgram.uniforms.uVelocity,velocity.read.attach(0)); gl.uniform1i(advectionProgram.uniforms.uSource,dye.read.attach(1)); gl.uniform1f(advectionProgram.uniforms.dissipation,config.DENSITY_DISSIPATION); blit(dye.write); dye.swap(); }function render(target){ gl.blendFunc(gl.ONE,gl.ONE_MINUS_SRC_ALPHA); gl.enable(gl.BLEND); drawDisplay(target); }function drawDisplay(target){ let width=target==null?gl.drawingBufferWidth:target.width; let height=target==null?gl.drawingBufferHeight:target.height; displayMaterial.bind(); if(config.SHADING) gl.uniform2f(displayMaterial.uniforms.texelSize,1.0/width,1.0/height); gl.uniform1i(displayMaterial.uniforms.uTexture,dye.read.attach(0)); blit(target); }function splatPointer(pointer){ let dx=pointer.deltaX*config.SPLAT_FORCE; let dy=pointer.deltaY*config.SPLAT_FORCE; splat(pointer.texcoordX,pointer.texcoordY,dx,dy,pointer.color); }function splat(x,y,dx,dy,color){ splatProgram.bind(); gl.uniform1i(splatProgram.uniforms.uTarget,velocity.read.attach(0)); gl.uniform1f(splatProgram.uniforms.aspectRatio,canvas.width/canvas.height); gl.uniform2f(splatProgram.uniforms.point,x,y); gl.uniform3f(splatProgram.uniforms.color,dx,dy,0.0); gl.uniform1f(splatProgram.uniforms.radius,correctRadius(config.SPLAT_RADIUS/100.0)); blit(velocity.write); velocity.swap(); gl.uniform1i(splatProgram.uniforms.uTarget,dye.read.attach(0)); gl.uniform3f(splatProgram.uniforms.color,color.r,color.g,color.b); blit(dye.write); dye.swap(); }function correctRadius(radius){ let a=canvas.width/canvas.height; if(a>1) radius*=a; return radius; }function burst(cx0,cy0,amount){ const px=scaleByPixelRatio(cx0), py=scaleByPixelRatio(cy0); const tx=px/canvas.width, ty=1.0-py/canvas.height;for(let i=0;i<amount;i++){ const col=generateColor(); col.r*=9; col.g*=9; col.b*=9; const dx=900*(Math.random()-0.5), dy=900*(Math.random()-0.5); splat(tx,ty,dx,dy,col); } }function updatePointerMoveData(pointer,posX,posY,color){ pointer.prevTexcoordX=pointer.texcoordX; pointer.prevTexcoordY=pointer.texcoordY; pointer.texcoordX=posX/canvas.width; pointer.texcoordY=1.0-posY/canvas.height; pointer.deltaX=correctDeltaX(pointer.texcoordX-pointer.prevTexcoordX); pointer.deltaY=correctDeltaY(pointer.texcoordY-pointer.prevTexcoordY); pointer.moved=Math.abs(pointer.deltaX)>0||Math.abs(pointer.deltaY)>0; pointer.color=color; }function correctDeltaX(delta){ let a=canvas.width/canvas.height; if(a<1) delta*=a; return delta; }function correctDeltaY(delta){ let a=canvas.width/canvas.height; if(a>1) delta/=a; return delta; }function generateColor(){ let h=0.09+Math.random()*0.055; let c=HSVtoRGB(h,0.92,1.0); c.r*=0.22; c.g*=0.16; c.b*=0.05; return c; }function HSVtoRGB(h,s,v){ let r,g,b,i,f,p,q,t; i=Math.floor(h*6); f=h*6-i; p=v*(1-s); q=v*(1-f*s); t=v*(1-(1-f)*s); switch(i%6){ case 0:r=v;g=t;b=p;break; case 1:r=q;g=v;b=p;break; case 2:r=p;g=v;b=t;break; case 3:r=p;g=q;b=v;break; case 4:r=t;g=p;b=v;break; case 5:r=v;g=p;b=q;break; } return {r,g,b}; }function wrap(value,min,max){ const range=max-min; if(range===0) return min; return ((value-min)%range)+min; }function getResolution(resolution){ let a=gl.drawingBufferWidth/gl.drawingBufferHeight; if(a<1) a=1.0/a; const min=Math.round(resolution); const max=Math.round(resolution*a); if(gl.drawingBufferWidth>gl.drawingBufferHeight) return {width:max,height:min}; else return {width:min,height:max}; }function scaleByPixelRatio(input){ const pr=window.devicePixelRatio||1; return Math.floor(input*pr); }function hashCode(s){ if(s.length===0) return 0; let hash=0; for(let i=0;i<s.length;i++){ hash=(hash<<5)-hash+s.charCodeAt(i); hash|=0; } return hash; }/* ============================ INTERACTION: hover card -> tilt + splash ============================ */const tiltEl=document.getElementById('tilt');const glare=document.querySelector('.glare');const cursor=document.getElementById('cursor');let firstMove=false, wasOver=false;function onMove(cx0,cy0){// golden mouse — follows the cursor everywhere on screencursor.style.left=cx0+'px'; cursor.style.top=cy0+'px';const r=tiltEl.getBoundingClientRect();const cx=r.left+r.width/2, cy=r.top+r.height/2;const nx=(cx0-cx)/(r.width/2), ny=(cy0-cy)/(r.height/2);const over=Math.abs(nx)<=1.02 && Math.abs(ny)<=1.06;if(over){// over the card: 3D tilt + its own shine + a brighter golden cursor. No fluid spread.tiltEl.style.transform='rotateX('+(-ny*10).toFixed(2)+'deg) rotateY('+(nx*12).toFixed(2)+'deg)';glare.style.opacity='0.55';glare.style.setProperty('--gx',(50+nx*42)+'%');glare.style.setProperty('--gy',(50+ny*42)+'%');cursor.classList.add('on');} else {tiltEl.style.transform='rotateX(0deg) rotateY(0deg)';glare.style.opacity='0';cursor.classList.remove('on');}// keep the fluid pointer synced everywhere (so there's no jump leaving the card),// but only let it paint its subtle trail when NOT over the card.const posX=scaleByPixelRatio(cx0), posY=scaleByPixelRatio(cy0);const col = firstMove ? pointers[0].color : generateColor();updatePointerMoveData(pointers[0], posX, posY, col); firstMove=true;if(over) pointers[0].moved=false;wasOver=over;}window.addEventListener('mousemove', e=>onMove(e.clientX,e.clientY));window.addEventListener('pointermove', e=>onMove(e.clientX,e.clientY));window.addEventListener('touchmove', e=>{ const t=e.touches[0]; if(t) onMove(t.clientX,t.clientY); }, {passive:true});updateFrame();})();</script></body></html>`;export default async function handler() {return new Response(HTML, { status: 200, headers: { "content-type": "text/html; charset=utf-8" } });}
Fork this and make it yours
Describe a change in plain English and QuikRun deploys it to your own URL.
Built on QuikRun by @naman-parashar-s-team · plain English in, live URL out