tool / parallaxjavascript · react · html

Parallax scrolling generator

Give each layer of a scene its own speed, scroll the little window to see the depth, then copy the code: one scrollParallax call per layer, as plain JavaScript, a React component or a single HTML file.

Open the generator ↓
Layers
4
Presets
5
Options
5
Main entry, gzip
11.6 KB
windowVscroll Δspeedlayer1.3Δ0.6front1.0Δ0page0.8Δ−0.4hills0.6Δ−0.8skynearfar
Fig. 1 — one scroll, four layers seen side on. Each moves Δ × (1 + speed × H ÷ (V + H)) up the screen, H its height and V the window’s. Drawn with layers as tall as the window: 1 + speed ÷ 2.
01The tool

Make your parallax scene

preset

Far layers lag behind the page, the ground runs a little ahead of it.

Scroll inside this box ↓

The scene has left the window ↑

4 of 4 layers move

01Skysun, clouds, birds↓ 110px0.83×
02Mountainsthe far ridge↓ 69px0.89×
03Hillstrees on the near ridge↓ 28px0.96×
04Groundgrass and a fence↑ 28px1.04×
parallax.js8 lines
import { scrollParallax } from 'svg-scroll-draw';

// One call per layer; the markup and CSS are in the HTML tab.
// Negative speeds lag behind the page (far), positive ones run ahead (near).
scrollParallax('#sky', { speed: -0.4 });
scrollParallax('#mountains', { speed: -0.25 });
scrollParallax('#hills', { speed: -0.1 });
scrollParallax('#ground', { speed: 0.1 });

On real scroll · the same settings, no seek()

This one follows the page. Each layer’s window runs from its top reaching the bottom of your window to its bottom leaving the top, so the whole effect plays while the scene is in view.

The preview
scrollParallax follows the page’s scroll, so the little window works out each layer’s progress from its own scroll, over the library’s default trigger window, and hands it to the real instances with seek(). Under the code, the same scene runs on this page’s scroll, with no seek().
02The model

How speed works

scrollParallax moves an element from translateY(0%) to translateY(−speed × 100%) while its window is scrolled through: from the moment its top reaches the bottom of the window to the moment its bottom leaves the top. The percentage is of the element’s own height, so speed is a share of the layer’s size, not of the scroll: 0.5 moves it half its height over that window, 1 its whole height, and a negative speed moves it down instead of up.

The page moves too, so what you see is the page’s rate plus the layer’s own (fig. 1). Positive speeds run ahead of the page and read as near, negative ones lag behind and read as far, 0 moves with it. For a layer H tall in a window V tall that is 1 + speed × H ÷ (V + H) times the scroll. The table gives each speed’s travel over that window, for layers as tall as the window, H = V.

speedtravelon screenreads as
−1↓ H0.50×Far away: half the page’s rate.
−0.5↓ 0.5 × H0.75×Far: a distant ridge or the sky.
−0.2↓ 0.2 × H0.90×Just behind the page.
0none1.00×The page itself: no parallax, and no call needed.
0.3↑ 0.3 × H1.15×The default: a little nearer than the page.
0.5↑ 0.5 × H1.25×Near: the foreground.
1↑ H1.50×Very near: rushes past. Its art has to run its whole height past its bottom edge.
Keep it small
Two layers drift apart by the difference of their speeds times their height over the window, the whole time the scene is on screen. Differences of 0.1 to 0.4 read as depth; much more and the scene comes apart before it has left the window.
Why ±1
Beyond ±1 a layer travels further than its own size, so the scene’s overflow: hidden clips its whole box away before the window ends. Measured with 2.23.2 in Chromium, Firefox and WebKit, the layer then stops following the scroll, and when its box has left the scene it stays where it stopped, even after scrolling back. The generator’s sliders stop at ±1.
035 options

Every scrollParallax option

speed

0.3 by default. How far the element travels over its window, as a share of its own height: positive up, negative down, 0 leaves it where the page puts it. It sets the element’s transform inline, which replaces any transform your CSS gives it, so put one of your own on a wrapper.

axis

'y' by default. 'x' moves it sideways by speed × its own width instead, a positive speed to the left. It still follows the page’s vertical scroll.

easing

'linear' by default. How the travel is spread over the window: 'ease-out', 'ease-in-out', 'ease-in', 'linear', 'spring', 'bounce', 'elastic', or any function of t. Linear keeps the layer locked to the scroll, which is what parallax wants.

trigger

{ start: 'top bottom', end: 'bottom top' } by default. The window the travel is spread over, element edge then window edge, as in ScrollTrigger: from the element’s top at the window’s bottom to its bottom at the window’s top.

onProgress

none by default. Called with the progress (after the easing, so 0 to 1 when it is linear) on every frame while the element is on screen, for anything else that should follow it.

It returns the same instance as every other API in the library, so a layer can be placed by hand or taken away:

hero.js9 lines
import { scrollParallax } from 'svg-scroll-draw';

