Skip to content
Founding Lifetime: every Pro skillfirst 100 members · $169 onceUpgrade
Lookbk
Sign In
Effect guide

Parallax effect: how parallax scrolling works on a website

Layers of a picture that move at different speeds as you scroll or move the pointer, so a flat page gains depth. What the parallax effect is, three ways to build it, a starter you can paste and run, and the skills that build a finished hero around it.

Free skills to start. Pro is $79 a year for every skill, or $9.99 a month for 10. See pricing

What is a parallax effect?

Parallax is the shift you see when your viewpoint moves: from a train window the fence posts rush past while the hills barely move. A web page fakes it by splitting a picture into layers and moving each one at its own speed. Scroll down a parallax hero and the sky and far hills slide up slowly while the foreground keeps pace with the page; move the pointer and the layers shift against each other as if you had leaned to one side.

The idea comes from animation and games. The multiplane camera filmed painted sheets of glass stacked at different distances from the lens, and 2D games scroll their background layers slower than the play field. On the web it became famous through long single-page parallax scrolling sites.

Today it works best in small doses: one hero with three to six layers and a calm area for the copy, rather than a whole page that slides around under the reader.

How the effect works

Every version does the same thing: give each layer a depth, and move it by the scroll or pointer position times that depth. Far layers move least against the viewer, near layers most, and the eye reads the difference as distance.

There are three common ways to build it. The oldest pure CSS trick puts the page in a scroll container with perspective: 1px and pushes a far layer back with transform: translateZ(-1px) scale(2): the 3D projection makes it scroll at half speed with no script, but the page then scrolls inside that container instead of the window. Scroll-driven animations (animation-timeline: scroll() or view()) tie a keyframe to the scroll position in plain CSS; wrap them in @supports, because not every browser has them yet, and leave the layers still where they are missing. The most portable way is a few lines of JavaScript that write the scroll and pointer position into CSS variables, with CSS turning them into one transform per layer. That is what the starter below does.

  1. 01

    Split the picture into layers

    Sky, far, middle, the copy, near. Each layer needs a transparent top so the one behind it shows through.

  2. 02

    Give each layer a depth

    A number from 0 (moves with the page) to 1 (barely moves). Keep the differences small; 0.2 to 0.6 of the scroll is plenty.

  3. 03

    Move with transform only

    Translate each layer by the position times its depth. The browser can move a transformed layer without repainting it; top, margin and background-position repaint every frame.

  4. 04

    Read the input once a frame

    Listen to scroll passively, store the value and apply it in one requestAnimationFrame. Do not read layout inside the scroll handler.

HTML

Scroll and pointer parallax with CSS variables

Six layers, each with a depth from 0 (near) to 1 (far). A few lines of JavaScript write the scroll and pointer position once a frame, and CSS moves each layer by its depth. The layers hold still for reduced motion.

