Fireflies
30 lucioles sur canvas : pulsation sinusoïdale individuelle, mouvement Brownien amorti, halo radial jaune-vert et wrapping torique aux bords.
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



Comment ça marche
setupCanvas("psFireflies") dimensionne le <canvas> sur les dimensions de son conteneur parent via getBoundingClientRect(), puis 30 particules sont créées — chacune avec position aléatoire, vecteur vitesse initial (vx = vy = ±0,25 px/frame max), une phase de départ dans [0, 2π] et une fréquence de pulsation speed dans [0,01 – 0,03] rad/frame.
À chaque frame rAF, une impulsion aléatoire de ±0,025 px est ajoutée à chaque composante de vitesse (bruit de Brown). Un amortissement ×0,98 borne la dérive sans la figer — la luciole ondule organiquement. Aux quatre bords, un wrapping torique téléporte la particule du côté opposé : elle disparaît à droite, réapparaît à gauche.
Chaque luciole possède sa propre fréquence : n = (sin(frame × speed + phase) + 1) / 2. Ce scalaire oscille entre 0 et 1 et module simultanément l'opacité du noyau (0,30 + 0,70×n) et son rayon effectif (size + 2×n). Les 30 phases et fréquences étant indépendantes, les lucioles ne clignotent jamais à l'unisson.
Pour chaque luciole, un createRadialGradient de rayon 6×h dessine le halo — couleur rgba(180,230,80, 0,15×n) au centre vers transparent. Le noyau solide est un arc plein en rgba(200,250,100, 0,30 + 0,70×n). L'effet ne gère ni prefers-reduced-motion, ni IntersectionObserver, ni API publique — c'est un IIFE autonome qui tourne en continu dès le chargement de la page.
Accessibilité
- prefers-reduced-motion absent : l'effet ne détecte pas la préférence système. L'animation tourne sans condition ; ajoutez
window.matchMedia('(prefers-reduced-motion:reduce)').matchespour stopper le RAF si l'accessibilité réduite est requise. - Le
<canvas>ne porte niaria-hidden, nirole, niaria-labeldans le code d'origine — ajoutezaria-hidden="true"si l'effet est purement décoratif. - Contraste du noyau :
rgba(200,250,100)sur fond#0a0a0fdépasse un ratio de luminance 7:1 — au-dessus du seuil AAA pour les composants graphiques. - Aucune interaction clavier ni pointeur n'est enregistrée — le canvas ne reçoit pas le focus, la navigation au clavier sur la page n'est pas impactée.
- Effet purement décoratif : aucun contenu textuel ni informatif ne réside dans le canvas — le contenu overlay associé (titre, CTA) doit vivre dans le DOM, pas dans le canvas.
Compatibilité navigateur
Requiert Canvas 2D et requestAnimationFrame — supportés dans tous les navigateurs modernes. Zéro dépendance externe.
Sans support <code>canvas</code>, le conteneur parent s'affiche avec son fond — aucune erreur JS fatale, le reste de la page reste fonctionnel.
Le code
Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :
<div class="ps-scene" role="img" aria-label="Lucioles animées en vol nocturne">
<div class="ps-canvas-wrap">
<canvas class="ps-canvas" id="psFireflies" aria-hidden="true"></canvas>
</div>
<!-- Facultatif : scrim + contenu overlay -->
<div class="ps-scrim" aria-hidden="true"></div>
<div class="ps-overlay">
<h2 class="ps-overlay-title">Votre message</h2>
<button class="ps-overlay-cta">CTA</button>
</div>
</div>
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éfaut | Effet |
|---|---|---|
| Nombre de particules (for e < 30) | 30 | Nombre de lucioles simultanées. 10–15 pour un fond discret dans une petite carte, 50–60 pour une scène plein-écran dense. |
| Vitesse initiale (0.5 × (random − 0.5)) | ±0,25 px/frame | Changez le préfacteur 0.5 pour moduler la dérive de départ. 0,3 = vol lent et posé, 0,8 = vol rapide et nerveux. |
| Bruit Brownien (0.05 × (random − 0.5)) | ±0,025 px/frame | Amplitude de l'impulsion aléatoire ajoutée à chaque frame. 0,02 = trajectoire quasi linéaire, 0,10 = vol très erratique et saccadé. |
| Amortissement (vx *= 0.98) | 0.98 | Facteur de dissipation de vitesse. 0,96 = décélération rapide (petits trajets), 0,995 = glissement quasi inertiel (longues trajectoires douces). |
| Fréquence de pulsation (0.02 × random + 0.01) | [0,01 – 0,03] rad/frame | Plage de vitesses de clignotement. 0.04 × random + 0.02 pour des battements vifs, 0.008 × random + 0.004 pour une respiration très lente. |
| Rayon de base (2 × random + 1.5) | [1,5 – 3,5] px | Taille des noyaux. 1 × random + 1 = insectes très petits, 3 × random + 2 = points plus visibles sur fond moins sombre. |
| Teinte du halo (rgba(180,230,80,…)) | jaune-vert #b4e650 | Couleur du dégradé radial. rgba(80,180,230,…) pour un halo bleu électrique, rgba(255,160,40,…) pour une lueur ambre. |
| Rayon du halo (6 × h) | 6× le rayon effectif | Multiplicateur de la taille du dégradé. 4× = halo serré et ponctuel, 10× = aura large et vaporeuse autour du noyau. |
FAQ
rgba(180,230,80,…) et le noyau rgba(200,250,100,…). Remplacez les deux par vos valeurs RVB — par exemple rgba(80,180,230,…) et rgba(100,210,255,…) pour des lucioles bleues, ou rgba(255,160,40,…) et rgba(255,200,80,…) pour un rendu ambre.requestAnimationFrame quand l'onglet n'est pas actif (zéro CPU). En revanche, l'effet n'implémente pas d'IntersectionObserver — il continue de tourner si la section est hors du viewport mais l'onglet reste au premier plan. Ajoutez un IntersectionObserver sur le canvas pour stopper le RAF hors-écran.psFireflies via getElementById — une seule instance est possible. Pour plusieurs instances, extrayez la logique dans une fonction paramétrée par l'id du canvas et appelez-la une fois par composant.