AtmosphèreGratuit

Frequency Bars

Vingt barres DOM animées par CSS transitions simulent un égaliseur audio — dégradé indigo→violet, flex bottom-aligned, zéro canvas, zéro dépendance.

JSClassic
Survolez ou cliquez la scène pour interagir

Cet effet est gratuit — la catégorie Atmosphère en contient 57 au total, dont 10 gratuits. Effect.Labs regroupe 811 effets vanilla. Découvrir la catégorie →

Exemples d'usage

① Hero / Landing — Player audio ou app de podcast

QuandUne lecture est en cours sur la landing page : le hero affiche le titre de la piste, l'artiste et un CTA « S'abonner » pendant que l'égaliseur anime en arrière-plan.
PourquoiL'égaliseur crée une preuve visuelle immédiate que l'audio fonctionne, sans attendre que l'utilisateur clique sur play — il réduit le doute et accélère la décision d'essai.
Réglages20 barres (barCount = 20), hauteur conteneur 120 px, gap 4 px, dégradé brand. La boucle requestAnimationFrame native assure un rendu fluide sans configuration supplémentaire.

② Composant produit — Widget niveau micro dans une app de visio

QuandLa carte de paramètres audio affiche le niveau du microphone en temps réel — typiquement dans un dashboard de conférence ou une app de stream.
PourquoiLa visualisation DOM est plus légère qu'un canvas pour un composant embarqué dans un UI dense ; les transitions CSS absorbe les mises à jour fréquentes sans provoquer de reflow visible.
Réglages12 barres (barCount = 12), hauteur 80 px, largeur 6 px, gap 3 px. Signal alimenté par AnalyserNode.getByteFrequencyData() à 60 FPS.
Aperçu gratuit

Écoutez avant d'acheter

Aperçu 30 secondes — sans compte

Intro · Effect.Labs
0:00 / 0:30

③ Micro-interaction de conversion — Aperçu audio sur une landing SaaS

