Sterrenstof dat versnelt als je scrolt New
Stars drift calmly toward you; as soon as you scroll, turn the wheel or swipe on a phone they shoot past as streaks and then slow down again. The button does the same for keyboard users. For the hero of an observatory, an evening event or festival, a space workshop or a loading screen that should not be dull. Canvas 2D; with reduced motion you get a still starry sky.
Sterrenkijken in Lauwersoog
Vrijdag 22 november · € 12,50 per persoon
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.
<!-- Sterrenstof dat versnelt als je scrolt · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie -->
<div class="jouw-container">
<div class="sts">
<canvas class="sts__cv" aria-hidden="true"></canvas>
<div class="sts__txt"><p class="sts__h">Sterrenkijken in Lauwersoog</p><p class="sts__p">Vrijdag 22 november · € 12,50 per persoon</p></div>
<button class="sts__btn" type="button">Versnel</button>
</div>
</div>/* Sterrenstof dat versnelt als je scrolt · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie */
.sts{position:absolute;inset:0;background:#0a0a0a;font-family:Switzer,sans-serif;color:#fff;overflow:hidden}
.sts__cv{position:absolute;inset:0;width:100%;height:100%;display:block}
.sts__txt{position:absolute;left:16px;right:16px;bottom:14px;pointer-events:none}
.sts__h{margin:0;font:600 18px/1.2 Switzer,sans-serif}
.sts__p{margin:4px 0 0;font:400 14px/1.35 Switzer,sans-serif;color:#d6d4d0}
.sts__btn{position:absolute;top:14px;right:14px}
.sts__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:#fff;color:#0a0a0a;transition:background-color .18s,color .18s}
.sts__btn:hover{background:#F5480F;color:#0a0a0a}
.sts__btn:focus-visible{outline:2px solid #fff;outline-offset:3px}
@media (prefers-reduced-motion: reduce){.sts__btn{transition:none}}// Sterrenstof dat versnelt als je scrolt · © 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('.sts'), cv = w.querySelector('.sts__cv'), ctx = cv.getContext('2d');
var W = 1, H = 1, st = [], boost = 0, base = .06, sy = window.scrollY, ty = null;
function mk(s, far) { s.x = Math.random() * 2 - 1; s.y = Math.random() * 2 - 1; s.z = far ? 1 : .05 + Math.random() * .95; s.o = Math.random() < .08; }
function draw(dt) {
ctx.fillStyle = '#0a0a0a'; ctx.fillRect(0, 0, W, H);
var cx = W / 2, cy = H / 2, f = Math.max(W, H) * .5, v = base + boost; ctx.lineCap = 'round';
for (var i = 0; i < st.length; i++) {
var s = st[i], z0 = s.z; s.z -= v * dt;
if (s.z <= .03) { mk(s, true); continue; }
var x = cx + s.x * f / s.z, y = cy + s.y * f / s.z;
if (x < -20 || x > W + 20 || y < -20 || y > H + 20) { mk(s, true); continue; }
var x0 = cx + s.x * f / z0, y0 = cy + s.y * f / z0, near = 1 - s.z;
ctx.strokeStyle = s.o ? 'rgba(245,72,15,' + Math.min(1, .25 + near) + ')' : 'rgba(255,255,255,' + Math.min(1, .2 + near * 1.1).toFixed(3) + ')';
ctx.lineWidth = .6 + near * 2.2;
ctx.beginPath(); ctx.moveTo(x0, y0); ctx.lineTo(x + .01, y + .01); ctx.stroke();
}
}
fit(cv, ctx, function (a, b) {
W = a; H = b; var n = Math.round(Math.min(380, W * H / 420)); st = [];
for (var i = 0; i < n; i++) { var s = {}; mk(s); st.push(s); }
draw(.016);
});
function kick(v) { if (!reduce) boost = Math.min(2.4, boost + v); }
w.addEventListener('wheel', function (e) { kick(Math.min(120, Math.abs(e.deltaY)) * .004); }, { passive: true });
window.addEventListener('scroll', function () { var y = window.scrollY; kick(Math.min(200, Math.abs(y - sy)) * .003); sy = y; }, { passive: true });
w.addEventListener('touchstart', function (e) { ty = e.touches[0].clientY; }, { passive: true });
w.addEventListener('touchmove', function (e) { var y = e.touches[0].clientY; if (ty != null) kick(Math.min(80, Math.abs(y - ty)) * .01); ty = y; }, { passive: true });
w.querySelector('.sts__btn').addEventListener('click', function () { kick(1.4); });
loop(w, function (dt) { if (reduce) return; boost *= Math.exp(-1.6 * dt); draw(dt); });
}
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 →