wtc-gl · ScrollRenderer
recipe · scroll-renderer
v2.3.1 · WebGL2
one canvas · n scenes
made by we the collective

One canvas,
Every scene

Anchor independent WebGL programs to any DOM element. A single fixed canvas, scissor-tested viewports, one shared render loop. ScrollRenderer is a WebGL scene manager for inline effects that scale.

npm install wtc-gl
01 / How it works

No per-element canvases, just one shared GL context

Each ScrollScene tracks a DOM element via getBoundingClientRect(). WebGL's scissor test clips each render to that element's exact pixel bounds.

Scenes outside the viewport are culled automatically by IntersectionObserver, so performance issues end up being minimal.

01 u_origin

DOM-anchored

Every scene knows its element. u_origin and u_resolution update each frame automatically, no event wiring needed.

02 scissor

Scissor-tested viewports

The rings above centre on this card's exact position in the canvas using u_origin.xy and gl_FragCoord.

03 rAF

One render loop

A single requestAnimationFrame drives every scene. Zero per-canvas overhead, no scheduling jank.

02 / The problem

Multiple WebGL programs don't scale

The traditional answer to multiple WebGL programs on one page is one <canvas> per effect, a separate GPU context for each. Browsers cap active WebGL contexts. Each one competes for GPU memory, initialises its own GL state, and drives its own render loop with zero coordination.

On top of CPU, GPU, and memory load, browsers drop contexts to reclaim resources which means effects disappear without warning. You end up building fragile context managers around the symptom.

ScrollRenderer solves all of this with a single shared context and one coordinated render loop with a coordinator that provides all the required functionality.

scroll-driven · u_cp
04 / Composited layers

Your markup and your WebGL laid out together

The HTML document lays the page out, including the placeholders that anchor WebGL programs, which composite into one shared full-screen canvas coordinated by a single renderer.

Source code on a screen
05 / API

Three lines to anchor a shader to any element

Extend with onBeforeRender and onAfterRender hooks, or opt out of per-scene clearing and viewport locking independently. Uniforms update automatically.

scene.js · 12 lines
const renderer = new ScrollRenderer()
const scene    = new ScrollScene({ element, scene: drawable })
renderer.addScene(scene)

// Uniforms updated automatically every frame:
// u_time        float  - elapsed time
// u_resolution  vec2   - element size in physical px
// u_origin      vec4   - .xy gl_FragCoord origin
//                       .zw element centre in NDC
06 / Showcase
GPU-
side.

The TransformFeedback class handles ping-pong buffers automatically. Complex particle systems run in the shared context alongside fragment-shader scenes with no extra overhead.

  • Curl-noise velocity field, GPU-side
  • Per-particle life, respawn, and seed
  • Composites over adjacent scenes via clearOnRender: false
  • Positioned with u_origin.zw when useViewport is false
scene · particles
07 / Get started

Simple to use,
light and unopinionated

ScrollRenderer is part of wtc-gl: a lightweight WebGL library by We The Collective.