Atmosphère✨ Premium

Pulse Ring

Cinq anneaux CSS concentriques s’élargissent et s’estompent au rythme d’un battement simulé — un indicateur visuel pulsant en pur CSS et JS, sans canvas.

JSBeat

Vous aurez accès à la démo interactive avec un compte gratuit.

Cet effet fait partie d'Effect.Labs — 811 effets vanilla, une partie gratuite, une partie premium. Atmosphère comprend 57 effets, dont 10 gratuits. Découvrir la catégorie →

3 exemples d'usage

Hero / Landing — Service de streaming musical — Pulse Ring exemple 1

① Hero / Landing — Service de streaming musical

QuandEn haut de page d’une app audio ou d’un service de podcasts — l’anneau signale visuellement qu’un flux est en cours ou que le service écoute.
PourquoiLe rythme régulier des anneaux imite un battement de cœur ou un BPM, ancrant immédiatement l’identité audio du produit sans nécessiter de fond sonore.
RéglagesConteneur agrandi à 180 × 180 px dans .pulse-ring-container, durée réduite à 800 ms (remplacer 1000 par 800 dans progress = elapsed / 800), couleur indigo #6366f1 conservée.
Composant produit — Widget indicateur audio dans un dashboard — Pulse Ring exemple 2

② Composant produit — Widget indicateur audio dans un dashboard

QuandDans une carte de tableau de bord quand une source audio est active — micro ouvert, conférence en cours, enregistrement actif.
PourquoiL’anneau pulsant remplace un simple point clignotant : il communique la même information (actif) avec une sensation de profondeur et de rythme, sans distraire des autres métriques.
RéglagesConteneur réduit à 64 × 64 px, border-width abaissé à 2 px, durée portée à 1 200 ms, couleur #22c55e (vert = actif) dans le CSS.
Micro-interaction de conversion — Confirmation d’activation de compte — Pulse Ring exemple 3

③ Micro-interaction de conversion — Confirmation d’activation de compte

QuandÀ l’instant où l’utilisateur finalise son inscription ou active un abonnement — page de bienvenue ou modal de confirmation.
PourquoiLes anneaux concentriques qui s’élargissent signalent l’activation visuellement sans prendre toute la place — discrets mais assez ryth més pour marquer l’instant et renforcer le sentiment de réussite.
RéglagesCouleur #d946ef (violet vif) pour une touche célébratoire, durée à 900 ms, pool de 5 anneaux conservé, tempo accéléré en réduisant beatInterval à 350 ms.

Comment ça marche

Un pool de cinq div.pulse-ring est créé dynamiquement au démarrage (en plus des cinq présents dans le HTML source). Chaque anneau est un cercle CSS pur : border: 3px solid #6366f1 sur fond transparent, border-radius: 50 %, opacity: 0 par défaut. La détection de battement (detectBeat) calcule un intervalle variable : 500 + Math.sin(time × 0.001) × 100 ms, ce qui produit 1,7 à 2,5 battements par seconde, simulant un tempo de 100 à 150 BPM.

À chaque battement détecté, le premier anneau inactif du pool reçoit active = true et un startTime horodaté. Chaque frame du requestAnimationFrame, la fonction calcule un progress linéaire : progress = (time − startTime) / 1000 sur une fenêtre d’une seconde. L’opacité descend de 1 à 0 et l’échelle monte de 1× à 2× — entièrement via des style.opacity et style.transform écrits à chaque frame. Aucune @keyframes CSS n’est utilisée.

L’interpolation est strictement linéaire : ni ease-in ni ease-out. Le diamètre de départ est fixé par width: 120px; height: 120px sur .pulse-ring-container, chaque anneau partant avec inset: 0 (120 × 120 px) et grandissant jusqu’à 240 × 240 px à progress = 1 avant de se réinitialiser.

L’animation tourne dans une boucle requestAnimationFrame partagée avec jusqu’à neuf autres visualiseurs audio de la même IIFE (barres de fréquences, visualiseur circulaire, onde, burst de particules…). Tous partagent la même variable globale lastBeat, ce qui synchronise l’ensemble des visualiseurs sur le même tempo simulé.

