Gallery✨ Premium

Infinite Loop

Endless carousel: after the last slide the track carries on to the first one in the same direction, without ever rewinding. Arrows, keyboard and swipe.

JSInfiniteTouch

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Gallery has 44 effects, including 6 free. Explore the category →

3 usage examples

A company canteen's dishes of the week — from Friday straight back to Monday — Infinite Loop example 1

① A company canteen's dishes of the week — from Friday straight back to Monday

WhenOn the intranet, an employee clicks › to see Tuesday's dish, then Wednesday's; once at Friday's soup, the next click brings Monday's pasta in from the right. The day pill and allergens follow.
WhyA canteen week starts again every Monday: the loop without rewinding mirrors that cycle, whereas rewinding through the whole week would feel like going back in time.
Settings310 px square photo: --inlp-width:100%, --inlp-height:100%, --inlp-radius:14px, dark arrows --inlp-arrow-bg:rgba(34,31,51,.4), data-slide="jour" ("day"), no "Swipe" hint. Dishes as <img>. Capture: click on › every 1.3 s.
Picking a city in the settings of a world clock — Infinite Loop example 2

② Picking a city in the settings of a world clock

WhenIn the settings panel, the user swipes the city name: Tokyo, Montreal, Dakar… and the local time there updates under the picker. After Lisbon, Tokyo comes back.
WhyA picker that turns forever feels like a dial: you never hit an end stop, which suits a list with no natural start or finish. At 74 px high it fits a form row.
Settings--inlp-height:74px, --inlp-ratio:auto, --inlp-radius:12px, white background, 30 px arrows with a blue stroke (scene CSS), data-slide="ville" ("city"). Text-only slides. Capture: the scene sends the real code a pointerdown then a pointerup 120 px further left.
The season's shows on the touch screen in a theatre lobby — Infinite Loop example 3

③ The season's shows on the touch screen in a theatre lobby

WhenIn the lobby, a visitor swipes the screen: each poster comes in with its dates and venue, and after the December concert the season starts over without going back. The "Swipe" hint reminds people of the gesture.
WhyAt a kiosk nobody reads instructions: the gesture is obvious, and there is never a last page to get stuck on. The large arrows stay there for those who prefer to tap.
SettingsFull screen: --inlp-height:100%, --inlp-radius:0, --inlp-arrow-bg:rgba(255,255,255,.16), data-slide="spectacle" ("show"), "Swipe" hint moved to the bottom right. Each slide: poster as <img> and text. Capture: driven swipe every 1.6 s (white circle = the finger).

How it works

The HTML only holds the real slides. On load, the script makes one full copy before them and one after (.inlp-clone), then sets the track on the first real slide without transition. There are thus three series side by side: copies, originals, copies.

A click on › slides the track one step (0.5 s). From the last slide, the next step lands on the copy of the first one, on the right; at the end of the transition (transitionend), the script puts the track back on the real first slide, with no animation. Both being identical, the eye sees nothing: the loop always carries on in the same direction. Same thing to the left with ‹.

Fast clicks are handled: if the track is still on a copy when the next click comes, it is first brought back to the matching original. The ← → keys and a horizontal drag of more than 50 px (Pointer Events) follow the same path. Under reduced motion there is no transitionend: the jump back happens right away.

The demo backgrounds are set by class (inlp-demo-1 to -5) so that the copies keep exactly the same look. Your photos go in as <img> inside the slides, and are copied too. The "Swipe" hint is decorative: its arrow sways three times then stops. Each .inlp-carousel has its own loop, nothing is declared on window, and a script pasted twice does not make the copies again.

Accessibility

  • Copies invisible to assistive tech: each copy gets aria-hidden="true" and inert, and loses its id and roles; a screen reader only finds the real slides.
  • Structure: role="region", aria-roledescription="carousel", slides as "slide" groups named "5 of 5"; only the visible slide stays reachable.
  • Announcement: after each change, an invisible aria-live="polite" area says "slide 1 of 5"; going from the last to the first is announced as an ordinary step.
  • Controls: two real named buttons, white focus ring, ← and →. The "Swipe" hint is hidden from screen readers (aria-hidden).
  • Reduced motion: no sliding and no swaying hint under prefers-reduced-motion: reduce; the slide changes at once and the loop stays endless.

Browser compatibility

About 5 KB of dependency-free JavaScript. Checked on 08/11/2026 with Playwright in Chromium, Firefox and WebKit: 15 slides in the track (5 + 2 × 5 copies), all with their background, order 1-2-3-4-5-1-2-3 going forward and 2-1-5 going back, twelve random bursts of fast clicks with no drift, two independent carousels, script pasted twice without new copies, finger swipe (Chromium, 390 px) and mouse drag, reduced motion. Minimum versions from @mdn/browser-compat-data 8.1.8 (inert).

Chrome 102+✓ Full (measured)
Firefox 112+✓ Full (measured)
Safari 15.5+✓ Full (measured in WebKit)
Edge 102+✓ Full (Chromium engine)
iOS Safari 15.5+✓ Not checked on a real device
Android Chrome 102+✓ Full (Chromium engine)

Without inert, copies stay hidden from screen readers (aria-hidden) but a link inside them could receive focus. Without aspect-ratio (Safari 14), set --inlp-height. Without JavaScript: no copies, the first slide alone.

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0170-root">
  <div class="inlp-carousel" role="region" aria-roledescription="carousel" aria-label="Slideshow" data-slide="slide" data-of="of">
    <div class="inlp-track">
      <div class="inlp-slide inlp-demo-1"><span class="inlp-caption">Slide 1</span></div>
      <div class="inlp-slide inlp-demo-2"><span class="inlp-caption">Slide 2</span></div>
      <!-- … only the real slides: the copies are made by the script -->
    </div>
    <button type="button" class="inlp-arrow inlp-prev" aria-label="Previous slide"><svg>…</svg></button>
    <button type="button" class="inlp-arrow inlp-next" aria-label="Next slide"><svg>…</svg></button>
    <div class="inlp-hint" aria-hidden="true"><svg>…</svg> Swipe</div>
  </div>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Slide duration (CSS — --inlp-duration on .fx-0170-root) 0.5s The silent jump back waits for the end of this transition.
Width, format, height (CSS — --inlp-width, --inlp-ratio, --inlp-height) 640px · 16 / 10 · auto The frame follows its container up to the maximum width.
Arrows and corners (CSS — --inlp-arrow-bg, --inlp-radius) white at 20% · 1rem Button background and frame corners.
"Swipe" hint (HTML — .inlp-hint) shown at the bottom, arrow sways 3 times Remove the block on desktop or in a small component; change the word for another language.
Demo backgrounds (CSS — .inlp-demo-1 to .inlp-demo-5) purple to yellow gradients Remove them with your images; prefer classes to :nth-child, which the copies would not match.
Demo area (CSS — --inlp-stage-bg, --inlp-stage-padding, --inlp-stage-min-height) #0a0a0f · 25px 16px · 300px Set to transparent, 0, 0 in your page.
Announced words (HTML — data-slide, data-of, aria-label) slide · of · Slideshow "day 3 of 5", "city 2 of 5"…

FAQ

No: only put your real slides. The script adds one copy of the set before and one after, once (5 slides give 15 items in the track); copies written by hand would be copied again and scramble the order.
The id attributes are removed from the copies to avoid duplicates, and the copies are inert: a link inside them can be neither clicked nor reached by keyboard. During the split second a copy is visible, the next click brings you back to the real slide anyway.
Yes, the script copies the whole series whatever its length. With a single slide there is nothing to scroll: hide the arrows and the hint in that case.