Image Dithering Nieuw
Ordered dithering (Bayer 8x8) in drie huiskleuren, met een lichtvlek die de cursor volgt en een schuif voor de korrel. Geeft foto's een print- of retro-schermkarakter.
Bayer 8x8
Zo gebruik je het
- Plak de HTML op de plek waar het onderdeel moet komen.
- Zet de CSS in je stylesheet. Alle klassen hebben een eigen voorvoegsel, dus niets botst met je eigen opmaak.
- Voeg de JavaScript toe en roep init() aan met het element waar de HTML in staat.
<!-- Image Dithering · Bron: Paper Shaders · licentie Apache-2.0. Laat deze vermelding staan bij hergebruik. -->
<div class="jouw-container">
<div class="idt"><div class="idt__fb"></div><canvas class="idt__gl" aria-hidden="true"></canvas><span class="idt__tag">Bayer 8x8</span><label class="idt__ctl">Korrel <input type="range" min="2" max="8" step="1" value="3" aria-label="Korrelgrootte"></label></div>
</div>/* Image Dithering · Bron: Paper Shaders · licentie Apache-2.0. Laat deze vermelding staan bij hergebruik. */
.idt{position:absolute;inset:0;overflow:hidden;background:#f4f3f1;font-family:'Switzer',system-ui,sans-serif}
.idt__gl,.idt__fb,.idt__fb canvas{position:absolute;inset:0;width:100%;height:100%;display:block}
.idt.is-nogl .idt__gl{display:none}
.idt__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}
.idt__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}
.idt__ctl input{width:84px;accent-color:#F5480F;margin:0}
.idt.is-nogl .idt__ctl{display:none}
.idt__fb canvas{image-rendering:pixelated;filter:contrast(1.6) saturate(1.3)}
.idt__fb::after{content:'';position:absolute;inset:0;background:radial-gradient(circle,rgba(10,10,10,.35) 1px,transparent 1.4px) 0 0/4px 4px;mix-blend-mode:multiply}// Image Dithering · 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('.idt'); if (!w) return;
var fb = w.querySelector('.idt__fb'), cv = w.querySelector('.idt__gl'), rng = w.querySelector('input');
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,uM;uniform float uPx,uT;
float b2(vec2 a){a=floor(a);return fract(dot(a,vec2(.5,a.y*.75)));}
float b4(vec2 a){return b2(.5*a)*.25+b2(a);}
float b8(vec2 a){return b4(.5*a)*.25+b2(a);}
void main(){
vec2 px=floor(gl_FragCoord.xy/uPx);
vec2 uv=(px*uPx+uPx*.5)/uRes;
vec3 c=texture2D(uTex,uv).rgb;
float l=dot(c,vec3(.299,.587,.114));
vec2 a=vec2(uRes.x/uRes.y,1.);
float d=distance(uv*a,uM*a);
l=l*.92+.32*exp(-d*d*7.)-.12;
float q=l+(b8(px)-.5)*.42;
vec3 col=q<.3?vec3(.039):(q<.6?vec3(.961,.282,.059):vec3(.957,.953,.945));
gl_FragColor=vec4(col,1.);
}`);
if (!g) { w.classList.add('is-nogl'); return; }
var tx = g.tex(sc, 0, 'uTex'), mx = .3, my = .6, tx2 = -1, ty2 = -1, L;
w.addEventListener('pointermove', function (e) { var r = w.getBoundingClientRect(); tx2 = (e.clientX - r.left) / r.width; ty2 = 1 - (e.clientY - r.top) / r.height; L.kick(); });
w.addEventListener('pointerleave', function () { tx2 = -1; L.kick(); });
rng.addEventListener('input', function () { L.kick(); });
L = runLoop(w, function (t) {
var ax = tx2 < 0 ? .5 + Math.cos(t * .5) * .3 : tx2, ay = tx2 < 0 ? .55 + Math.sin(t * .7) * .2 : ty2;
if (L && L.reduce) { mx = ax; my = ay; } else { mx += (ax - mx) * .08; my += (ay - my) * .08; }
var d = g.fit();
g.gl.uniform1f(g.u('uPx'), +rng.value * d); g.gl.uniform2f(g.u('uM'), mx, my); g.gl.uniform1f(g.u('uT'), t);
g.draw();
});
fontsThen(function () { paintScene(sc, 0); g.upd(tx, sc, 0); L.kick(); });
}
init(document.querySelector('.jouw-container'));
Meer uit achtergronden & webgl
Iets gezien dat je mooi vindt?
Zet het in je selectie en stuur hem op. Dan bouwen we je site eromheen, in jouw huisstijl.
Neem contact op →