AtmosphèreGratuit

Starfield

200 étoiles projetées en perspective 3D convergent vers l'œil — le fill semi-transparent crée l'effet de vitesse warp en pur canvas 2D, sans librairie.

JSCanvasStars
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 — Page de lancement d'un outil tech ou SaaS

QuandLancement d'une plateforme à thème spatial, d'une app de data ou d'un outil dev — le starfield est l'arrière-plan du fold principal, vu avant tout scroll.
PourquoiLe défilement d'étoiles vers l'œil crée une profondeur immersive sans écraser le titre : les étoiles, blanches et fines sur fond quasi-noir, forment un fond contrasté qui ne distrait pas. Aucune image externe, zéro requête réseau.
RéglagesVitesse z − 2 px/frame (défaut), 200 étoiles, trail opacity 0.2, rayon max 2.5 px. Ajouter un scrim CSS semi-transparent (rgba(0,0,0,0.35)) en position absolute sur le canvas pour garantir le contraste texte WCAG AA.

② Composant produit — Carte de chargement dans un dashboard

QuandUn module du dashboard attend des données (import en cours, API lente, onboarding incomplet) — la carte occupe l'espace avec une animation signifiante le temps de la résolution.
PourquoiLe starfield donne l'impression que le système « cherche » activement, réduisant l'anxiété d'attente. Il est natif canvas 2D : aucune lib, aucun GIF, 0 requête réseau supplémentaire.
RéglagesConteneur réduit (360 × 200 px), changer s.z -= 2 en s.z -= 1.5 pour une vitesse plus douce, trail opacity 0.15 pour un rendu plus éthéré. Carte glassmorphisme en overlay (background: rgba(15,15,22,0.82); backdrop-filter: blur(8px)).

③ Splash screen jeu spatial — Écran d'intro plein viewport

QuandUn jeu sci-fi ou une app d'astronomie démarre — l'écran d'attente précède la session et doit capter l'attention le temps que les assets s'initialisent.
PourquoiLe Starfield exploite ici sa perspective 3D convergente sur grande surface : le tunnel warp est immédiatement lisible, les traînes longues renforcent la sensation de propulsion hyperspatiale, et le fond quasi-noir est l'environnement natif de l'effet — aucun compromis, aucun habillage artificiel.
RéglagesVitesse z − 3 à 4 px/frame, trail opacity 0.15–0.2 (traînes longues), canvas plein viewport (height: 100vh). Le contraste étoiles blanches sur fond quasi-noir dépasse 7:1 naturellement — aucun scrim requis pour des titres en gras.

Comment ça marche

L'effet repose sur une projection en perspective (modèle pinhole camera) : chaque étoile est un point 3D (x, y, z) centré sur l'axe Z. À chaque frame, z diminue de 2, simulant un déplacement vers la caméra. La position écran est calculée par sx = (x/z) × (w/2) + w/2 et sy = (y/z) × (h/2) + h/2 — plus z est petit (étoile proche), plus sx et sy s'écartent du centre, créant l'expansion radiale caractéristique du tunnel.

L'effet de vitesse (les traînes) n'est pas un motion blur calculé mais un remplissage semi-transparent à rgba(10,10,15,0.2) avant chaque redraw : le fond est peint à 20 % d'opacité, ce qui laisse subsister les frames précédentes atténuées. Les étoiles proches (petit z) accumulent davantage de fantômes car elles se déplacent vite en pixels — la traîne s'allonge naturellement sans calcul supplémentaire.

La taille et la luminosité scalent inversement avec z : rayon r = max(0.5, (1 − z/w) × 2.5), alpha a = 1 − z/w. Une étoile au fond (z ≈ w) est un point de 0,5 px quasi-invisible ; en avant-plan (z → 0) elle atteint 2,5 px blanc pur. Quand z ≤ 0, l'étoile est réinitialisée à z = w avec une nouvelle position x, y aléatoire, assurant un flux continu de 200 particules sans allocation dynamique.

La boucle tourne via un simple requestAnimationFrame récursif — il n'y a pas d'IntersectionObserver pour couper le RAF hors viewport, ni de prefers-reduced-motion. La taille du canvas est lue depuis le parent via getBoundingClientRect() à l'initialisation uniquement : aucun ResizeObserver, le canvas ne se recalibre pas au redimensionnement.

Accessibilité

  • prefers-reduced-motion : non géré. Le code ne vérifie pas window.matchMedia('(prefers-reduced-motion: reduce)') — l'animation tourne en boucle quelle que soit la préférence système. Pour conformité WCAG 2.3.3, ajouter un guard au début de l'IIFE et stopper le RAF si la préférence est active.
  • Le <canvas> n'a pas d'attribut aria-hidden="true" dans le code livré — les lecteurs d'écran peuvent tenter de l'explorer sans description utile. À corriger : ajouter aria-hidden="true" sur le canvas et role="img" aria-label="Champ d'étoiles animé" sur le wrapper.
  • Pas de mécanisme de pause exposé : l'animation démarre automatiquement et tourne en continu sans bouton Pause, ce qui ne satisfait pas WCAG 2.2.2 pour les animations auto-lancées de plus de 5 secondes.
  • Contraste étoiles sur fond : rgba(255,255,255,a) sur rgb(10,10,15) donne un ratio supérieur à 7:1 pour les étoiles en avant-plan. Aucun texte n'est rendu sur le canvas — les textes overlay sont à gérer entièrement côté HTML.
  • Aucune interaction clavier ou souris dans l'effet : le canvas est décoratif pur, sans trap de focus, sans événements clavier.

