Zonlicht door bladeren (komorebi) New
Warm patches of sunlight through leaves move softly across a light wall, like a garden room on a June afternoon; move your mouse and a breeze stirs the branches. For the hero of a yoga studio, physiotherapist, B&B, garden centre or funeral home, where calm matters more than imagery. One fragment shader with OGL; without WebGL a few fixed CSS light patches remain, with reduced motion they stand still.
How to use it
- Paste the HTML where the backgrounds & webgl should appear. Include the script tags at the top once per page.
- 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.
<!-- Zonlicht door bladeren (komorebi) · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie -->
<!-- benodigd, één keer per pagina: -->
<script type="module">import * as OGL from "https://esm.sh/ogl@1.0.11"; window.OGL = OGL;</script>
<div class="jouw-container">
<div class="kmb">
<div class="kmb__css" aria-hidden="true"></div>
<div class="kmb__txt"><p class="kmb__h">Yogastudio Lindehof, Zeist</p><p class="kmb__p">Ochtendles in de tuin, dinsdag en donderdag om 7.30 uur</p><a class="kmb__btn" href="#">Plan een proefles</a></div>
</div>
</div>/* Zonlicht door bladeren (komorebi) · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie */
.kmb{position:absolute;inset:0;background:#f4f3f1;font-family:Switzer,sans-serif;color:#0a0a0a;overflow:hidden}
.kmb__css{position:absolute;inset:-10%;background:radial-gradient(14% 18% at 22% 30%,rgba(255,246,232,.95),rgba(255,246,232,0) 70%),radial-gradient(10% 13% at 46% 52%,rgba(255,244,228,.9),rgba(255,244,228,0) 70%),radial-gradient(18% 20% at 72% 26%,rgba(255,246,232,.9),rgba(255,246,232,0) 70%),radial-gradient(9% 12% at 84% 64%,rgba(255,240,220,.85),rgba(255,240,220,0) 70%),radial-gradient(12% 15% at 30% 78%,rgba(255,246,232,.8),rgba(255,246,232,0) 70%);animation:kmb-wieg 9s ease-in-out infinite alternate}
@keyframes kmb-wieg{to{transform:translate(2%,1.5%) rotate(1deg)}}
.kmb__cv{position:absolute;inset:0;width:100%;height:100%;display:block}
.kmb__txt{position:absolute;left:16px;right:16px;bottom:14px;z-index:1;max-width:420px}
.kmb__h{margin:0;font:600 18px/1.2 Switzer,sans-serif}
.kmb__p{margin:4px 0 10px;font:400 14px/1.35 Switzer,sans-serif;color:#2c2c2c}
.kmb__btn{display:inline-flex;align-items:center;min-height:36px;padding:0 14px;border:0;border-radius:4px;font:600 14px/1 Switzer,sans-serif;cursor:pointer;text-decoration:none;background:#0a0a0a;color:#fff;transition:background-color .18s,color .18s}
.kmb__btn:hover{background:#F5480F;color:#0a0a0a}
.kmb__btn:focus-visible{outline:2px solid #F5480F;outline-offset:3px}
@media (prefers-reduced-motion: reduce){.kmb__css{animation:none}.kmb__btn{transition:none}}// Zonlicht door bladeren (komorebi) · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie
// root = het element waarin de HTML staat
function init(root){
var reduce=!!(window.matchMedia&&matchMedia('(prefers-reduced-motion: reduce)').matches);
function fit(cv,ctx,cb){var dpr=Math.min(window.devicePixelRatio||1,2);function f(){var r=cv.getBoundingClientRect();var w=Math.max(1,Math.round(r.width)),h=Math.max(1,Math.round(r.height));if(cv.width===Math.round(w*dpr)&&cv.height===Math.round(h*dpr))return;cv.width=Math.round(w*dpr);cv.height=Math.round(h*dpr);if(ctx)ctx.setTransform(dpr,0,0,dpr,0,0);if(cb)cb(w,h,dpr);}if(window.ResizeObserver)new ResizeObserver(f).observe(cv);f();}
function loop(el,step){var on=false,raf=0,last=0;function fr(t){raf=requestAnimationFrame(fr);var dt=last?Math.min(.05,(t-last)/1000):.016;last=t;step(dt,t/1000);}function go(v){if(v&&!on){on=true;last=0;raf=requestAnimationFrame(fr);}else if(!v&&on){on=false;cancelAnimationFrame(raf);}}if(window.IntersectionObserver)new IntersectionObserver(function(es){go(es[0].isIntersecting);}).observe(el);else go(true);}
var w = root.querySelector('.kmb'), fb = w.querySelector('.kmb__css');
w.querySelector('.kmb__btn').addEventListener('click', function (e) { e.preventDefault(); });
if (typeof OGL === 'undefined') return;
var cv = document.createElement('canvas'); cv.className = 'kmb__cv'; cv.setAttribute('aria-hidden', 'true');
var renderer;
try { renderer = new OGL.Renderer({ canvas: cv, dpr: Math.min(window.devicePixelRatio || 1, 1.5), alpha: false }); } catch (e) { return; }
var gl = renderer.gl; if (!gl) return;
fb.replaceWith(cv);
var program = new OGL.Program(gl, {
vertex: 'attribute vec2 uv;attribute vec2 position;varying vec2 vUv;void main(){vUv=uv;gl_Position=vec4(position,0.,1.);}',
fragment: 'precision highp float;varying vec2 vUv;uniform float uT;uniform vec2 uRes;uniform vec2 uWind;' +
'vec2 h2(vec2 p){p=vec2(dot(p,vec2(127.1,311.7)),dot(p,vec2(269.5,183.3)));return fract(sin(p)*43758.5453);}' +
'float h1(vec2 p){return fract(sin(dot(p,vec2(12.9898,78.233)))*43758.5453);}' +
'float vn(vec2 p){vec2 i=floor(p),f=fract(p);f=f*f*(3.-2.*f);return mix(mix(h1(i),h1(i+vec2(1,0)),f.x),mix(h1(i+vec2(0,1)),h1(i+vec2(1,1)),f.x),f.y);}' +
'float fbm(vec2 p){float a=.5,s=0.;for(int i=0;i<4;i++){s+=a*vn(p);p*=2.03;a*=.5;}return s;}' +
'void main(){vec2 uv=vUv;float ar=uRes.x/uRes.y;vec2 p=vec2(uv.x*ar,uv.y);' +
'vec2 sway=vec2(sin(uT*.35),cos(uT*.27))*.08+uWind*.12+vec2(fbm(p*1.3+uT*.05)-.5)*.25;' +
'vec2 q=(p+sway)*vec2(5.,3.6);vec2 g=floor(q),f=fract(q);float L=0.;' +
'for(int j=-1;j<=1;j++)for(int i=-1;i<=1;i++){vec2 o=vec2(float(i),float(j));vec2 h=h2(g+o);' +
'vec2 pt=o+.5+.32*sin(uT*.5*(.6+h)+6.2831*h);float r=.2+.22*h.x;float d=length((pt-f)*vec2(1.,1.25));L+=smoothstep(r,r*.45,d);}' +
'float canopy=smoothstep(.22,.58,fbm(p*1.6+vec2(uT*.03,0.)+uWind*.2));float light=clamp(L,0.,1.)*canopy;' +
'vec3 wall=vec3(.93,.92,.90)*(.94+.06*uv.y);vec3 sun=vec3(1.,.965,.91);vec3 rim=vec3(.961,.282,.059);' +
'vec3 c=mix(wall*.86,sun,light);c=mix(c,rim,smoothstep(.0,.25,light)*smoothstep(.55,.25,light)*.035);gl_FragColor=vec4(c,1.);}',
uniforms: { uT: { value: 0 }, uRes: { value: [1, 1] }, uWind: { value: [0, 0] } }
});
var mesh = new OGL.Mesh(gl, { geometry: new OGL.Triangle(gl), program: program });
var wind = [0, 0], tw = [0, 0], t = 7;
function size() { var r = w.getBoundingClientRect(); renderer.setSize(Math.max(1, r.width), Math.max(1, r.height)); program.uniforms.uRes.value = [Math.max(1, r.width), Math.max(1, r.height)]; renderer.render({ scene: mesh }); }
if (window.ResizeObserver) new ResizeObserver(size).observe(w); size();
w.addEventListener('pointermove', function (e) { var r = w.getBoundingClientRect(); tw = [((e.clientX - r.left) / r.width - .5) * 2, ((e.clientY - r.top) / r.height - .5) * 2]; });
w.addEventListener('pointerleave', function () { tw = [0, 0]; });
loop(w, function (dt) {
if (reduce) return;
t += dt; var k = Math.min(1, dt * 1.5); wind[0] += (tw[0] - wind[0]) * k; wind[1] += (tw[1] - wind[1]) * k;
program.uniforms.uT.value = t; program.uniforms.uWind.value = [wind[0], wind[1]]; renderer.render({ scene: mesh });
});
}
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 →