Fluted Glass New
Fluted glass over an image: each rib refracts differently and shifts with the cursor. Works well as a hero or mood image for interior or architecture studios.
Beweeg over het glas
How to use it
- Paste the HTML where the backgrounds & webgl should appear.
- Add the CSS to your stylesheet. All classes are prefixed, so nothing clashes with your own styles.
- Add the JavaScript and call init() with the element that contains the HTML.
<!-- Fluted Glass · Bron: Paper Shaders · licentie Apache-2.0. Laat deze vermelding staan bij hergebruik. -->
<div class="jouw-container">
<div class="flg"><div class="flg__fb"></div><canvas class="flg__gl" aria-hidden="true"></canvas><span class="flg__tag">Beweeg over het glas</span></div>
</div>/* Fluted Glass · Bron: Paper Shaders · licentie Apache-2.0. Laat deze vermelding staan bij hergebruik. */
.flg{position:absolute;inset:0;overflow:hidden;background:#f4f3f1;font-family:'Switzer',system-ui,sans-serif}
.flg__gl,.flg__fb,.flg__fb canvas{position:absolute;inset:0;width:100%;height:100%;display:block}
.flg.is-nogl .flg__gl{display:none}
.flg__tag{position:absolute;left:12px;bottom:12px;z-index:3;font:500 10px/1 'JetBrains Mono',monospace;letter-spacing:.14em;text-transform:uppercase;background:#0a0a0a;color:#fff;padding:6px 8px;border-radius:3px}
.flg__ctl{position:absolute;right:12px;bottom:12px;z-index:3;display:flex;align-items:center;gap:8px;background:#fff;border:1px solid rgba(10,10,10,.14);border-radius:4px;padding:6px 9px;font:500 10px/1 'JetBrains Mono',monospace;letter-spacing:.1em;text-transform:uppercase;color:#0a0a0a}
.flg__ctl input{width:84px;accent-color:#F5480F;margin:0}
.flg.is-nogl .flg__ctl{display:none}
.flg__fb::after{content:'';position:absolute;inset:0;background:repeating-linear-gradient(90deg,rgba(255,255,255,.32) 0,rgba(255,255,255,0) 35%,rgba(10,10,10,.16) 100%);background-size:22px 100%;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}// Fluted Glass · Bron: Paper Shaders · licentie Apache-2.0. Laat deze vermelding staan bij hergebruik.
// root = het element waarin de HTML staat
function init(root){
function mkGL(cv, frag) {
var gl = null;
try { gl = cv.getContext('webgl', { antialias: false, premultipliedAlpha: false }) || cv.getContext('experimental-webgl'); } catch (e) { gl = null; }
if (!gl) return null;
function sh(t, s) { var o = gl.createShader(t); gl.shaderSource(o, s); gl.compileShader(o); if (!gl.getShaderParameter(o, gl.COMPILE_STATUS)) throw new Error(gl.getShaderInfoLog(o) || 'shader'); return o; }
var pr = gl.createProgram();
try {
gl.attachShader(pr, sh(gl.VERTEX_SHADER, 'attribute vec2 p;varying vec2 vUv;void main(){vUv=p*.5+.5;gl_Position=vec4(p,0.,1.);}'));
gl.attachShader(pr, sh(gl.FRAGMENT_SHADER, 'precision highp float;varying vec2 vUv;' + frag));
gl.linkProgram(pr);
if (!gl.getProgramParameter(pr, gl.LINK_STATUS)) throw new Error('link');
} catch (e) { console.warn('lab webgl', e); return null; }
gl.useProgram(pr);
var buf = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW);
var loc = gl.getAttribLocation(pr, 'p'); gl.enableVertexAttribArray(loc); gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);
var U = {};
var api = {
gl: gl,
u: function (n) { if (!(n in U)) U[n] = gl.getUniformLocation(pr, n); return U[n]; },
upd: function (t, src, unit) {
gl.activeTexture(gl.TEXTURE0 + unit); gl.bindTexture(gl.TEXTURE_2D, t);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, src);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
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);
},
tex: function (src, unit, name) { var t = gl.createTexture(); api.upd(t, src, unit); gl.uniform1i(api.u(name), unit); return t; },
fit: function () {
var d = Math.min(window.devicePixelRatio || 1, 2), w = Math.max(1, Math.round(cv.clientWidth * d)), h = Math.max(1, Math.round(cv.clientHeight * d));
if (cv.width !== w || cv.height !== h) { cv.width = w; cv.height = h; }
gl.viewport(0, 0, w, h); gl.uniform2f(api.u('uRes'), w, h); return d;
},
draw: function () { gl.drawArrays(gl.TRIANGLES, 0, 3); }
};
return api;
}
function runLoop(el, frame, still) {
var reduce = matchMedia('(prefers-reduced-motion:reduce)').matches, vis = true, raf = 0, t0 = performance.now();
function tick(now) { raf = 0; frame((now - t0) / 1000); if (!reduce && !still && vis) raf = requestAnimationFrame(tick); }
if ('IntersectionObserver' in window) new IntersectionObserver(function (es) { vis = es[0].isIntersecting; if (vis && !raf) raf = requestAnimationFrame(tick); }).observe(el);
raf = requestAnimationFrame(tick);
return { reduce: reduce, kick: function () { if (!raf) raf = requestAnimationFrame(tick); } };
}
function paintScene(c, v) {
var x = c.getContext('2d'), W = c.width, H = c.height, night = v === 1;
var bg = x.createLinearGradient(0, 0, 0, H);
if (night) { bg.addColorStop(0, '#0a0a0a'); bg.addColorStop(1, '#2b1b12'); } else { bg.addColorStop(0, '#f4f3f1'); bg.addColorStop(1, '#ddd5c9'); }
x.fillStyle = bg; x.fillRect(0, 0, W, H);
var sx = W * .68, sy = H * .44, sr = H * .27;
var sg = x.createRadialGradient(sx - sr * .35, sy - sr * .35, sr * .1, sx, sy, sr);
if (night) { sg.addColorStop(0, '#ffffff'); sg.addColorStop(1, '#cfc8bd'); } else { sg.addColorStop(0, '#ffa071'); sg.addColorStop(1, '#F5480F'); }
x.fillStyle = sg; x.beginPath(); x.arc(sx, sy, sr, 0, 7); x.fill();
x.fillStyle = night ? '#7a2a0c' : '#6b645d';
x.beginPath(); x.moveTo(0, H * .72); x.bezierCurveTo(W * .25, H * .58, W * .45, H * .78, W * .7, H * .66); x.bezierCurveTo(W * .85, H * .6, W * .95, H * .66, W, H * .64); x.lineTo(W, H); x.lineTo(0, H); x.fill();
x.fillStyle = night ? '#F5480F' : '#0a0a0a';
x.beginPath(); x.moveTo(0, H * .84); x.bezierCurveTo(W * .3, H * .74, W * .6, H * .92, W, H * .8); x.lineTo(W, H); x.lineTo(0, H); x.fill();
x.strokeStyle = night ? '#f4f3f1' : '#0a0a0a'; x.lineWidth = H * .012;
x.beginPath(); x.moveTo(W * .2, H * .8); x.lineTo(W * .2, H * .34); x.lineTo(W * .44, H * .34); x.moveTo(W * .16, H * .34); x.lineTo(W * .2, H * .34); x.moveTo(W * .4, H * .34); x.lineTo(W * .4, H * .5); x.stroke();
x.fillStyle = night ? '#f4f3f1' : '#0a0a0a';
x.fillRect(W * .385, H * .5, W * .03, H * .05);
x.textBaseline = 'top';
x.font = '400 ' + Math.round(H * .08) + "px Boldonse, Switzer, sans-serif"; x.fillText('HAVENKADE', W * .06, H * .08);
x.font = '500 ' + Math.round(H * .034) + "px 'JetBrains Mono', monospace"; x.fillText(night ? 'NACHTVERSIE · ROTTERDAM' : 'DAGVERSIE · ROTTERDAM', W * .06, H * .22);
}
function fontsThen(cb) {
if (!document.fonts || !document.fonts.load) return;
Promise.all(['400 40px Boldonse', "500 20px 'JetBrains Mono'", '600 20px Switzer'].map(function (f) { return document.fonts.load(f).catch(function () {}); })).then(cb);
}
var w = root.querySelector('.flg'); if (!w) return;
var fb = w.querySelector('.flg__fb'), cv = w.querySelector('.flg__gl');
var sc = document.createElement('canvas'); sc.width = 960; sc.height = 600; paintScene(sc, 0); fb.appendChild(sc);
var g = mkGL(cv, `uniform sampler2D uTex;uniform vec2 uRes;uniform float uT,uM,uN;
void main(){
vec2 uv=vUv;
float x=uv.x*uN, f=fract(x), c=f-.5;
float pan=(uM-.5)*.12+sin(uT*.35)*.015;
float k=-.55/uN;
vec2 s=vec2(uv.x+c*k+pan*(.4+abs(c)), uv.y+sin(x*6.2831)*.004);
vec3 col=vec3(0.);
for(int i=-2;i<=2;i++){float o=float(i)*.0035;
col.r+=texture2D(uTex,vec2(s.x+o+.002*c,s.y)).r;
col.g+=texture2D(uTex,vec2(s.x+o,s.y)).g;
col.b+=texture2D(uTex,vec2(s.x+o-.002*c,s.y)).b;}
col/=5.;
float shade=1.-pow(abs(c)*2.,3.)*.28;
float hl=(1.-smoothstep(0.,.16,abs(c+.24)))*.22;
col=col*shade+hl;
gl_FragColor=vec4(col,1.);
}`);
if (!g) { w.classList.add('is-nogl'); return; }
var tx = g.tex(sc, 0, 'uTex'), mx = .5, tm = .5, L;
w.addEventListener('pointermove', function (e) { var r = w.getBoundingClientRect(); tm = (e.clientX - r.left) / r.width; L.kick(); });
w.addEventListener('pointerleave', function () { tm = .5; L.kick(); });
L = runLoop(w, function (t) {
mx = L && L.reduce ? tm : mx + (tm - mx) * .07;
var d = g.fit();
g.gl.uniform1f(g.u('uT'), t); g.gl.uniform1f(g.u('uM'), mx); g.gl.uniform1f(g.u('uN'), Math.max(9, Math.round(cv.clientWidth / 26)));
g.draw();
});
fontsThen(function () { paintScene(sc, 0); g.upd(tx, sc, 0); L.kick(); });
}
init(document.querySelector('.jouw-container'));
More backgrounds & webgl
Seen something you like?
Add it to your selection and send it over. We build your site around it, in your brand.
Get in touch →