Accessibilité

  • prefers-reduced-motion absent du code — l’IIFE ne vérifie ni la media query CSS ni window.matchMedia : l’animation tourne quelle que soit la préférence OS. Pour un usage en production, ajouter @media (prefers-reduced-motion: reduce) { .pulse-ring { display: none; } } ou conserver le résultat de matchMedia pour stopper le rAF.
  • Aucun attribut aria-* sur le conteneur ni sur les anneaux — l’effet est invisible aux lecteurs d’écran. Ajouter aria-hidden="true" sur .pulse-ring-container s’il est purement décoratif.
  • Aucune interaction clavier — l’effet est passif et décoratif, ce qui est correct pour un indicateur audio. Si le conteneur reçoit un tabindex, le retirer ou masquer l’élément aux technologies d’assistance.
  • Contraste des anneaux (#6366f1 sur fond transparent) : élément décoratif non-textuel, WCAG 1.4.3 ne s’applique pas. Si l’anneau sert d’indicateur fonctionnel (signal actif/inactif), WCAG 1.4.11 (rapport ≥ 3:1 entre l’anneau et le fond réel) s’applique.
  • Aucun role ni aria-label sur le conteneur — ajouter ces attributs si l’animation transmet une information signifiante (ex. : détection de battement, signal actif en cours).

Compatibilité navigateur

Nécessite requestAnimationFrame, document.createElement, Array.prototype.find et la propriété CSS inset — couverts par tous les navigateurs modernes.

Chrome 87+✓ Complet
Firefox 87+✓ Complet
Safari 14.1+✓ Complet
Edge 87+✓ Complet
Mobile iOS 15+✓ Complet
Android Chrome✓ Complet

Sans <code>requestAnimationFrame</code> (navigateurs très anciens), la boucle ne démarre pas et les anneaux restent invisibles (<code>opacity: 0</code>) — aucune erreur JS fatale, la page reste fonctionnelle. <code>inset</code> nécessite Chrome 87+, Firefox 87+, Safari 14.1+ ; <code>transform: scale()</code> est universel.

Le code

Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :

index.html — structure
<div class="pulse-ring-container" id="pulseRingContainer">
  <div class="pulse-ring"></div>
  <div class="pulse-ring"></div>
  <div class="pulse-ring"></div>
  <div class="pulse-ring"></div>
  <div class="pulse-ring"></div>
  <!-- Le JS ajoute 5 anneaux supplémentaires au démarrage (seuls ceux-ci sont animés) -->
</div>
🔒 Débloquer le code complet — à partir de 2,99 € le 1er mois

HTML + CSS + JS complet, prêt à coller — avec des centaines d'effets premium.

Personnaliser

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

Option / propriétéDéfautEffet
border: 3px solid #6366f1 (CSS .pulse-ring) 3px solid #6366f1 Couleur et épaisseur de chaque anneau. Remplacez la valeur hex pour coller à votre charte ; montez à 4–6 px pour un trait plus affirmé.
width / height (CSS .pulse-ring-container) 120px × 120px Diamètre de départ des anneaux. Montez à 180–240 px pour un usage hero ; réduisez à 60–80 px pour un indicateur compact dans une carte.
elapsed / 1000 (JS animatePulseRing) 1000 ms Durée d’animation de chaque anneau. Baissez à 600 ms pour un battement nerveux ; montez à 1 500–2 000 ms pour un effet lent et hypnotique.
scale(${1 + progress}) (JS animatePulseRing) échelle finale 2× (delta 1) Expansion maximale de l’anneau. Remplacez par scale(${1 + 2 * progress}) pour tripler la taille finale ; 1 + 0.5 * progress pour une expansion plus discrète.
500 + Math.sin(time * 0.001) * 100 (JS detectBeat) 500 ± 100 ms Intervalle inter-battements. Baissez le centre à 350 pour ~170 BPM ; montez à 800 pour un rythme lent (~75 BPM). Remplacez toute la formule par une constante fixe pour un tempo régulier.
for (let i = 0; i < 5; i++) (JS initialisation pool) 5 anneaux Taille du pool d’anneaux. Montez à 8–10 pour plus de chevauchement simultané ; descendez à 3 pour un rendu épuré.

FAQ

Le code utilise getElementById(’pulseRingContainer’) et un tableau rings fermé dans l’IIFE — une seule instance est gérée par défaut. Pour plusieurs instances, extrayez la logique dans une fonction usine qui prend un élément en paramètre, crée son propre pool et partage la boucle requestAnimationFrame : la détection de battement via lastBeat reste partagée, gardant toutes les instances synchronisées sur le même tempo.
Par JavaScript pur. animatePulseRing écrit style.opacity et style.transform directement sur chaque anneau à chaque frame du requestAnimationFrame — aucune @keyframes, aucune propriété animation CSS, aucun transition. Cela donne un contrôle total sur le timing et l’easing, au prix d’une dépendance forte à la boucle rAF.
Remplacez detectBeat par une fonction qui analyse l’énergie d’un flux AudioContext. Connectez votre source à un AnalyserNode, calculez le RMS des données temporelles à chaque frame, et déclenchez un battement quand le RMS dépasse un seuil. La variable lastBeat peut rester pour éviter des déclenchements trop rapprochés.