<style>
  body { margin: 0; font-family: system-ui, sans-serif; color: #1f2a27; }
  .hero { position: relative; height: 100vh; overflow: hidden; background: linear-gradient(#f4d6a0, #efe4d2 70%); }
  .layer {
    position: absolute; inset: -60px -60px 0;
    /* --z: 0 is near the lens, 1 is far away */
    transform: translate3d(calc(var(--px, 0) * (1 - var(--z)) * 40px), calc(var(--sy, 0) * var(--z) * 0.6px), 0);
    will-change: transform;
  }
  .sun { inset: auto; left: 62%; top: 22%; width: 18vmin; height: 18vmin; border-radius: 50%; background: #fff3d6; }
  .far { background: #cbb9a5; clip-path: polygon(0 62%, 12% 48%, 24% 57%, 37% 40%, 50% 54%, 63% 42%, 76% 53%, 88% 38%, 100% 50%, 100% 100%, 0 100%); }
  .mid { background: #8f8a7e; clip-path: polygon(0 72%, 15% 60%, 30% 70%, 45% 58%, 58% 68%, 72% 56%, 86% 66%, 100% 58%, 100% 100%, 0 100%); }
  .title { display: grid; place-items: center; text-align: center; }
  .title h1 { margin: 0 0 18vh; font-size: clamp(40px, 8vw, 104px); line-height: 0.95; letter-spacing: -0.03em; }
  .near { background: #3d4a45; clip-path: polygon(0 80%, 18% 70%, 34% 82%, 52% 72%, 70% 84%, 86% 74%, 100% 80%, 100% 100%, 0 100%); }
  .lens { background: #1f2a27; clip-path: polygon(0 90%, 25% 86%, 50% 92%, 75% 87%, 100% 91%, 100% 100%, 0 100%); }
  .next { min-height: 100vh; padding: 12vh 8vw; background: #1f2a27; color: #efe4d2; font-size: 20px; }
</style>
<section class="hero" id="hero">
  <div class="layer sun" style="--z:0.9"></div>
  <div class="layer far" style="--z:0.8"></div>
  <div class="layer mid" style="--z:0.55"></div>
  <div class="layer title" style="--z:0.4"><h1>Every ridge<br>hides another</h1></div>
  <div class="layer near" style="--z:0.15"></div>
  <div class="layer lens" style="--z:0"></div>
</section>
<section class="next"><p>Scroll back up, or move the pointer over the hills.</p></section>
<script>
const hero = document.getElementById('hero')
const still = matchMedia('(prefers-reduced-motion: reduce)').matches
if (!still) {
  let px = 0, queued = false
  const apply = () => {
    queued = false
    // Stop at one screen: past that the hero is out of view.
    hero.style.setProperty('--sy', Math.min(scrollY, innerHeight).toFixed(1))
    hero.style.setProperty('--px', px.toFixed(3))
  }
  const queue = () => { if (!queued) { queued = true; requestAnimationFrame(apply) } }
  addEventListener('scroll', queue, { passive: true })
  hero.addEventListener('pointermove', (e) => { px = (e.clientX / innerWidth) * 2 - 1; queue() })
}
</script>

A starting point written for this page, free to use in your own projects. The skills below build the finished version with checks, fallbacks and a recorded video.

What to get right on a real page

  1. 01

    Keep the speeds close

    Small differences between layers read as depth. Big ones, or layers that zoom and spin, read as broken and can make some visitors feel sick.

  2. 02

    Do not rely on background-attachment: fixed

    It is the classic one-line parallax, but Safari on iPhone and iPad ignores it and other browsers repaint the whole background on every scroll. Use a separate layer moved with a transform.

  3. 03

    Keep the copy on a calm layer

    Let near layers cross the foot of the headline at most, and never the body text or the button. Words that slide out from under a moving layer are hard to read.

  4. 04

    Test it on a mid-range phone

    Scroll on a real device, not just a desktop window. If it stutters, use fewer and smaller layers, cap the effect to the hero and stop updating once it has scrolled out of view.

  5. 05

    Hold the layers still for reduced motion

    Respect prefers-reduced-motion: leave every layer where it is. The page still scrolls normally and the picture keeps its depth.

Skills that build a parallax effect

Finished heroes built on layered depth, with your colors, fonts, logo and words, a paste-in embed and a 1080p video for launch posts.

  1. Parallax Depth: an example of what it makes
    Free
    web
    Parallax DepthClaude CodeCodexCursor+3 more agents

    Motion Heroes · Free

    Parallax Depth

    WebGL multiplane scene with the headline as a plate · page + embed · 1080p scroll video

    A multiplane camera in code: a landscape painted on 6 plates at real distances from the lens, with your headline as one of them. Far plates drift against the pointer, near ones move with it and the words hold still; scrolling raises the camera so the near ridge drops below the headline while the sun rises behind it. Near trees or roofs pass in front of the foot of the letters, never more than 30% of any letter.

    Worth knowing: It paints one of two worlds, a mountain range on a light theme or a city at night on a dark one, not your own photo, and the live hero needs WebGL.

    Needs: Node.js 18+, Chrome (or Chromium/Edge), and ffmpeg for the video

    See what Parallax Depth makes
  2. Scroll Expand: an example of what it makes
    web
    Scroll ExpandClaude CodeCodexCursor+3 more agents

    Motion Heroes · Pro

    Scroll Expand

    WebGL cut-paper scroll story hero · page + embed · 1080p video

    A tunnel book: the headline is cut through a printed card, and as the visitor scrolls the card parts a word at a time and the camera pushes into a diorama of 8 cut-paper sheets at real depths, with soft shadows that slide against each other with the parallax. The pointer looks around inside the book.

    Worth knowing: The section pins for 3 screen heights, it ships 2 scenes (a lakeside sauna and a courtyard) and needs WebGL2.

    Needs: Node.js 18+, Chrome (or Chromium/Edge), and ffmpeg for the video

    See what Scroll Expand makes

Install

Build a parallax effect with your agent

Each skill is a SKILL.md folder, so it installs the same way in Claude Code, Codex, Cursor and other agents. The example below uses Parallax Depth, which is free; each skill page has its own prompt.

1. Paste the install prompt

Your agent runs the installer. The first time, it shows a sign-in link so you can approve it in the browser.

Install the "Parallax Depth" skill from Lookbk (https://lookbk.dev/skills/parallax-depth-hero).
1. Run: curl -fsSL https://lookbk.dev/cli.js | node - add parallax-depth-hero
2. When the install finishes, read the SKILL.md it installed and tell me what you need from me to make the first result.

Or run it in a terminal

curl -fsSL https://lookbk.dev/cli.js | node - add parallax-depth-hero

2. Start a new session

Agents load skills when a session starts. The installer writes the folder where your agent looks:

Claude Code
~/.claude/skills/parallax-depth-hero
Codex
~/.agents/skills/parallax-depth-hero
Cursor
~/.agents/skills/parallax-depth-hero

3. Ask for the result

Describe it in plain words, for example:

Make a hero where our headline sits inside a layered landscape, with near ridges passing in front of the words and the far mountains and sun rising as visitors scroll.

Another agent? Install a skill in any agent

FAQ

Questions

Still stuck? The guides cover installing, agents and licensing in more detail.

Read the guides
What is the parallax effect on a website?

Parts of the page move at different speeds as you scroll or move the pointer, usually the layers of a hero picture. Far layers move slowly and near ones fast, so a flat page seems to have depth.

How do I make a parallax effect with only CSS?

Either put the page in a scroll container with perspective: 1px and push far layers back with translateZ and a matching scale, or use scroll-driven animations with animation-timeline: scroll() inside @supports. The first changes how the page scrolls; the second is not in every browser yet.

Why does my parallax not work on iPhone?

Most likely it uses background-attachment: fixed, which Safari on iPhone and iPad ignores, so the background scrolls with the page. Put the image on its own layer and move it with a transform instead.

Is parallax scrolling bad for SEO or accessibility?

The effect itself is neither, as long as the copy is real HTML text and not baked into the moving pictures. Long single-page parallax sites put everything on one URL, which leaves fewer pages to rank. For accessibility, large motion tied to scrolling can trigger dizziness, so honor prefers-reduced-motion.

Can I get the parallax hero as a video?

Yes. Parallax Depth records a frame-exact 1080p MP4 of the scroll and a poster, for launch posts and places where scripts are not allowed.