← Backgrounds

Rotating Sunburst

Retro sunburst rays fan out from behind your content and turn slowly, like a vintage poster or a game-show backdrop, with a warm glow in the middle and darker edges. Start them from the centre or rising from the bottom edge.

Grand opening

How to use it

  1. Paste the snippet just before </head>.
  2. Add data-rotating-sunburst to a section.

Options

Add any of these attributes to the same element.

data-colorsThe two ray colours. Default #f59e0b,#fbbf24.
data-raysHow many pairs of rays. Default 14.
data-durationSeconds per turn. Default 60.
data-origincenter or bottom: where the rays start. Default center.
data-reverseTurn anticlockwise.
data-vignette="false"No glow in the middle or darker edges.

The rays are a single CSS repeating-conic-gradient turned with a transform, so it costs next to nothing; it pauses when off screen. With reduced motion it holds still.

1 · Snippet
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/anime.umd.min.js"></script>
<script>
  (function () {
    // Rotating Sunburst: retro sunburst rays fan out from behind the content
    // and turn slowly, like a vintage poster or a game-show backdrop, softened
    // towards the edges. Drawn with a CSS gradient, turned on the GPU, and
    // paused when off screen.
    // Usage: <section data-rotating-sunburst>…</section>
    // Optional: data-colors (the two ray colours, default "#f59e0b,#fbbf24"),
    // data-rays (how many pairs of rays, default 14), data-duration (seconds
    // per turn, default 60), data-origin ("center" or "bottom", where the rays
    // start, default center), data-reverse (turn anticlockwise),
    // data-vignette="false" (no darker edges).
    var DEFAULTS = { colors: '#f59e0b,#fbbf24', rays: 14, duration: 60 };

    var css =
      '[data-rotating-sunburst]{position:relative;isolation:isolate;overflow:hidden}' +
      '.rsu-layer{position:absolute;inset:0;z-index:-1;pointer-events:none;overflow:hidden}' +
      '.rsu-rays{position:absolute;border-radius:50%;will-change:transform}' +
      '.rsu-shade{position:absolute;inset:0}';
    var style = document.createElement('style');
    style.textContent = css;
    document.head.appendChild(style);

    function build(el) {
      var d = el.dataset;
      var colors = (d.colors || DEFAULTS.colors).split(',').map(function (c) { return c.trim(); });
      var c2 = colors[1] || colors[0];
      var rays = Math.max(3, +d.rays || DEFAULTS.rays);
      var turn = (+d.duration || DEFAULTS.duration) * 1000;
      var bottom = d.origin === 'bottom';
      var reverse = d.reverse !== undefined && d.reverse !== 'false';
      var layer = document.createElement('div');
      layer.className = 'rsu-layer';
      layer.setAttribute('aria-hidden', 'true');
      layer.innerHTML = '<div class="rsu-rays"></div>' + (d.vignette === 'false' ? '' : '<div class="rsu-shade"></div>');
      el.insertBefore(layer, el.firstChild);
      var disc = layer.firstChild;
      // One ray pair repeated round the circle, with a hair of blending on
      // each edge so the rays stay crisp but never jagged.
      var a = 360 / rays / 2, e = Math.min(0.35, a / 10);
      disc.style.background = 'repeating-conic-gradient(from -' + (a / 2) + 'deg,' +
        colors[0] + ' 0deg ' + (a - e) + 'deg,' + c2 + ' ' + (a + e) + 'deg ' + (2 * a - e) + 'deg,' + colors[0] + ' ' + (2 * a) + 'deg)';
      if (layer.children[1]) {
        layer.children[1].style.background = 'radial-gradient(' + (bottom ? 'ellipse 90% 120% at 50% 100%' : 'ellipse at 50% 50%') +
          ',rgba(255,255,255,.28),transparent 35%,transparent 55%,rgba(60,20,0,.32) 100%)';
      }

      function resize() {
        var w = el.clientWidth, h = el.clientHeight;
        var cy = bottom ? h : h / 2;
        // Big enough to cover the section at every angle.
        var r = Math.sqrt((w / 2) * (w / 2) + Math.max(cy, h - cy) * Math.max(cy, h - cy)) + 4;
        disc.style.width = disc.style.height = 2 * r + 'px';
        disc.style.left = w / 2 - r + 'px';
        disc.style.top = cy - r + 'px';
      }

      resize();
      if (window.ResizeObserver) new ResizeObserver(resize).observe(el);
      if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;

      var spin = anime.animate(disc, { rotate: reverse ? [0, -360] : [0, 360], duration: turn, ease: 'linear', loop: true, autoplay: false });
      new IntersectionObserver(function (entries) {
        if (entries[0].isIntersecting) spin.play(); else spin.pause();
      }).observe(el);
    }

    function init() {
      document.querySelectorAll('[data-rotating-sunburst]').forEach(function (el) {
        if (el._rsu) return;
        el._rsu = true;
        build(el);
      });
    }

    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
    else init();
  })();
</script>
2 · Your HTML
<section data-rotating-sunburst style="color: #451a03">
  <h1>Grand opening</h1>
</section>

<!-- rising from the bottom, in your own colours: -->
<section data-rotating-sunburst data-origin="bottom" data-colors="#6a0dad,#7e22ce">…</section>