Compatibilité navigateur

Nécessite Canvas 2D Context et requestAnimationFrame — disponibles dans tous les navigateurs modernes depuis 2012. Zéro dépendance externe.

Chrome 4+✓ Complet
Firefox 3.6+✓ Complet
Safari 3.1+✓ Complet
Edge 12+✓ Complet
Mobile iOS✓ Complet
Android Chrome✓ Complet

Si <code>canvas</code> n'est pas supporté (navigateurs très anciens), le <code>&lt;div&gt;</code> parent s'affiche vide avec le fond CSS dark — aucune erreur JS fatale, le contenu overlay HTML reste lisible.

Le code

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

HTML
<div class="ps-canvas-wrap">
  <canvas class="ps-canvas" id="psStarfield" width="392" height="280">
  </canvas>
</div>
CSS
.ps-canvas  {
   width: 100%;
   height: 280px;
   display: block;
   border-radius: 0px;
   }

.ps-canvas-wrap  {
   position: relative;
   width: 100%;
   height: 280px;
   }

.ps-canvas-wrap canvas  {
   position: absolute;
   top: 0px;
   left: 0px;
   width: 100%;
   height: 100%;
   }
JavaScript (fx-0485)
(function() {
  const canvas = document.getElementById('psStarfield');
  if (!canvas) return;
  const ctx = canvas.getContext('2d');
  const rect = canvas.parentElement.getBoundingClientRect();
  canvas.width = rect.width;
  canvas.height = rect.height;
  const w = canvas.width, h = canvas.height;
  const stars = [];
  for (let i = 0; i < 200; i++) {
    stars.push({ x: Math.random() * w - w / 2, y: Math.random() * h - h / 2, z: Math.random() * w });
  }
  function draw() {
    ctx.fillStyle = 'rgba(10,10,15,0.2)';
    ctx.fillRect(0, 0, w, h);
    stars.forEach(s => {
      s.z -= 2;
      if (s.z <= 0) { s.z = w; s.x = Math.random() * w - w / 2; s.y = Math.random() * h - h / 2; }
      const sx = (s.x / s.z) * w / 2 + w / 2;
      const sy = (s.y / s.z) * h / 2 + h / 2;
      const r = Math.max(0.5, (1 - s.z / w) * 2.5);
      const a = 1 - s.z / w;
      ctx.beginPath();
      ctx.arc(sx, sy, r, 0, Math.PI * 2);
      ctx.fillStyle = `rgba(255,255,255,${a})`;
      ctx.fill();
    });
    requestAnimationFrame(draw);
  }
  draw();
})();

Personnaliser

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

Option / propriétéDéfautEffet
Nb. d'étoiles (JS init : 200) 200 Taille du tableau stars[]. 80–120 pour un ciel calme, 300–400 pour une densité galactique — surveiller les perfs sur mobile au-delà de 300.
Vitesse (JS : s.z -= 2) 2 px/frame Décrémentation de z par frame. 1 = contemplatif, 2 = warp défaut, 4–5 = hyperpropulsion. Extraire dans une variable globale (window.sfSpeed) pour contrôle à la volée.
Taille max étoiles (JS : × 2.5) 2.5 Facteur dans (1 − z/w) × 2.5. Augmenter à 4 pour des étoiles plus grosses en avant-plan, réduire à 1.5 pour un rendu stellaire plus fin.
Trail opacity (JS : rgba …, 0.2) 0.2 Alpha du remplissage de rémanence. 0.1 = traînes longues et spectrales, 0.4 = traînes courtes et nettes, 1.0 = aucune traîne (étoiles pures, sans effet vitesse).
Couleur étoiles (JS : rgba(255,255,255,…)) blanc Remplacer 255,255,255 par 200,220,255 pour un bleu galactique ou 255,220,180 pour un teint chaud ambré.
Fond CSS (rgb(10,10,15)) rgb(10,10,15) Couleur CSS du conteneur et valeur de rémanence dans le fillStyle JS. Modifier les deux ensemble : rgb(15,5,25) = violet nuit, rgb(0,10,20) = bleu marine.
.ps-canvas-wrap height (CSS) 280px Le canvas s'étend sur toute la largeur parent mais la hauteur est fixée en CSS. Passer à 100vh pour un hero plein écran ou à 400px pour une bannière plus haute.

FAQ

Peut-on contrôler la vitesse du starfield en temps réel sans recharger la page ?

Pas nativement — le code IIFE ferme la constante vitesse (s.z -= 2) dans son closure. Pour un contrôle dynamique, extrayez la valeur dans une variable globale avant de copier : window.sfSpeed = 2 puis remplacez s.z -= 2 par s.z -= window.sfSpeed. Vous pouvez ensuite écrire window.sfSpeed = 4 depuis n'importe quel callback pour accélérer à la volée.

Pourquoi toutes les étoiles émergent-elles du centre plutôt que d'une position aléatoire ?

C'est la mécanique de la projection en perspective : à grande distance (z → w), sx et sy convergent mathématiquement vers le centre du canvas (w/2, h/2). Quand une étoile est remise à z = w, elle réapparaît imperceptiblement près du centre et s'écarte progressivement — c'est l'effet tunnel correct, pas un bug.

L'effet fonctionne-t-il dans un iframe ou un web component avec shadow DOM ?

En iframe, oui sans modification — getElementById('psStarfield') est local au document de l'iframe. En web component avec shadow DOM, remplacez getElementById par this.shadowRoot.getElementById pour que la recherche reste dans le shadow tree. L'IIFE est entièrement isolée et ne touche pas au DOM global.