// Half its own height over the window, upward: a near layer.
const hero = scrollParallax('#hero-art', { speed: 0.5 });

hero.seek(0.5);   // place it by hand, as the generator's preview does:
                  // it stops following the scroll until resume()
hero.resume();
hero.destroy();   // stop, and put the element back as it was
Many at once
scrollParallaxGroup applies one set of options to several elements and hands back a single instance for all of them.
badges.js4 lines
import { scrollParallaxGroup } from 'svg-scroll-draw/group';

// The same options for several elements, with one instance to destroy.
const badges = scrollParallaxGroup(['#badge', '#tag', '#pill'], { speed: -0.2 });
04Transforms only

Parallax that stays smooth

scrollParallax changes one property per layer, transform, plus the --scroll-draw-progress variable for your own CSS, so moving a layer never makes the browser lay the page out again. It measures each layer when it starts and again when the window resizes or the layout around the layer changes, never while you scroll, and runs its frame loop only while the layer is on screen.

The travel is a percentage of the layer’s own box, which the browser resolves every frame, so a layer that grows after it starts, an image arriving inside it, travels its new size. Inside an SVG, a <g> can be a layer too: scrollParallax gives it its own transform box, so the percentage is of the group, and puts the old one back on destroy().

Your part
Keep the layers few and their art simple, clip the scene with overflow: hidden, and give large layers will-change: transform so the browser can move them without painting them again.
05prefers-reduced-motion

Parallax and reduced motion

For readers whose system asks for reduced motion, scrollParallax does not move at all. Each layer is set at its end position, translateY(−speed × 100%) (translateX on axis x), and stays there; if the setting is turned on while the page is open, every layer goes there at once, as if the page had loaded that way.

Parallax is the kind of motion that setting exists for, so make sure the scene still reads at its end position: the generator’s End position button shows it.

067 questions

Parallax scrolling questions

Q1

What is parallax scrolling?

A scrolling effect where the layers of a scene move at different speeds, so the near ones seem to pass faster than the far ones and the page gains depth. With svg-scroll-draw each layer is an element, and scrollParallax(element, { speed }) moves it a share of its own height as it crosses the window: negative speeds lag behind the page and look far away, positive ones run ahead of it and look near.

Q2

How do I make a parallax effect with CSS and JavaScript?

Stack the layers in one container with position: relative and overflow: hidden, make each one position: absolute with inset: 0, then call scrollParallax('#layer', { speed }) for each layer that should move. The generator above writes the CSS and the calls; its HTML tab is a whole file, with a comment where each layer’s art goes; give the scene a page to scroll through and it runs as it is. CSS alone can tie an animation to scroll with animation-timeline: view() in the browsers that support it; scrollParallax always runs on its own JavaScript engine, so it does not depend on that support.

Q3

How do I add a parallax effect in React or Next.js?

svg-scroll-draw/react has no parallax component. Call scrollParallax in a useEffect with a ref to each layer, and destroy the instances in the effect’s cleanup; the generator’s React tab writes the component, with 'use client' for the Next.js App Router. For a single element, <ScrollAnimate> from svg-scroll-draw/react does the same with the transform scrollParallax would set: props={{ transform: ['translateY(0%)', 'translateY(-50%)'] }}, easing="linear" and native={false} for a speed of 0.5.

Q4

What do the parallax speed values mean?

Speed is how far a layer travels over its window, as a share of its own height: 0.5 is half its height, 1 all of it, 0 no movement at all, and a negative value moves it down instead of up. On screen it moves at the page’s rate plus that travel, 1 + speed × H ÷ (V + H) times the scroll for a layer H tall in a window V tall. The default is 0.3.

Q5

Does parallax scrolling hurt performance?

Not when it only moves transforms, which is all scrollParallax changes: it writes transform (and a progress variable for CSS) once a frame while a layer is on screen, measures the layer only at the start and when the window or the layout around it changes, never while scrolling, and stops when the layer leaves the screen. Keep the layers few, clip the scene with overflow: hidden, and give large layers will-change: transform.

Q6

Is parallax scrolling bad for accessibility?

It can be: large motion the reader does not control can make some people dizzy or sick, which is what the reduced-motion setting is for. scrollParallax honours it. Under prefers-reduced-motion: reduce each layer is set at its end position and stays there, and if the setting is turned on while the page is open, every layer goes there at once. Keep the differences between layers small, and the scene readable at its end position.

Q7

Can parallax run inside a scrolling div instead of the page?

scrollParallax follows the window’s scroll. For layers inside their own scrolling element, use scrollAnimate with the same transform and its scrollContainer option: scrollAnimate('#layer', { props: { transform: ['translateY(0%)', 'translateY(-50%)'] }, easing: 'linear', scrollContainer: '#box' }) moves the layer half its height as it crosses the box.

More: parallax on many elements at once, scroll animation and reduced motion, and the text animation generator. All of svg-scroll-draw 2.23.3: the docs.