SVG✨ Premium

Fractal Tree

Un arbre fractal dessiné par une fonction récursive sur 10 niveaux : ses branches s'écartent et se resserrent doucement, du tronc rose pâle aux pointes vertes.

CanvasFractalRecursive

Mis à jour le

Cet effet fait partie d'Effect.Labs — 840 effets vanilla, une partie gratuite, une partie premium. SVG comprend 30 effets, dont 3 gratuits. Découvrir la catégorie →

3 exemples d'usage

Arbre en tête de la page d'accueil d'un site de généalogie — Fractal Tree exemple 1

① Arbre en tête de la page d'accueil d'un site de généalogie

QuandSur Lignavel, l'arbre occupe la moitié droite du hero, posé sur une ligne de sol ; à gauche, le titre « Chaque branche a son histoire » et le champ de recherche par nom.
PourquoiUn arbre qui se ramifie en deux à chaque génération est la métaphore exacte d'un arbre généalogique, et son lent balancement donne de la vie sans détourner du formulaire.
RéglagesRacine en position:absolute sur 47 % de la largeur, de 40 px sous le haut jusqu'en bas (--ftr-height:100%) : tronc de 64 px. Colonne de texte limitée à 46 % pour que les branches les plus écartées ne passent jamais sous le formulaire.
Carte de récompense après 30 jours d'affilée dans une appli d'habitudes — Fractal Tree exemple 2

② Carte de récompense après 30 jours d'affilée dans une appli d'habitudes

QuandDans Pousselin, la carte de droite reste verrouillée tant que la série fait 29 jours ; le clic sur « Valider aujourd'hui » coche la 30e case et fait apparaître l'arbre avec le message de félicitations.
PourquoiLa récompense est un objet vivant plutôt qu'un badge figé : l'arbre se balance tant que la carte est ouverte, ce qui donne envie de revenir le voir.
RéglagesLa racine est livrée avec l'attribut hidden ; le script de la scène le retire au clic. Le code vendu, qui attend une taille non nulle, démarre à ce moment-là. Zone de 220 × 222 px, tronc de 44 px.
Petite illustration de l'état vide d'un carnet de jardin — Fractal Tree exemple 3

③ Petite illustration de l'état vide d'un carnet de jardin

QuandDans Semavert, quand aucune plantation n'est saisie pour la saison, un arbre de 132 × 112 px se balance au-dessus du message « Aucune plantation cette saison » et du bouton d'ajout.
PourquoiUn état vide animé rassure (l'appli n'est pas en panne) et invite à agir ; à cette taille, l'arbre reste lisible car l'épaisseur des branches suit leur niveau, pas la taille de la zone.
RéglagesRacine dans un bloc de 132 × 112 px (--ftr-height:100%), canvas en aria-hidden="true" puisque le titre dit déjà l'état. Fond de la scène aligné sur celui de la racine (#0a0a0f).

Comment ça marche

La fonction branch() trace un segment, puis s'appelle deux fois depuis son extrémité, avec une longueur réduite à 70 % et un angle décalé à gauche puis à droite. Elle s'arrête au 10e niveau ou quand une branche ferait moins de 2 px : 1 023 segments en tout. Le tronc part du milieu du bord bas et mesure 20 % de la hauteur de la zone ; l'arbre entier occupe environ 64 % de la hauteur et 76 % de la hauteur en largeur, donc une zone de 300 px de haut a besoin d'au moins 230 px de large.

Seul l'écartement bouge : il vaut 0,5 + sin(phase) × 0,2 radian, de 0,3 à 0,7, et la phase avance de 0,0012 radian par milliseconde, un cycle complet toutes les 5,2 s environ. Le temps écoulé est plafonné à 50 ms par image, donc le rythme est le même à 60 et à 120 Hz. Rien ne pousse et rien n'est aléatoire : l'arbre est identique à chaque chargement. La teinte et l'épaisseur dépendent du niveau : tronc de 5 px, rose très clair (teinte 320, luminosité 90 %), pointes de 0,5 px, vertes (teinte 140, luminosité 45 %).

Le canvas est effacé à chaque image (fond transparent : c'est le background de .fx-0616-root qu'on voit) puis entièrement redessiné. Sa taille est lue sur la zone affichée et multipliée par devicePixelRatio ; un ResizeObserver la suit, un IntersectionObserver arrête la boucle hors écran, et rien ne démarre tant que la zone mesure 0 px. Chaque racine de la page tourne seule, et une seule fois (data-ftr-ready), même si le script est collé deux fois.

