Mouse parallax
Mouse parallax gives stages a sense of depth: as the visitor moves their cursor, layers drift in 3D space. Done well it’s a subtle “alive” feel; done aggressively it’s nausea. This page covers how the engine works, how to tune it, and how it respects accessibility.
The model
Section titled “The model”Parallax is per layer, not global. Each layer can independently opt in via the Layer Style modal → Parallax tab, where you set:
- Depth (−1 to 1) — translate offset multiplier.
- Tilt (0 to 1) — rotateX / rotateY multiplier.
- Respect reduced-motion preference — skip parallax for visitors with
prefers-reduced-motion: reduce.
The stage’s Parallax intensity (0–100) (Stage settings → Parallax) is the global gain. Each layer’s transform on storefront is:
translate3d(depth × intensity × Xratio, depth × intensity × Yratio, 0)rotateX(tilt × intensity × Yratio deg)rotateY(tilt × intensity × −Xratio deg)where Xratio / Yratio are the cursor’s normalized position relative to the shell (-1 at the left/top edge, +1 at the right/bottom edge).
Picking values
Section titled “Picking values”A good visual hierarchy uses small absolute values:
- Background layers — depth ≈
0to0.1. Or slightly negative (-0.05) so they drift opposite to foreground, exaggerating depth. - Mid-ground — depth ≈
0.2to0.4. - Foreground / hero element — depth up to
0.6or0.7.
Tilt is more aggressive than translate; values above 0.3 usually feel too much. Start with 0.1–0.2.
The stage intensity is the master fader. With every layer set to its design values, the intensity slider lets you tune the overall parallax strength without re-editing layers. Set to 0 to disable parallax stage-wide without touching individual layers.
How the engine works
Section titled “How the engine works”The plugin’s parallax engine attaches a mousemove listener to the .p2lab-stage__shell root (or the slider root for slider stages). On each mousemove:
- Compute cursor position relative to the shell.
- Normalize to
(-1, -1)…(+1, +1)range. - For every layer with
data-depth/data-tilt, compute the transform and apply it to.p2lab-stage__layer-wrapviastyle.transform.
The translate and rotate go on the .p2lab-stage__layer-wrap (positioning element). The actual layer content (.p2lab-stage__layer) keeps its own animation transforms — they compose on a separate DOM element so parallax and timeline animations don’t fight.
Disable conditions:
prefers-reduced-motion: reduce— the engine doesn’t attach the listener (assuming the layer has Respect reduced-motion enabled — and it should be on by default for accessibility).- Coarse pointer (
pointer: coarse) — touch devices don’t have a meaningful cursor; the engine disables itself rather than producing erratic touch-driven motion.
Sliders and parallax
Section titled “Sliders and parallax”Slider stages get the same engine, attached to the slider’s outer wrapper. Each slide can have its own parallax layers; transitioning between slides smoothly hands over the parallax origin from one slide’s centre to the next.
Accessibility
Section titled “Accessibility”A few things to keep in mind:
- Respect reduced-motion is on by default for every parallax layer. Leave it on. Visitors who set the OS-level “Reduce motion” preference (or who use assistive tech) will see the stage without the cursor drift.
- Vestibular disorders are real — even mild parallax can trigger discomfort for some users. Conservative depth / tilt values reduce risk.
- Stage intensity = 0 is a kill switch you can flip if you need to ship a stage with parallax disabled for a specific campaign.
Performance
Section titled “Performance”The engine throttles via requestAnimationFrame — even on fast machines it does at most one update per frame. Transforms are GPU-composited (translate3d, rotate3d), so CPU cost is low even on slow devices.
Debugging
Section titled “Debugging”If parallax doesn’t appear to work:
- Open DevTools, inspect a layer’s
.p2lab-stage__layer-wrap. Confirmdata-depth/data-tiltare emitted. If not, the layer’s Parallax effect isn’t enabled or has both values at 0. - Check the stage’s intensity slider — if it’s at 0, nothing moves.
- Confirm the OS isn’t reporting reduced-motion (System Preferences → Accessibility → Display → Reduce Motion on macOS, or the equivalent toggle in Windows).
- Confirm the device isn’t reporting coarse pointer (parallax is intentionally disabled on touch).
Where to go next
Section titled “Where to go next”- Editor → Layer Style — the parallax tab for layer-level depth / tilt.
- Editor → Stage Settings — the global intensity slider.
- CSS customization — override the wrap transform with theme CSS if you must.