Lottie Scroll Pulse Dots — Ring Assembles on Scroll

Lottie Scroll Pulse Dots · Scroll · Plain HTML, CSS & JS · Live preview

What's included

Features

Lottie JSON built in code with keyframe helpers — no exported file, nothing to fetch or 404
Dots placed around a circle with trigonometry, each growing in place from scale 0
Every animated keyframe carries i/o easing tangents so lottie-web interpolates instead of holding
Per-dot stagger assembles the ring as a sweep around the circle rather than all at once
Scale and opacity animate together so each dot pops cleanly into existence
autoplay: false + goToAndStop(frame, true): scroll position, not a timer, decides the frame
Fully reversible — scrolling up scatters the dots back to nothing for free
Hue gradient around the wheel, easy to recolour, with a CSS drop-shadow glow

About this UI Snippet

How to Build a Scroll-Assembled Ring of Lottie Dots With Staggered Scale Keyframes

Screenshot of the Lottie Scroll Pulse Dots snippet rendered live

The Lottie Scroll Pulse Dots snippet assembles a ring of coloured dots one at a time as the visitor scrolls — each dot scaling up from nothing and fading in a beat after the last — and scatters them again on the way up. Nothing plays on a timer; the scrollbar drives the assembly frame by frame, and the whole animation is generated in JavaScript so the staggered build is fully visible in the code.

Lottie is JSON, assembled in code

A Lottie animation is a JSON document of vector layers and keyframes. Instead of fetching one, this snippet builds the animationData object directly and passes it to lottie.loadAnimation, so there is nothing to host and nothing that can 404. A loop places each dot around a circle with basic trigonometry and gives it its own scale and opacity keyframes, all carrying the easing tangents lottie-web requires.

Placing dots around a circle

Each dot sits at a point on a circle computed from its index: the angle is the index divided by the total, times a full turn, and the x and y come from the cosine and sine of that angle scaled by the radius. Starting the angle at minus ninety degrees puts the first dot at the top so the ring reads cleanly. Because the position is fixed and only the scale and opacity animate, each dot grows in place rather than flying in from elsewhere.

Scaling in from zero, staggered

Every dot animates its transform scale from 0 to 100 and its opacity from 0 to 100, so it pops into existence at its spot on the ring. The key to the effect is the stagger: each dot's animation starts a little later than the previous one, offset by its index, so the ring assembles as a sweep around the circle rather than all dots appearing at once. That single per-dot delay is what turns twelve separate pops into one flowing build.

The one keyframe detail that matters

When you author a Lottie by hand, every animated keyframe must carry its i (in) and o (out) bezier tangents. Omit them and lottie-web treats the keyframe as a hold keyframe: it never interpolates, so a scale animated 0 to 100 stays stuck at 0 and the dot never appears. The keyframe helpers here attach linear tangents to every keyframe, which is what makes the dots grow.

Autoplay off, scroll on

The animation is loaded with autoplay: false so its internal clock never runs. A GSAP ScrollTrigger pins the stage and scrubs a single plain value from 0 to 1 over a range several viewport-heights tall, and a requestAnimationFrame loop reads that value every frame and calls anim.goToAndStop(p * (totalFrames - 1), true). The true tells lottie-web the value is a frame number, and goToAndStop renders a single still frame without starting playback, so the assembly is welded to scroll position. Because lottie-web interpolates between keyframes, fractional frames render smoothly.

Reversible for free

Deriving the frame from one scrubbed value means scrolling up produces smaller values, so the dots scatter back to nothing in reverse with no extra code. A smoothed scrub: 0.5 lets the assembly glide toward the scroll position rather than snapping to every wheel tick, and the caption fades out the moment the first dot appears. Pair it with the Lottie scroll scrub ring or the Lottie scroll orbit rings for a fuller loader sequence.

Step by step

How to Use

  1. 1
    Load the three CDN scriptsAdd lottie.min.js, gsap.min.js, and ScrollTrigger.min.js from the CDN panel, in that order.
  2. 2
    Paste the HTML, CSS, and JSA faint core dot appears centred in a pinned stage with a live "% assembled" read-out.
  3. 3
    Scroll downThe dots scale and fade in one at a time around the ring, tied directly to scroll position.
  4. 4
    Scroll back upThe dots scatter back to nothing exactly, because goToAndStop seeks a single frame rather than playing on a timer.
  5. 5
    Change the ringEdit the dot count, radius, dot size, and the hue gradient to design your own loader pattern.
  6. 6
    Tune the buildChange the per-dot stagger or the ScrollTrigger end value to speed up or slow the assembly.

Real-world uses

Common Use Cases

