← quik.run
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
Endpointquik.run/r/golden-cardRunFork
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 &middot; <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 metal
let 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-lines
c.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é rosette
c.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();
// chip
const 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();
// contactless
c.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 patch
const 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 — personalised
c.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 helper
function 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 mark
const 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 SHADING
vec3 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;
#endif
float 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_FILTERING
vec2 coord=vUv-dt*bilerp(uVelocity,vUv,texelSize).xy*texelSize; vec4 result=bilerp(uSource,coord,dyeTexelSize);
#else
vec2 coord=vUv-dt*texture2D(uVelocity,vUv).xy*texelSize; vec4 result=texture2D(uSource,coord);
#endif
float 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 screen
cursor.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.

Fork snippet

Built on QuikRun by @naman-parashar-s-team · plain English in, live URL out