Logo Wall Cycle
Documentation
Webflow
Code
Setup: External Scripts
HTML
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15/dist/ScrollTrigger.min.js"></script>Step 1: Add HTML
HTML
<div data-logo-wall-shuffle="false" data-logo-wall-cycle-init="" class="logo-wall">
<div class="logo-wall__collection">
<div data-logo-wall-list="" class="logo-wall__list">
<div data-logo-wall-item="" class="logo-wall__item">
<div data-logo-wall-target-parent="" class="logo-wall__logo">
<div class="logo-wall__logo-before"></div>
<div data-logo-wall-target="" class="logo-wall__logo-target">
<img src="https://cdn.prod.website-files.com/68836e3f51ac98fec14ceed2/688370ea9d37fbceb3be49cb_logo-webflow.svg" loading="lazy" width="100" alt="" class="logo-wall__logo-img">
</div>
</div>
</div>
<div data-logo-wall-item="" class="logo-wall__item">
<div data-logo-wall-target-parent="" class="logo-wall__logo">
<div class="logo-wall__logo-before"></div>
<div data-logo-wall-target="" class="logo-wall__logo-target">
<img src="https://cdn.prod.website-files.com/68836e3f51ac98fec14ceed2/688370ea48d4fb0c708dd1dc_logo-microsoft.svg" loading="lazy" width="100" alt="" class="logo-wall__logo-img">
</div>
</div>
</div>
<div data-logo-wall-item="" class="logo-wall__item">
<div data-logo-wall-target-parent="" class="logo-wall__logo">
<div class="logo-wall__logo-before"></div>
<div data-logo-wall-target="" class="logo-wall__logo-target">
<img src="https://cdn.prod.website-files.com/68836e3f51ac98fec14ceed2/688370ea9ba384ff47fa5d51_logo-asana.svg" loading="lazy" width="100" alt="" class="logo-wall__logo-img">
</div>
</div>
</div>
<div data-logo-wall-item="" class="logo-wall__item">
<div data-logo-wall-target-parent="" class="logo-wall__logo">
<div class="logo-wall__logo-before"></div>
<div data-logo-wall-target="" class="logo-wall__logo-target">
<img src="https://cdn.prod.website-files.com/68836e3f51ac98fec14ceed2/688370eaec918fbd4a0acc12_logo-snapchat.svg" loading="lazy" width="100" alt="" class="logo-wall__logo-img">
</div>
</div>
</div>
<div data-logo-wall-item="" class="logo-wall__item">
<div data-logo-wall-target-parent="" class="logo-wall__logo">
<div class="logo-wall__logo-before"></div>
<div data-logo-wall-target="" class="logo-wall__logo-target">
<img src="https://cdn.prod.website-files.com/68836e3f51ac98fec14ceed2/688370ea155a551c08692a03_logo-google.svg" loading="lazy" width="100" alt="" class="logo-wall__logo-img">
</div>
</div>
</div>
<div data-logo-wall-item="" class="logo-wall__item">
<div data-logo-wall-target-parent="" class="logo-wall__logo">
<div class="logo-wall__logo-before"></div>
<div data-logo-wall-target="" class="logo-wall__logo-target">
<img src="https://cdn.prod.website-files.com/68836e3f51ac98fec14ceed2/688370eafdf2b295d65f9450_logo-bluesky.svg" loading="lazy" width="100" alt="" class="logo-wall__logo-img">
</div>
</div>
</div>
<div data-logo-wall-item="" class="logo-wall__item">
<div data-logo-wall-target-parent="" class="logo-wall__logo">
<div class="logo-wall__logo-before"></div>
<div data-logo-wall-target="" class="logo-wall__logo-target">
<img src="https://cdn.prod.website-files.com/68836e3f51ac98fec14ceed2/688370ea68a433ee5808ed90_logo-codepen.svg" loading="lazy" width="100" alt="" class="logo-wall__logo-img">
</div>
</div>
</div>
<div data-logo-wall-item="" class="logo-wall__item">
<div data-logo-wall-target-parent="" class="logo-wall__logo">
<div class="logo-wall__logo-before"></div>
<div data-logo-wall-target="" class="logo-wall__logo-target">
<img src="https://cdn.prod.website-files.com/68836e3f51ac98fec14ceed2/688370ea2ebc0415055d04f3_logo-linkedin.svg" loading="lazy" width="100" alt="" class="logo-wall__logo-img">
</div>
</div>
</div>
<div data-logo-wall-item="" class="logo-wall__item">
<div data-logo-wall-target-parent="" class="logo-wall__logo">
<div class="logo-wall__logo-before"></div>
<div data-logo-wall-target="" class="logo-wall__logo-target">
<img src="https://cdn.prod.website-files.com/68836e3f51ac98fec14ceed2/688370ea7699561e6f9f008f_logo-android.svg" loading="lazy" width="100" alt="" class="logo-wall__logo-img">
</div>
</div>
</div>
<div data-logo-wall-item="" class="logo-wall__item">
<div data-logo-wall-target-parent="" class="logo-wall__logo">
<div class="logo-wall__logo-before"></div>
<div data-logo-wall-target="" class="logo-wall__logo-target">
<img src="https://cdn.prod.website-files.com/68836e3f51ac98fec14ceed2/688370ea753f2afe2f6b036f_logo-apple.svg" loading="lazy" width="100" alt="" class="logo-wall__logo-img">
</div>
</div>
</div>
<div data-logo-wall-item="" class="logo-wall__item">
<div data-logo-wall-target-parent="" class="logo-wall__logo">
<div class="logo-wall__logo-before"></div>
<div data-logo-wall-target="" class="logo-wall__logo-target">
<img src="https://cdn.prod.website-files.com/68836e3f51ac98fec14ceed2/688370eabec1e0c00348b5ed_logo-twitter.svg" loading="lazy" width="100" alt="" class="logo-wall__logo-img">
</div>
</div>
</div>
<div data-logo-wall-item="" class="logo-wall__item">
<div data-logo-wall-target-parent="" class="logo-wall__logo">
<div class="logo-wall__logo-before"></div>
<div data-logo-wall-target="" class="logo-wall__logo-target">
<img src="https://cdn.prod.website-files.com/68836e3f51ac98fec14ceed2/688370ea0e0e1dc81a9b5799_logo-osmo.svg" loading="lazy" width="100" alt="" class="logo-wall__logo-img">
</div>
</div>
</div>
<div data-logo-wall-item="" class="logo-wall__item">
<div data-logo-wall-target-parent="" class="logo-wall__logo">
<div class="logo-wall__logo-before"></div>
<div data-logo-wall-target="" class="logo-wall__logo-target">
<img src="https://cdn.prod.website-files.com/68836e3f51ac98fec14ceed2/688370ea36c91584afe43e2d_logo-medium.svg" loading="lazy" width="100" alt="" class="logo-wall__logo-img">
</div>
</div>
</div>
<div data-logo-wall-item="" class="logo-wall__item">
<div data-logo-wall-target-parent="" class="logo-wall__logo">
<div class="logo-wall__logo-before"></div>
<div data-logo-wall-target="" class="logo-wall__logo-target">
<img src="https://cdn.prod.website-files.com/68836e3f51ac98fec14ceed2/688370ea87b05cdce0387084_logo-eventbrite.svg" loading="lazy" width="100" alt="" class="logo-wall__logo-img">
</div>
</div>
</div>
<div data-logo-wall-item="" class="logo-wall__item">
<div data-logo-wall-target-parent="" class="logo-wall__logo">
<div class="logo-wall__logo-before"></div>
<div data-logo-wall-target="" class="logo-wall__logo-target">
<img src="https://cdn.prod.website-files.com/68836e3f51ac98fec14ceed2/688370eaf4465d763c2f9b2a_logo-behance.svg" loading="lazy" width="100" alt="" class="logo-wall__logo-img">
</div>
</div>
</div>
<div data-logo-wall-item="" class="logo-wall__item">
<div data-logo-wall-target-parent="" class="logo-wall__logo">
<div class="logo-wall__logo-before"></div>
<div data-logo-wall-target="" class="logo-wall__logo-target">
<img src="https://cdn.prod.website-files.com/68836e3f51ac98fec14ceed2/688370eaec1d445957d7e3a1_logo-chatgpt.svg" loading="lazy" width="100" alt="" class="logo-wall__logo-img">
</div>
</div>
</div>
</div>
</div>
</div>Step 2: Add CSS
CSS
.logo-wall {
display: flex;
justify-content: center;
width: 100%;
}
.logo-wall__collection {
width: 100%;
}
.logo-wall__list {
display: flex;
flex-flow: wrap;
}
.logo-wall__item {
width: 25%;
position: relative;
}
[data-logo-wall-list] [data-logo-wall-item]:nth-child(n+9) {
display: none;
}
.logo-wall__logo {
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
.logo-wall__logo-before {
padding-top: 66.66%;
}
.logo-wall__logo-target {
justify-content: center;
align-items: center;
width: 66.66%;
height: 40%;
display: flex;
position: absolute;
}
.logo-wall__logo-img {
width: 100%;
height: 100%;
max-height: 100%;
}
@media screen and (max-width: 991px) {
.logo-wall__item {
width: 33.333%;
}
[data-logo-wall-list] [data-logo-wall-item]:nth-child(n+7) {
display: none;
}
}Step 2: Add Javascript
Step 3: Add Javascript
Javascript
function initLogoWallCycle() {
const loopDelay = 1.5; // Loop Duration
const duration = 0.9; // Animation Duration
document.querySelectorAll('[data-logo-wall-cycle-init]').forEach(root => {
const list = root.querySelector('[data-logo-wall-list]');
const items = Array.from(list.querySelectorAll('[data-logo-wall-item]'));
const shuffleFront = root.getAttribute('data-logo-wall-shuffle') !== 'false';
const originalTargets = items
.map(item => item.querySelector('[data-logo-wall-target]'))
.filter(Boolean);
let visibleItems = [];
let visibleCount = 0;
let pool = [];
let pattern = [];
let patternIndex = 0;
let tl;
function isVisible(el) {
return window.getComputedStyle(el).display !== 'none';
}
function shuffleArray(arr) {
const a = arr.slice();
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
function setup() {
if (tl) {
tl.kill();
}
visibleItems = items.filter(isVisible);
visibleCount = visibleItems.length;
pattern = shuffleArray(
Array.from({ length: visibleCount }, (_, i) => i)
);
patternIndex = 0;
// remove all injected targets
items.forEach(item => {
item.querySelectorAll('[data-logo-wall-target]').forEach(old => old.remove());
});
pool = originalTargets.map(n => n.cloneNode(true));
let front, rest;
if (shuffleFront) {
const shuffledAll = shuffleArray(pool);
front = shuffledAll.slice(0, visibleCount);
rest = shuffleArray(shuffledAll.slice(visibleCount));
} else {
front = pool.slice(0, visibleCount);
rest = shuffleArray(pool.slice(visibleCount));
}
pool = front.concat(rest);
for (let i = 0; i < visibleCount; i++) {
const parent =
visibleItems[i].querySelector('[data-logo-wall-target-parent]') ||
visibleItems[i];
parent.appendChild(pool.shift());
}
tl = gsap.timeline({ repeat: -1, repeatDelay: loopDelay });
tl.call(swapNext);
tl.play();
}
function swapNext() {
const nowCount = items.filter(isVisible).length;
if (nowCount !== visibleCount) {
setup();
return;
}
if (!pool.length) return;
const idx = pattern[patternIndex % visibleCount];
patternIndex++;
const container = visibleItems[idx];
const parent =
container.querySelector('[data-logo-wall-target-parent]') ||
container.querySelector('*:has(> [data-logo-wall-target])') ||
container;
const existing = parent.querySelectorAll('[data-logo-wall-target]');
if (existing.length > 1) return;
const current = parent.querySelector('[data-logo-wall-target]');
const incoming = pool.shift();
gsap.set(incoming, { yPercent: 50, autoAlpha: 0 });
parent.appendChild(incoming);
if (current) {
gsap.to(current, {
yPercent: -50,
autoAlpha: 0,
duration,
ease: "expo.inOut",
onComplete: () => {
current.remove();
pool.push(current);
}
});
}
gsap.to(incoming, {
yPercent: 0,
autoAlpha: 1,
duration,
delay: 0.1,
ease: "expo.inOut"
});
}
setup();
ScrollTrigger.create({
trigger: root,
start: 'top bottom',
end: 'bottom top',
onEnter: () => tl.play(),
onLeave: () => tl.pause(),
onEnterBack: () => tl.play(),
onLeaveBack: () => tl.pause()
});
document.addEventListener('visibilitychange', () =>
document.hidden ? tl.pause() : tl.play()
);
});
}
// Initialize Logo Wall Cycle
document.addEventListener('DOMContentLoaded', () => {
initLogoWallCycle();
});Implementation
Container
Add the [data-logo-wall-cycle-init] attribute to the outermost wrapper (the “logo wall container”). This will initialize a single logo wall animation instance.
You can configure shuffling behavior with the optional [data-logo-wall-shuffle] attribute. When this is enabled the first visible logo's will be shuffeled too.
List
Apply [data-logo-wall-list] to the element that wraps all logo items.
Item
Each logo entry requires [data-logo-wall-item]. This acts as a placeholder for rotating logos.
We use CSS to hide the extra logos, this can be different on every viewport. The script will look for display: none; to decide what logos are not included.
/* Desktop: Show only first 8 */
[data-logo-wall-list] > [data-logo-wall-item]:nth-child(n+9) {
display: none;
}
/* Tablet/mobile: Show only first 6 */
@media (max-width: 768px) {
[data-logo-wall-list] > [data-logo-wall-item]:nth-child(n+7) {
display: none;
}
}Target & Target Parent
Inside each item, add a [data-logo-wall-target] element. This is the element that gets animated in/out during swaps.
You can wrap this in a parent element marked with [data-logo-wall-target-parent] if you want more control over placement. When the this attribute is not added the next logo will be placed in the [data-logo-wall-item] element.
Customizing the Animation
This example uses a slide-and-fade animation to cycle logos in and out. You can easily replace it with any animation style that better suits your project.
Resource details
Published
July 27, 2025
Category
Sections & Layouts
Popularity
3.5K visitors
Need help?
Join Slack