Loading and progress indicators
A ring that assembles on scroll reads as a system spinning up — a fitting centrepiece above a section describing a process or wait.
Section entrances
Assemble the ring as a section scrolls into view to punctuate the transition with a crafted micro-animation.
Brand loaders
Tune the dot colours and count to build a distinctive, scroll-reactive loading mark for a product or campaign.
Step and status rings
Map each dot to a step or node in a flow so the ring fills as the visitor scrolls through the accompanying stages.
Learn Lottie stagger and scale
A clear example of positioning shapes with trig and staggering their scale keyframes. Pair it with the Lottie scroll scrub ring.
Hero micro-motion
A small assembling ring gives a hero section subtle, premium motion that reacts to scroll without the weight of a video.

Got questions?

Frequently Asked Questions

Each dot uses its index to compute an angle — index divided by the total, times a full turn — and takes its x and y from the cosine and sine of that angle scaled by the radius, centred on the canvas. Offsetting the angle by minus ninety degrees puts the first dot at the top. Because the position is static and only scale and opacity animate, each dot grows in place rather than travelling.

Because lottie-web treats an animated keyframe with no i/o easing tangents as a hold keyframe — it never interpolates, so a scale keyframed 0 to 100 stays stuck at 0 and the dot never appears. The keyframe helpers in this snippet attach linear tangents to every keyframe. If you hand-edit a keyframe and a dot stops appearing, missing tangents are the first thing to check.

Each dot animation starts at an offset derived from its index, so the ring builds in order around the circle. Change that per-index offset to make the assembly faster, slower, or start from a different point; reverse the index to sweep the other way. Adjust the scale and opacity durations to make each dot pop harder or ease in more gently.

play runs the animation on its internal clock, which is exactly what you do not want when scroll should control it. goToAndStop(frame, true) renders one specific still frame and never starts the timer — the second argument tells lottie-web the value is a frame number, not milliseconds. Multiplying scroll progress by totalFrames gives the frame to show, and fractional frames interpolate smoothly so the assembly is continuous.

Yes. Click JSX for a React component, Vue for a Vue 3 SFC, Angular for a standalone component, or Tailwind for a React + Tailwind version. Call lottie.loadAnimation against a ref inside a mount effect, register the ScrollTrigger there, and on cleanup call anim.destroy() and kill the ScrollTrigger instance (or revert a gsap.context) so the SVG and scroll listener are released on unmount.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You do not have to reverse-engineer how a JSON file becomes a scroll-assembled ring. Paste this snippet's HTML, CSS, and JS into an AI assistant like Claude and ask it to walk through how the dots are placed with trigonometry, or why every keyframe needs i/o tangents. The same assistant can help you extend it — ask it to add a connecting line between dots, make each dot pulse after it lands, map the dots to real data, or nest a second inner ring rotating the other way. Treat the code as a conversation starter, not a finished artifact.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "scroll-assembled ring of Lottie dots" in plain HTML, CSS, and JavaScript using lottie-web and GSAP's ScrollTrigger plugin, both from a CDN (no bundler).

Requirements:
- A pinned full-height section with a centered container div for the Lottie, an intro caption overlay, and a live "percent assembled" read-out.
- Build the Lottie as an inline animationData JSON object (leave a comment showing how to swap it for path: 'your-animation.json'). Include keyframe helpers that ALWAYS attach i/o easing tangents (linear) — without them lottie-web holds the value and nothing animates.
- The animation is a ring of about twelve dots placed around a circle with trigonometry (angle = index/total * full turn, offset so the first is at the top). Each dot animates its transform scale from 0 to 100 and opacity 0 to 100, staggered by its index so the ring assembles one dot at a time around the circle. Colour the dots across a hue gradient by index, with a faint core dot at the centre.
- Load with lottie.loadAnimation using renderer 'svg', loop false, autoplay FALSE.
- Register a GSAP tween on a ScrollTrigger targeting the pinned section, with pin true, start at top top, scrub around 0.5, and an end several hundred percent tall, animating one value p from 0 to 1.
- In a requestAnimationFrame loop, read p and call anim.goToAndStop(p * (anim.totalFrames - 1), true), update the read-out, and fade the caption out once p passes a small threshold.
- Confirm scrolling up scatters the dots back to nothing.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Source Code

<section class="pdt-stage" id="pdtStage">
  <div class="pdt-intro-overlay"><p>Scroll ↓ to assemble the dots</p></div>
  <div class="pdt-wrap"><div class="pdt-canvas" id="pdtMount"></div></div>
  <div class="pdt-hud"><span id="pdtPct">0</span>% assembled</div>
</section>
<section class="pdt-bottom"><p>The ring is complete — scroll up to scatter it.</p></section>