Accessibilité

  • Libellé : role="img" et aria-label sur le canvas. Quand un texte voisin dit déjà la même chose (état vide, carte de récompense), passez-le en aria-hidden="true".
  • Mouvement réduit : sous prefers-reduced-motion: reduce, l'arbre est dessiné une fois, immobile, à son écartement moyen de 0,5 radian.
  • Pause : le balancement dure tant que l'arbre est visible. Pour le critère WCAG 2.2.2, un bouton qui bascule l'attribut hidden de la racine suffit : la boucle s'arrête à taille nulle et reprend à la même phase.
  • Clavier : l'effet n'a aucun élément interactif.
  • Contraste : les pointes vertes à 45 % de luminosité restent visibles sur fond sombre ; sur fond blanc, les dernières branches de 0,5 px deviennent très discrètes.

Compatibilité navigateur

Canvas 2D et environ 3,4 Ko de JavaScript, sans dépendance. ResizeObserver : Chrome 64, Firefox 69, Safari 13.1 ; IntersectionObserver : Chrome 51, Firefox 55, Safari 12.1 (données MDN). Mesuré dans Chromium 151, Firefox 153 et WebKit 26.5 : rendu à densité 2, deux copies indépendantes, démarrage après un affichage différé, arbre immobile en mouvement réduit, aucune image calculée hors écran.

Chrome 64+✓ Complet (mesuré)
Firefox 69+✓ Complet (mesuré)
Safari 13.1+✓ Complet (mesuré dans WebKit)
Edge 79+✓ Complet (moteur Chromium)
Safari iOS 13.4+✓ Complet (WebKit, non vérifié sur appareil)
Chrome Android 64+✓ Complet (moteur Chromium)

Sans ResizeObserver, l'arbre suit seulement le redimensionnement de la fenêtre ; sans IntersectionObserver, il continue de se balancer hors écran. Sans JavaScript, la zone garde le fond de la racine, sans arbre.

Le code

Structure HTML à coller dans votre page (le CSS + JS sont inclus avec votre achat) :

index.html — structure
<div class="fx-0616-root">
  <canvas class="ftr-canvas" role="img" aria-label="Arbre fractal dont les branches s'écartent et se resserrent doucement"></canvas>
</div>

Paiement unique, sans abonnement — le code est à toi pour toujours

Personnaliser

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

Option / propriétéDéfautEffet
Profondeur (JS — LEVELS) 10 niveaux (1 023 segments) 7 donne un arbre stylisé aux branches nettes. Les branches de moins de 2 px ne sont jamais tracées : au-delà de 10, un niveau ne s'ajoute que dans une zone assez haute (le 11e dès 355 px de haut, le 12e dès 506 px, jusqu'à 4 095 segments).
Tronc (JS — TRUNK) 0.2 (20 % de la hauteur) 0.25 fait un arbre plus haut qui touche presque le bord supérieur ; 0.15 laisse de la place au-dessus pour un titre.
Réduction par niveau (JS — REDUCTION) 0.7 0.6 donne une couronne compacte, 0.75 des branches longues qui débordent sur les côtés.
Écartement (JS — SPREAD) 0.5 radian 0.3 dessine un peuplier étroit, 0.8 un arbre très ouvert.
Balancement (JS — SWAY, SPEED) 0.2 radian, 0.0012 radian/ms SWAY à 0.05 pour un frémissement, SPEED à 0.0005 pour un cycle de 12 s.
Couleurs (JS — ligne strokeStyle de branch()) teinte 120 + niveau × 20, luminosité 40 + niveau × 5 % Une teinte fixe (par exemple 30) et la même formule de luminosité donnent un arbre d'automne en dégradé.
Fond (CSS — background de .fx-0616-root) #0a0a0f Le canvas est transparent : seule cette couleur compte.
Hauteur (CSS — --ftr-height) 300px Toute hauteur fixe ou 100% dans un parent de hauteur connue. Prévoyez une largeur d'au moins 0,76 fois la hauteur.

FAQ

Chaque branche s'ouvre du même angle à gauche et à droite, sans hasard, parce que l'arbre est redessiné à chaque image : un tirage aléatoire le ferait trembler. Pour un arbre irrégulier mais stable, il faudrait tirer les angles une seule fois au chargement et les stocker par branche.
Pas avec les réglages fournis : LEVELS est fixe. Une pousse demanderait d'augmenter la profondeur ou la longueur du tronc au fil du temps dans loop(). C'est pourquoi l'exemple de récompense fait apparaître l'arbre d'un coup plutôt que de prétendre qu'il grandit.
1 023 segments tracés un par un, soit quelques dixièmes de milliseconde sur un ordinateur récent. Chaque niveau en plus double ce nombre. La boucle s'arrête hors écran et quand la racine est masquée.