3DFree

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.

JSMouseParallax

Updated

Hover or click the scene to interact

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

Pas de Loup · mountain guide

Snowshoeing by headlamp

Friday 7 pm · 8 spots

Book

① Moonlit snowshoe outing — page of a mountain guide

WhenAt the top of the page for the "Snowshoeing by headlamp — Friday 7 pm, 8 spots" outing, the landscape fills the whole banner: moon, peaks, fir trees; the mouse moves the point of view, and the "Book" button stays in front.
WhyPlanes shifting according to their distance give the depth of a valley at night, without a photo. A single tilted plane, like Layered Parallax 3D, would not make a landscape.
SettingsRoot and scene stretched to the whole banner (position: absolute; inset: 0, height: 100%), square corners; title, time and button placed inside .parallax-scene above the planes; on phones, moon shrunk to 44 px and moved closer to the edge so it does not pass behind the title.

Saint-Firmin village library

Storytelling evening for ages 6 to 10

Saturday 6 pm, bring a blanket

② Storytelling evening by the fire — program of a village library

WhenThe program page announces "Storytelling evening for ages 6 to 10 — Saturday 6 pm, bring a blanket" in a box set on the starry sky; the scenery moves gently with the mouse.
WhyA night backdrop that reacts to the gaze sets the mood of a storytelling evening without a busy illustration; the text stays in a fixed box, only the landscape moves.
SettingsSky recolored toward plum at the bottom (#0b1026 → #1b1f4a → #3a2140); 62% dark box with a gold border on the left, the moon on the right; same script, same depths.

Bocage bird-watchers

Owl Night

Night listening walk, meet at 8:30 pm at the wash house

③ Owl Night — page of a bird protection association

WhenUnder a full moon, an owl perches in front of the fir trees; the box announces "Night listening walk, meet at 8:30 pm at the wash house". When the mouse moves, the owl slides with the trees, faster than the sky.
WhyPlaced in the fastest plane, the owl moves more than the moon and the stars: it really seems to stand in the foreground, in front of the night.
SettingsSVG owl (aria-hidden="true") added to the data-depth="0.6" layer, next to .parallax-trees; 84 px white moon (56 px on phones); dark box at the bottom left.

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-root forces transform: none on 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 div elements, 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 #1a1a4e sky 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.

Chrome 55+✓ Full
Firefox 59+✓ Full
Safari 13+✓ Measured on WebKit
Edge 79+✓ Full
Mobile iOS 13+✓ Not checked on a real iPhone
Android Chrome✓ Full, sideways swipe

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.

HTML
<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>
CSS
.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;
  }
}
JavaScript (fx-0021)
(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 / propertyDefaultEffect
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.