How it's done

The HTML content you're reading right now is overlaid with a full-screen <canvas> element. There is a fragment shader that defines opacity and color for each pixel of the <canvas> . Shader input values are scroll progress (aka animation progress) , time , and resolution .

While time and window size (resolution) are super easy to gather, for animation progress I use GSAP ScrollTrigger plugin.

Once the inputs are prepared, we pass them as uniforms to the shader. The WebGL part of this demo is a basic JS boilerplate to render a fragment shader on the single full-screen plane. No extra libraries here.

The fragment shader is based on Fractal Brownian Motion (fBm) noise.

First, we create a semi-transparent mask to define a contour of burning paper. It is basically a low-scale fBm noise with scroll progress value used as a threshold. Taking the same fBm noise with different thresholds we can
(a) darken parts of the paper so each pixel gets darker before turning transparent
(b) define the stripe along the paper edge and use it as a mask for flames

The fire is done as another two fBm based functions - one for shape and one for color. Both have a much higher scale and both are animated with time value instead of scroll progress .

linkedIn | codepen | twitter (X)

Hello 👋
scroll me
↓