>_htmlhub
library/backgrounds/3-ascendant-light

Ascendant Light

Live previewPerformance varies by device
3-ascendant-light.html

No account needed to copy or download. Check dependencies and thelicense status before publishing.

<!DOCTYPE html>
<html>
<head>
<title>3. Ascendant Light</title>
<style>
    body {
        margin: 0;
        height: 100vh;
        background-color: #1a1a2e;
        overflow: hidden;
        position: relative;
    }
    .light-source {
        position: absolute;
        top: -20%;
        left: 50%;
        width: 300%;
        height: 150%;
        transform: translateX(-50%);
        background: linear-gradient(
            45deg,
            rgba(255, 238, 204, 0) 40%,
            rgba(255, 238, 204, 0.25) 48%,
            rgba(255, 238, 204, 0.25) 52%,
            rgba(255, 238, 204, 0) 60%
        );
        animation: pan-light 10s infinite linear;
        /* To create a soft origin point for the rays */
        -webkit-mask-image: radial-gradient(ellipse at 50% -20%, black 20%, transparent 70%);
        mask-image: radial-gradient(ellipse at 50% -20%, black 20%, transparent 70%);
    }
    .light-source.two {
        transform: translateX(-50%) rotate(-20deg);
        animation-duration: 8s; /* Vary speed for realism */
    }
    @keyframes pan-light {
        from { transform: translateX(-50%) rotate(-40deg); }
        to { transform: translateX(-50%) rotate(40deg); }
    }
    .mote-container {
        position: absolute;
        top: 0; left: 0;
        width: 100%;
        height: 100%;
    }
    .mote {
        position: absolute;
        width: 4px;
        height: 4px;
        background: #fff;
        border-radius: 50%;
        opacity: 0.5;
        box-shadow: 0 0 10px #ffeecc;
        animation: float 10s infinite linear;
    }
    @keyframes float {
        0% { 
            transform: translateY(110vh) translateX(0) rotate(0deg); 
            opacity: 0;
        }
        10% { opacity: 0.7; }
        90% { opacity: 0.7; }
        100% {
            transform: translateY(-10vh) translateX(100px) rotate(360deg);
            opacity: 0;
        }
    }
    @media (prefers-reduced-motion: reduce) {
        .light-source, .mote { animation: none; }
    }
</style>
</head>
<body>
<div class="light-source"></div>
<div class="light-source two"></div>
<div class="mote-container" id="moteContainer"></div>
<script>
    const container = document.getElementById('moteContainer');
    const numMotes = 50;
    for (let i = 0; i < numMotes; i++) {
        const mote = document.createElement('div');
        mote.className = 'mote';
        const size = Math.random() * 3 + 1;
        mote.style.width = `${size}px`;
        mote.style.height = `${size}px`;
        mote.style.left = `${Math.random() * 100}vw`;
        mote.style.animationDelay = `${Math.random() * -10}s`;
        mote.style.animationDuration = `${Math.random() * 5 + 8}s`;
        container.appendChild(mote);
    }
</script>
<script>
  /**
   * Animation control function, called on every frame.
   * @param {object} frame - The animation frame object (contains time, progress, etc.).
   */
  window.nextFrame = function(frame) {
    // AI-suggested functions to consider for this animation:
    // - document.querySelectorAll\n    // - element.style.transform\n    // - element.style.opacity\n    // - Math.sin\n    // - Math.cos\n    // - performance.now\n    // - Math.random\n    // - element.style.animationDuration\n    // - element.style.animationPlayState\n    // - element.animate\n    // - container.appendChild\n    // - element.remove
    // Add your animation logic here.
    // console.log('Current frame progress:', frame.progress);
    // Bridge to existing animation if any.
    if (typeof animate === 'function') {
      animate(frame.time);
    }
  }
  // If there's an existing animation loop, we can modify it to be controlled by nextFrame.
  // This is a placeholder for how you might adapt your code.
  if (typeof requestAnimationFrame === 'function' && !window.__HTV__?.exporting) {
    let tick = 0;
    function animate(time) {
      // Your animation logic driven by RAF.
      tick++;
      // draw(tick);
      requestAnimationFrame(animate);
    }
    // requestAnimationFrame(animate);
  }
</script>
</body>
</html>

About this animation

Rising beams of ethereal light.

Under the hood

CSS Light Beams and Floating Motes

Two oversized div beams use a CSS linear-gradient softened by a mask-image: radial-gradient(...) and sweep via @keyframes pan-light, rotating from -40deg to 40deg. JavaScript adds 50 small mote div elements that rise the screen via @keyframes float, with randomised size, position, delay and duration.

Source Code
Copied to clipboard