Guide

How to make an Apple‑style scroll animation

On Apple's product pages, the phone or the headphones turn and come apart as you scroll, and run backwards when you scroll up. It looks like video, but it is a stack of still images. Here is how it works, a demo you can scroll, the code to build one, and how to make one without writing any.

Frame 001 of 120

Keep scrolling. The scroll position chooses one of 120 frames, so scrolling up plays it backwards.

What an Apple-style scroll animation is

The effect has a few names: scroll-scrubbed animation, image sequence animation, scrollytelling. They all describe the same thing. A short animation is exported as a numbered sequence of images, say frame_0000.jpg to frame_0119.jpg. The page pins a full-screen canvas behind the content. As the visitor scrolls, the page works out how far down they are, as a number from 0 to 1, and draws the matching frame.

Nothing plays on a timer. If the visitor stops, the picture stops. If they scroll back up, it reverses. That direct control is why it feels more like handling an object than watching a clip, and it is why product pages use it to show something from every side.

How it works, step by step

  1. Make the frames. Render an animation from 3D software, or take frames out of a video. 120 frames at 1280 pixels on the long edge is a reasonable starting point.
  2. Preload them. Every frame has to be in memory before it is needed, or the canvas shows a gap while the image downloads. A good approach loads every fifth frame first so the whole range is roughly covered, then fills in the rest.
  3. Map scroll to a frame. Progress is scrollY divided by the scrollable distance (page height minus window height). Multiply by the frame count minus one and round.
  4. Draw on the next animation frame. Scroll events fire faster than the screen repaints. Wrap the draw in requestAnimationFrame so it runs at most once per repaint.
  5. Cover the screen. Scale each image so it fills the canvas and crop the overflow, the same way object-fit: cover does for an image.

How to make a scroll animation website without code

ScrollCraft is a free, open-source builder that does every step above for you and hands you the finished site as plain files.

  1. Pick a starting point. Choose one of 23 templates, each a finished site with its own scroll background, or describe your business in a paragraph and the AI writer drafts the whole site for you.
  2. Choose the background.Keep the template's drawn background (Gradient Flow, Geometric, Particles or Wave, in your colours) or upload your own video and its frames are taken out in your browser.
  3. Edit the words. Change headings, text and buttons in the editor and watch the preview scroll.
  4. Export. Download a ZIP with index.html, your frames if you used a video, and a README that explains how to put it online for free. No account is needed for any of this.

A drawn background ships no images at all. The exported page carries the drawing code and redraws each frame at the visitor's screen size, so the page itself comes to about 30 KB. See what the export contains for the full list of files.

How to build an image sequence scroll animation in HTML and JavaScript

If you would rather write it yourself, this is the whole idea in about forty lines. Put your frames in a frames folder beside the page.

<canvas id="seq"></canvas>
<div style="height: 400vh"></div>

<script>
  const canvas = document.getElementById("seq");
  const ctx = canvas.getContext("2d");
  const count = 120;
  const frames = [];

  for (let i = 0; i < count; i++) {
    const img = new Image();
    img.src = `frames/frame_${String(i).padStart(4, "0")}.jpg`;
    frames.push(img);
  }

  function draw(index) {
    const img = frames[index];
    if (!img.complete || !img.naturalWidth) return;
    canvas.width = innerWidth;
    canvas.height = innerHeight;
    const s = Math.max(canvas.width / img.naturalWidth, canvas.height / img.naturalHeight);
    const w = img.naturalWidth * s, h = img.naturalHeight * s;
    ctx.drawImage(img, (canvas.width - w) / 2, (canvas.height - h) / 2, w, h);
  }

  let ticking = false;
  addEventListener("scroll", () => {
    if (ticking) return;
    ticking = true;
    requestAnimationFrame(() => {
      const max = document.documentElement.scrollHeight - innerHeight;
      const p = Math.min(Math.max(scrollY / max, 0), 1);
      draw(Math.round(p * (count - 1)));
      ticking = false;
    });
  }, { passive: true });

  frames[0].onload = () => draw(0);
</script>

And one line of CSS to pin the canvas behind everything else:

#seq { position: fixed; inset: 0; width: 100%; height: 100%; z-index: -1; }

That is enough to see the effect. A production version needs more: it should size the canvas for the device pixel ratio (capped at 2, or a phone ends up drawing millions of pixels each frame), only resize the canvas when the window changes rather than on every draw, preload in a sensible order, and show something useful if the frames fail to load. The exported ScrollCraft page does all of that, and since it is plain JavaScript you can open it and read how.

Performance on mobile

  • Weight. 120 JPEGs at 1280 pixels can easily add up to several megabytes. Lower the frame count or the JPEG quality before anything else.
  • Portrait screens. A landscape frame on a tall phone is mostly cropped away. A separate portrait set, cut for phones, looks better. ScrollCraft can make one from an uploaded video and serves it below 768 pixels wide.
  • Decode off the scroll path. Call img.decode() after each frame loads so the browser does not stall decoding a JPEG in the middle of a scroll.
  • Keep text readable. Moving pictures behind words make the words harder to read. A dark overlay or a panel behind the text fixes that.

Reduced motion and accessibility

Some people set their system to reduce motion because animation makes them unwell. A scrubbed background only moves when the visitor scrolls, which is gentler than autoplay, but any extra entrance animations should switch off under prefers-reduced-motion: reduce. ScrollCraft's exported pages turn their text reveal animations off for those visitors. Mark the canvas aria-hidden if it is decoration, and keep every piece of real information in the text, not in the frames.

The demo at the top of this page follows the same rule: with reduced motion on, it stops following the scroll and gives you a slider instead.

Questions

Is this the same effect Apple uses?
The technique is the same one people have taken apart on Apple's AirPods and iPhone pages: a numbered set of still images drawn to a canvas, with the scroll position choosing which image is shown. Apple's own code and assets are theirs; the idea is common knowledge.
Do I need GSAP or another library?
No. GSAP's ScrollTrigger is convenient if you already use it, but the core is a scroll listener, a little arithmetic and drawImage. The sketch on this page has no dependencies, and neither do the pages ScrollCraft exports.
How many frames should I use?
Somewhere between 60 and 200. More frames scrub more smoothly and cost more to download. ScrollCraft's builder offers 60 to 200 and starts at 120, which is smooth on a typical page length.
Can I do it with CSS only?
Not with an image sequence. CSS scroll-driven animations (animation-timeline: scroll()) can move, fade and scale elements as you scroll, but they cannot pick a frame from a set of images. That part needs a few lines of JavaScript.
Is it free to make one with ScrollCraft?
Yes. The builder, the templates and the export are free and need no account. Only the AI writer on Describe is paid after the first two sites.

Try it onyour own business

Describe what you do in a paragraph and get a finished scroll site to edit and export, or start from a template.