Mouse Parallax Scene
A night landscape in four planes, stars, moon, purple mountains and fir trees, that slide at different speeds according to the pointer position: the trees move six times as much as the stars, and depth appears by itself — mouse, pen or finger swipe, several scenes per page, still landscape when the user reduces motion.
Updated
This effect is free — the 3D category contains 50 effects total, including 8 free. Effect.Labs has 840 vanilla effects. Explore the category →
Usage examples
Saint-Firmin village library
Storytelling evening for ages 6 to 10
Saturday 6 pm, bring a blanket
Bocage bird-watchers
Owl Night
Night listening walk, meet at 8:30 pm at the wash house
How it works
The root .fx-0021-root (250 px tall, rgb(26, 26, 36) background) holds .parallax-scene, marked data-parallax-scene: full width, 250 px tall, sky gradient #0a0a2e → #1a1a4e → #2a1a3e, 12 px corners. It stacks four .parallax-layer that overflow by 30 px on every side (inset: -30px), so their edges never show when they slide. From back to front: eight 3 px stars (data-depth="0.1"), a 60 px moon with a golden halo, 30 px from the top and 60 px from the right (0.25), four triangular indigo and purple mountains 90 to 130 px tall (0.4), seven #064e3b fir trees 40 to 70 px tall (0.6).
The script goes through every .fx-0021-root on the page and wires one instance per scene. On each pointer move, it measures the offset from the scene's center and moves each plane by that offset times its depth and times 0.5: with the pointer at the left edge of a 560 px scene, the trees slide 84 px, the mountains 56, the moon 35, the stars 14. Measured in Chromium 151, Firefox 153 and WebKit 26.5 with the pointer 5% from the top left corner: 75.6 px across and 33.8 px down for the trees. A 0.1 s transition smooths the motion; when the pointer leaves, every plane returns to its place. These are flat translations only: no 3D, nothing a parent could flatten.
Two choices keep the scenery together as it moves. The foot of the mountains sits 24 px below the bottom edge and the foot of the trees 6 px below it, with a dark ground strip (#022c22, 64 px) hidden under the trees: when the pointer goes up and the planes rise, the ground follows instead of showing sky under the trees. The script listens to pointer events rather than the mouse alone: with a finger, touch-action: pan-y keeps vertical page scrolling, and a sideways swipe plays the depth; the landscape settles back when the finger lifts.
Accessibility
- Reduced motion: under
prefers-reduced-motion: reduce, a block scoped to.fx-0021-rootforcestransform: noneon the four planes and removes their transition: the landscape stays in its rest position, with moon, mountains and trees visible. Measured in Chromium, Firefox and WebKit: no movement under the mouse (75 px without the preference). - Scenery: the scene holds no text and no control, there is nothing to reach with the keyboard: its rest state reads as is. If you lay a title or a button over it, as in the scenes above, put it inside
.parallax-scene: it stays clickable and the landscape keeps following the pointer while hovering it. - Screen readers: the planes are empty
divelements, ignored. If the picture carries meaning (a place, a night outing), say it in the page text, not in the scenery. - Contrast: white text on the
#1a1a4esky stays very readable; near the mountains and trees, add a semi-transparent dark background under the text, as in the second and third scenes.
Browser compatibility
CSS (transform: translate, transition, border triangles, touch-action) + JS with pointer events, no dependencies. Measured in Chromium 151, Firefox 153 and WebKit 26.5: same shifts, two independent scenes on one page, still landscape under reduced motion; finger swipe measured in Chromium in touch mode. No CSS 3D here: the WebKit rendering is reliable, but a real Safari and a real iPhone were not checked.
Without JavaScript, the landscape shows in its rest position, without motion. On a touch screen, a single tap moves nothing: the finger has to slide sideways.
The code
Copy the three blocks into your page. No dependencies.
<div class="fx-0021-root">
<div class="parallax-scene" data-parallax-scene>
<div class="parallax-layer" data-depth="0.1">
<div class="parallax-stars">
<span class="parallax-star" style="top:10%;left:15%"></span>
<span class="parallax-star" style="top:20%;left:75%"></span>
<span class="parallax-star" style="top:35%;left:45%"></span>
<span class="parallax-star" style="top:15%;left:90%"></span>
<span class="parallax-star" style="top:50%;left:25%"></span>
<span class="parallax-star" style="top:8%;left:55%"></span>
<span class="parallax-star" style="top:42%;left:80%"></span>
<span class="parallax-star" style="top:28%;left:10%"></span>
</div>
</div>
<div class="parallax-layer" data-depth="0.25" style="display:flex;align-items:flex-start;justify-content:flex-end;padding:60px 90px 0 0;">
<div class="parallax-moon"></div>
</div>
<div class="parallax-layer" data-depth="0.4">
<div class="parallax-mountains">
<div class="parallax-mountain mountain-1"></div>
<div class="parallax-mountain mountain-2"></div>
<div class="parallax-mountain mountain-3"></div>
<div class="parallax-mountain mountain-4"></div>
</div>
</div>
<div class="parallax-layer" data-depth="0.6">
<div class="parallax-trees">
<div class="parallax-tree tree-sm"></div>
<div class="parallax-tree tree-lg"></div>
<div class="parallax-tree tree-md"></div>
<div class="parallax-tree tree-sm"></div>
<div class="parallax-tree tree-lg"></div>
<div class="parallax-tree tree-md"></div>
<div class="parallax-tree tree-sm"></div>
</div>
</div>
</div>
</div>
.fx-0021-root,
.fx-0021-root *,
.fx-0021-root *::before,
.fx-0021-root *::after {
box-sizing: border-box
}
.fx-0021-root {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 250px;
overflow: hidden;
position: relative;
background: rgb(26, 26, 36);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.parallax-scene {
width: 100%;
height: 250px;
position: relative;
overflow: hidden;
border-radius: 12px;
background: linear-gradient(180deg, #0a0a2e 0%, #1a1a4e 50%, #2a1a3e 100%);
cursor: crosshair;
/* Touch: a sideways drag moves the layers, a vertical swipe still scrolls the page */
touch-action: pan-y;
}
.parallax-layer {
position: absolute;
inset: -30px;
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.1s ease-out;
will-change: transform;
}
.parallax-stars {
width: 100%;
height: 100%;
}
.parallax-star {
position: absolute;
width: 3px;
height: 3px;
background: white;
border-radius: 50%;
opacity: 0.6;
}
.parallax-moon {
width: 60px;
height: 60px;
background: radial-gradient(circle at 35% 35%, #fef3c7, #fbbf24);
border-radius: 50%;
box-shadow: 0 0 40px rgba(251, 191, 36, 0.4), 0 0 80px rgba(251, 191, 36, 0.2);
}
/* The layers overflow the scene by 30 px on every side: the mountains stand 24 px below
the bottom edge, the trees 6 px below, so both stay visible when the layers move */
.parallax-mountains {
position: absolute;
bottom: 6px;
width: 120%;
left: -10%;
}
.parallax-mountain {
position: absolute;
bottom: 0;
width: 0;
height: 0;
border-style: solid;
}
.mountain-1 {
left: 5%;
border-width: 0 80px 120px 80px;
border-color: transparent transparent #1e1b4b transparent;
}
.mountain-2 {
left: 25%;
border-width: 0 100px 100px 100px;
border-color: transparent transparent #2e1065 transparent;
}
.mountain-3 {
left: 55%;
border-width: 0 90px 130px 90px;
border-color: transparent transparent #1e1b4b transparent;
}
.mountain-4 {
left: 75%;
border-width: 0 70px 90px 70px;
border-color: transparent transparent #2e1065 transparent;
}
.parallax-trees {
position: absolute;
bottom: -40px;
width: 120%;
left: -10%;
display: flex;
justify-content: space-around;
align-items: flex-end;
/* Ground strip under the trees, hidden below the edge at rest: it shows when the trees rise */
padding-bottom: 64px;
background: linear-gradient(to top, #022c22 64px, transparent 64px);
}
.parallax-tree {
width: 0;
height: 0;
border-style: solid;
border-color: transparent transparent #064e3b transparent;
}
.tree-sm {
border-width: 0 12px 40px 12px;
}
.tree-md {
border-width: 0 16px 55px 16px;
}
.tree-lg {
border-width: 0 20px 70px 20px;
}
/* Reduced motion: the landscape stays still in its rest position */
@media (prefers-reduced-motion: reduce) {
.fx-0021-root .parallax-layer {
transform: none !important;
transition: none;
}
}
(function() {
// ========== SPATIAL DEPTH ==========
// ==========================================
// 1. Mouse Parallax Scene (pointer events: mouse, pen and touch)
// One instance per .fx-0021-root: several scenes on one page each move on their own.
// ==========================================
document.querySelectorAll('.fx-0021-root').forEach(function(root) {
var scene = root.querySelector('[data-parallax-scene]');
if (!scene) return;
var layers = scene.querySelectorAll('.parallax-layer');
scene.addEventListener('pointermove', function(e) {
var rect = scene.getBoundingClientRect();
var centerX = rect.width / 2;
var centerY = rect.height / 2;
var mouseX = e.clientX - rect.left - centerX;
var mouseY = e.clientY - rect.top - centerY;
layers.forEach(function(layer) {
var depth = parseFloat(layer.getAttribute('data-depth')) || 0.1;
var moveX = mouseX * depth * 0.5;
var moveY = mouseY * depth * 0.5;
layer.style.transform = 'translate(' + moveX + 'px, ' + moveY + 'px)';
});
});
scene.addEventListener('pointerleave', function() {
layers.forEach(function(layer) {
layer.style.transform = 'translate(0, 0)';
});
});
});
})();
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Speed of each plane (HTML — data-depth of the .parallax-layer elements) |
0.1, 0.25, 0.4, 0.6 | The larger the number, the more the plane moves and the closer it looks. Keep a clear gap between neighboring planes; a plane at 0 stays still (handy for a title). |
Overall amplitude (JS — 0.5 in moveX and moveY) |
0.5 | Multiplies every movement. At 0.3 the effect turns subtle; above 0.5, raise the planes' overflow as well. |
Plane overflow (CSS — inset: -30px on .parallax-layer) |
30 px | Hidden margin that keeps a plane's edge out of sight as it slides. The mountains and trees, wider than the scene (120%), already cover the sides. |
Foot of the mountains and trees (CSS — bottom of .parallax-mountains and .parallax-trees, padding-bottom and ground gradient) |
6 px and −40 px, 64 px ground | Raising the trees shows more of them, but the ground must stay taller than the trees' vertical travel (up to 37 px in a 250 px scene), or the sky shows underneath. |
Colors (CSS — .parallax-scene gradient, border-color of the mountains and trees, moon) |
sky #0a0a2e → #2a1a3e, mountains #1e1b4b and #2e1065, trees #064e3b | The mountains and trees are border triangles: change the bottom border color (border-color: transparent transparent …). |
Height (CSS — height: 250px on .fx-0021-root and .parallax-scene) |
250 px | Change both together. The vertical travel follows the height: in a taller scene, raise the ground too. |
Responsiveness (CSS — transition: transform 0.1s ease-out) |
0.1 s | 0.4 s gives a floating backdrop that catches up with the pointer after a delay. |
FAQ
How do I add an element to the landscape, like the owl in the third scene?
Put it in the layer whose speed it should follow: in the data-depth="0.6" layer for the foreground, in 0.25 for something far away. Give it position: absolute and a bottom or percentage position. For an extra plane, add a .parallax-layer with its own data-depth: the script picks it up without any change.
How is it different from Layered Parallax 3D (fx-0007)?
Mouse Parallax Scene slides planes flat, each at its own speed, with no rotation: it is a backdrop that spans the full width. Layered Parallax 3D rotates a small stack of layers in real 3D. For a banner or a section background: Mouse Parallax Scene; to show stacked thicknesses: Layered Parallax 3D.
Can I put several scenes on one page?
Yes. Each scene has its own .fx-0021-root and the script gives each one its own instance: each scene only reacts to the pointer hovering it. That is the case on this page, where the demo at the top and the three examples run on a single script.