QuandL'utilisateur survole ou clique un bouton « Écouter l'aperçu » — les barres s'animent pour signaler que l'audio est actif, puis un CTA « Essayer gratuitement » s'affiche.
PourquoiLe retour visuel immédiat (les barres bougent dès le clic) réduit la latence perçue avant le démarrage réel de l'audio, et l'association « son vivant = produit qui marche » renforce la confiance avant la conversion.
Réglages16 barres, hauteur 60 px, dégradé vert (#10b981#34d399) pour associer au bouton CTA. Transition à 0.08s pour un rendu plus nerveux.

Comment ça marche

Le conteneur #frequencyBars est un flexbox en align-items: flex-end : quand la propriété height d'une .bar augmente, la barre grandit vers le haut — l'axe de croissance est inversé par rapport à la lecture naturelle du DOM. Le JS génère les .bar avec la constante barCount (20 par défaut) ; flexbox les répartit automatiquement avec un gap de 4 px et les centre horizontalement.

La boucle est lancée dès le démarrage par un premier appel à requestAnimationFrame(animate) dans l'IIFE. À chaque frame, getSimulatedAudio(barCount, time) calcule 20 valeurs en [0, 1] ; animate applique ensuite bar.style.height = (10 + data[i] × 100) + 'px' sur chaque .bar. La CSS transition: height 0.05s interpole visuellement les sauts entre frames — le navigateur lisse sans calcul JS supplémentaire par image.

getSimulatedAudio(count, time) simule un spectre audio sans microphone ni fichier. Pour chaque barre i, une fréquence individuelle est calculée (freq = 1 + i × 0,5) puis superposée en trois composantes : sin(t × freq × 0,002) × 0,5 (onde lente), sin(t × freq × 0,003 + i) × 0,3 (onde décalée par barre), Math.random() × 0,2 (bruit). La valeur absolue est clampée à [0, 1] puis convertie en hauteur : minimum 10 px (silence) · maximum 110 px (pic). Pour un signal audio réel, remplacez l'appel à getSimulatedAudio() par les données d'un AnalyserNode.getByteFrequencyData() mappées sur [0, 1].

L'effet cible un seul id="frequencyBars" — il est prévu pour une instance unique par page. Pour plusieurs instances simultanées, il faut passer l'élément en paramètre à la logique d'animation plutôt qu'utiliser directement getElementById.

Accessibilité

  • prefers-reduced-motion géré : l'IIFE lit window.matchMedia('(prefers-reduced-motion: reduce)').matches au démarrage. Si réduit, la condition if (!reduceMotion) requestAnimationFrame(animate) ne relance pas la boucle après la première frame — les barres restent figées à leur dernière position calculée.
  • Aucun attribut ARIA sur #frequencyBars ni sur les .bar. Pour un usage décoratif, ajouter aria-hidden="true" sur le conteneur ; pour un usage informatif (visualisation d'un signal en cours), ajouter role="img" et aria-label.
  • Les <div class="bar"> ne sont pas focusables et n'interceptent aucun événement clavier — aucun risque de piège de focus.
  • Le dégradé indigo→violet (#6366f1#d946ef) sur fond sombre : composant graphique décoratif, WCAG 1.4.11 (Non-text Contrast ≥ 3:1) satisfait sur fond #0a0a0f.
  • L'effet est purement visuel — aucune information critique ne transite par la visualisation. Un lecteur d'écran n'a pas besoin d'en lire l'état.

Compatibilité navigateur

Nécessite CSS Transitions et Flexbox — supportés dans tous les navigateurs depuis 2015. Zéro dépendance externe.

Chrome 80+✓ Complet
Firefox 75+✓ Complet
Safari 13+✓ Complet
Edge 80+✓ Complet
Mobile iOS✓ Complet
Android Chrome✓ Complet

Sans support CSS Transitions, les barres sautent directement à la hauteur cible sans interpolation — l'effet reste lisible, mais saccadé. Sans Flexbox (IE 9 et antérieurs), la mise en page s'effondre ; pas de fallback prévu dans le code.

Le code

Copiez les trois blocs dans votre page. Aucune dépendance.

HTML
<div class="frequency-bars" id="frequencyBars">
  <div class="bar" style="height: 10px;">
  </div>
  <div class="bar" style="height: 10px;">
  </div>
  <div class="bar" style="height: 10px;">
  </div>
  <div class="bar" style="height: 10px;">
  </div>
  <div class="bar" style="height: 10px;">
  </div>
  <div class="bar" style="height: 10px;">
  </div>
  <div class="bar" style="height: 10px;">
  </div>
  <div class="bar" style="height: 10px;">
  </div>
  <div class="bar" style="height: 10px;">
  </div>
  <div class="bar" style="height: 10px;">
  </div>
  <div class="bar" style="height: 10px;">
  </div>
  <div class="bar" style="height: 10px;">
  </div>
  <div class="bar" style="height: 10px;">
  </div>
  <div class="bar" style="height: 10px;">
  </div>
  <div class="bar" style="height: 10px;">
  </div>
  <div class="bar" style="height: 10px;">
  </div>
  <div class="bar" style="height: 10px;">
  </div>
  <div class="bar" style="height: 10px;">
  </div>
  <div class="bar" style="height: 10px;">
  </div>
  <div class="bar" style="height: 10px;">
  </div>
</div>
CSS
.frequency-bars  {
   display: flex;
   align-items: flex-end;
   justify-content: center;
   gap: 4px;
   height: 120px;
   }

.frequency-bars .bar  {
   width: 8px;
   background: linear-gradient(to top, rgb(99, 102, 241), rgb(217, 70, 239));
   border-radius: 4px 4px 0px 0px;
   transition: height 0.05s;
   }
JavaScript (fx-0515)
(function () {
  const frequencyBars = document.getElementById('frequencyBars');
  if (!frequencyBars) return;
  const barCount = 20;
  for (let i = 0; i < barCount; i++) {
    const bar = document.createElement('div');
    bar.className = 'bar';
    bar.style.height = '10px';
    frequencyBars.appendChild(bar);
  }
  // Audio simulé (pas de micro ni de fichier) : superposition de sinus + bruit
  function getSimulatedAudio(count, time, baseFreq = 1) {
    const values = [];
    for (let i = 0; i < count; i++) {
      const freq = baseFreq + i * 0.5;
      const value = Math.abs(
        Math.sin(time * freq * 0.002) * 0.5 +
        Math.sin(time * freq * 0.003 + i) * 0.3 +
        Math.random() * 0.2
      );
      values.push(Math.min(1, value));
    }
    return values;
  }
  const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  function animate(time) {
    const bars = frequencyBars.querySelectorAll('.bar');
    const data = getSimulatedAudio(barCount, time);
    bars.forEach((bar, i) => { bar.style.height = (10 + data[i] * 100) + 'px'; });
    if (!reduceMotion) requestAnimationFrame(animate);
  }
  requestAnimationFrame(animate);
})();

Personnaliser

Options passées à l'API ou attributs data-* :

Option / propriétéDéfautEffet
barCount (constante JS) 20 Nombre de barres créées au démarrage. Augmenter à 32–64 pour un spectre dense ; descendre à 8–12 pour un widget compact. Modifier avant l'appel d'initialisation.
.frequency-bars height 120px Hauteur maximale que peut atteindre une barre. La valeur CSS contrôle le conteneur — les barres ne dépassent pas cette limite.
.frequency-bars .bar width 8px Largeur de chaque barre en px. Réduire à 4–5 px pour un spectre dense, augmenter à 12–16 px pour un style « chunky ».
.frequency-bars gap 4px Espacement entre barres. À ajuster proportionnellement à la largeur : ratio width/gap ≈ 2 conserve un look équilibré.
.frequency-bars .bar background linear-gradient(to top, #6366f1, #d946ef) Couleurs du dégradé. Remplacez par votre palette brand. Le sens to top aligne le pied (couleur 1) vers la tête (couleur 2) — inversez pour un effet différent.
.frequency-bars .bar transition height 0.05s Vitesse d'interpolation CSS. 0.05s (50 ms) = réactif. Monter à 0.12s pour un mouvement plus fluide et musical ; descendre à 0s pour un saut direct (visualiseur technique).
.frequency-bars .bar border-radius 4px 4px 0 0 Arrondi des coins hauts. 4px 4px 0 0 = capsule ouverte en bas. 4px (tous côtés) = capsule flottante ; 0 = rectangle strict.

FAQ

Le signal audio est-il simulé ou réel ?

Simulé par défaut : l'IIFE embarque getSimulatedAudio(), qui superpose des sinusoïdes à fréquences décalées (freq × 0,002 et freq × 0,003) et ajoute un bruit aléatoire (× 0,2) pour imiter un spectre vivant sans microphone ni fichier audio. Pour brancher un signal réel, créez un AnalyserNode, appelez analyser.getByteFrequencyData(dataArray) dans la boucle requestAnimationFrame, et mappez les valeurs [0–255] sur [0, 1] en remplacement de getSimulatedAudio().

Peut-on afficher plusieurs instances sur la même page ?

Pas directement : le JS cible getElementById('frequencyBars'), qui retourne un seul élément. Pour plusieurs instances, donnez un id unique à chaque conteneur (frequencyBars1, frequencyBars2…) et exécutez la logique d'animation indépendamment pour chacun, en passant l'élément comme variable plutôt qu'en répétant getElementById.

Quel est l'impact sur les performances ?

Très faible pour 20 barres à 10–20 mises à jour par seconde : CSS Transitions délèguent l'interpolation au moteur composite du navigateur. Au-delà de 60 barres ou à 60 FPS, surveiller les reflows — chaque mise à jour de bar.style.height peut déclencher un recalcul de layout. Pour des configurations denses ou haute fréquence, basculer vers un canvas 2D (une seule opération de rendu par frame).